diff --git a/AADATA.CPP b/AADATA.CPP new file mode 100644 index 0000000..c7893d2 --- /dev/null +++ b/AADATA.CPP @@ -0,0 +1,798 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\aadata.cpv 2.18 16 Oct 1995 16:49:50 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * name in * + * File Name : AADATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 22, 1994 * + * * + * Last Update : August 7, 1995 [JLB] * + * Determines * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AircraftTypeClass::AircraftTypeClass -- Constructor for aircraft objects. * + * AircraftTypeClass::Create_And_Place -- Creates and places aircraft using normal game syste* + * AircraftTypeClass::Create_One_Of -- Creates an aircraft object of the appropriate type. * + * AircraftTypeClass::Dimensions -- Fetches the graphic dimensions of the aircraft type. * + * AircraftTypeClass::Display -- Displays a generic version of the aircraft type. * + * AircraftTypeClass::From_Name -- Converts an ASCIIto an aircraft type number. * + * AircraftTypeClass::Max_Pips -- Fetches the maximum number of pips allowed. * + * AircraftTypeClass::Occupy_List -- Returns with occupation list for landed aircraft. * + * AircraftTypeClass::One_Time -- Performs one time initialization of the aircraft type class.* + * AircraftTypeClass::Overlap_List -- the overlap list for a landed aircraft. * + * AircraftTypeClass::Prep_For_Add -- Prepares the scenario editor for adding an aircraft objec* + * AircraftTypeClass::Repair_Cost -- Fetchs the cost per repair step. * + * AircraftTypeClass::Repair_Step -- Fetches the number of health points per repair. * + * AircraftTypeClass::Who_Can_Build_Me -- Determines which object can build the aircraft obje* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +void const * AircraftTypeClass::LRotorData = NULL; +void const * AircraftTypeClass::RRotorData = NULL; + +// A-10 attack plane +static AircraftTypeClass const AttackPlane( + AIRCRAFT_A10, // What kind of aircraft is this. + TXT_A10, // Translated text number for aircraft. + "A10", // INI name of aircraft. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is a leader type? + false, // Does it fire a pair of shots in quick succession? + false, // Is this a typical transport vehicle? + true, // Fixed wing aircraft? + false, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + false, // Can the aircraft be crushed by a tracked vehicle? + true, // Is it invisible on radar? + false, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Can it be repaired in a repair facility? + false, // Can the player construct or order this unit? + true, // Is there a crew inside? + 3, // Number of shots it has (default). + 60, // The strength of this unit. + 0, // The range that it reveals terrain around itself. + 800, // Credit cost to construct. + 0, // The scenario this becomes available. + 10,1, // Risk, reward when calculating AI. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // Who can own this aircraft type. + WEAPON_NAPALM,WEAPON_NONE, + ARMOR_ALUMINUM, // Armor type of this aircraft. + MPH_FAST, // Maximum speed of aircraft. + 5, // Rate of turn. + MISSION_HUNT // Default mission for aircraft. +); + +// Transport helicopter. +static AircraftTypeClass const TransportHeli( + AIRCRAFT_TRANSPORT, // What kind of aircraft is this. + TXT_TRANS, // Translated text number for aircraft. + "TRAN", // INI name of aircraft. + 6, // Build level. + STRUCTF_HELIPAD, // Building prerequisite. + false, // Is a leader type? + false, // Does it fire a pair of shots in quick succession? + true, // Is this a typical transport vehicle? + false, // Fixed wing aircraft? + true, // Equipped with a rotor? + true, // Custom rotor sets for each facing? + true, // Can this aircraft land on clear terrain? + false, // Can the aircraft be crushed by a tracked vehicle? + true, // Is it invisible on radar? + true, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Theater specific graphic image? + false, // Is it equipped with a combat turret? + false, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + 0, // Number of shots it has (default). + 90, // The strength of this unit. + 0, // The range that it reveals terrain around itself. + 1500, // Credit cost to construct. + 98, // The scenario this becomes available. + 10,80, // Risk, reward when calculating AI. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD| + HOUSEF_GOOD, // Who can own this aircraft type. + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // Armor type of this aircraft. + MPH_MEDIUM_FAST, // Maximum speed of aircraft. + 5, // Rate of turn. + MISSION_HUNT // Default mission for aircraft. +); + +// Apache attach helicopter. +static AircraftTypeClass const AttackHeli( + AIRCRAFT_HELICOPTER, // What kind of aircraft is this. + TXT_HELI, // Translated text number for aircraft. + "HELI", // INI name of aircraft. + 6, // Build level. + STRUCTF_HELIPAD, // Building prerequisite. + true, // Is a leader type? + true, // Does it fire a pair of shots in quick succession? + false, // Is this a typical transport vehicle? + false, // Fixed wing aircraft? + true, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + false, // Can the aircraft be crushed by a tracked vehicle? + true, // Is it invisible on radar? + true, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + 15, // Number of shots it has (default). + 125, // The strength of this unit. + 0, // The range that it reveals terrain around itself. + 1200, // Credit cost to construct. + 10, // The scenario this becomes available. + 10,80, // Risk, reward when calculating AI. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // Who can own this aircraft type. + WEAPON_CHAIN_GUN,WEAPON_NONE, + ARMOR_STEEL, // Armor type of this aircraft. + MPH_FAST, // Maximum speed of aircraft. + 4, // Rate of turn. + MISSION_HUNT // Default mission for aircraft. +); + + +// Orca attack helicopter. +static AircraftTypeClass const OrcaHeli( + AIRCRAFT_ORCA, // What kind of aircraft is this. + TXT_ORCA, // Translated text number for aircraft. + "ORCA", // INI name of aircraft. + 6, // Build level. + STRUCTF_HELIPAD, // Building prerequisite. + true, // Is a leader type? + true, // Does it fire a pair of shots in quick succession? + false, // Is this a typical transport vehicle? + false, // Fixed wing aircraft? + false, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + false, // Can the aircraft be crushed by a tracked vehicle? + true, // Is it invisible on radar? + true, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + 6, // Number of shots it has (default). + 125, // The strength of this unit. + 0, // The range that it reveals terrain around itself. + 1200, // Credit cost to construct. + 10, // The scenario this becomes available. + 10,80, // Risk, reward when calculating AI. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD, // Who can own this aircraft type. + WEAPON_DRAGON,WEAPON_NONE, + ARMOR_STEEL, // Armor type of this aircraft. + MPH_FAST, // Maximum speed of aircraft. + 4, // Rate of turn. + MISSION_HUNT // Default mission for aircraft. +); + + +// C-17 transport plane. +static AircraftTypeClass const CargoPlane( + AIRCRAFT_CARGO, // What kind of aircraft is this. + TXT_C17, // Translated text number for aircraft. + "C17", // INI name of aircraft. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is a leader type? + false, // Does it fire a pair of shots in quick succession? + true, // Is this a typical transport vehicle? + true, // Fixed wing aircraft? + false, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + false, // Can the aircraft be crushed by a tracked vehicle? + true, // Is it invisible on radar? + false, // Can the player select it so as to give it orders? + false, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Can it be repaired in a repair facility? + false, // Can the player construct or order this unit? + true, // Is there a crew inside? + 0, // Number of shots it has (default). + 25, // The strength of this unit. + 0, // The range that it reveals terrain around itself. + 800, // Credit cost to construct. + 0, // The scenario this becomes available. + 10,1, // Risk, reward when calculating AI. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // Who can own this aircraft type. + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // Armor type of this aircraft. + MPH_FAST, // Maximum speed of aircraft. + 5, // Rate of turn. + MISSION_HUNT // Default mission for aircraft. +); + + +AircraftTypeClass const * const AircraftTypeClass::Pointers[AIRCRAFT_COUNT] = { + &TransportHeli, + &AttackPlane, + &AttackHeli, + &CargoPlane, + &OrcaHeli, +}; + + +/*********************************************************************************************** + * AircraftTypeClass::AircraftTypeClass -- Constructor for aircraft objects. * + * * + * This is the constructor for the aircraft object. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +AircraftTypeClass::AircraftTypeClass( + AircraftType airtype, + int name, + char const *ininame, + unsigned char level, + long pre, + bool is_leader, + bool is_twoshooter, + bool is_transporter, + bool is_fixedwing, + bool is_rotorequipped, + bool is_rotorcustom, + bool is_landable, + bool is_crushable, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_theater, + bool is_repairable, + bool is_buildable, + bool is_crew, + int ammo, + unsigned short strength, + int sightrange, + int cost, + int scenario, + int risk, + int reward, + int ownable, + WeaponType primary, + WeaponType secondary, + ArmorType armor, + MPHType maxspeed, + int rot, + MissionType deforder) : + TechnoTypeClass(name, + ininame, + level, + pre, + is_leader, + false, + false, + is_transporter, + false, + is_crushable, + is_stealthy, + is_selectable, + is_legal_target, + is_insignificant, + is_immune, + is_theater, + is_twoshooter, + false, + is_repairable, + is_buildable, + is_crew, + ammo, + strength, + maxspeed, + sightrange, + cost, + scenario, + risk, + reward, + ownable, + primary, + secondary, + armor) +{ + IsRotorEquipped = is_rotorequipped; + IsRotorCustom = is_rotorcustom; + IsLandable = is_landable; + IsFixedWing = is_fixedwing; + Type = airtype; + ROT = rot; + Mission = deforder; +} + + +/*********************************************************************************************** + * AircraftTypeClass::From_Name -- Converts an ASCII name into an aircraft type number. * + * * + * This routine is used to convert an ASCII representation of an aircraft into the * + * matching aircraft type number. This is used by the scenario INI reader code. * + * * + * INPUT: name -- Pointer to ASCII name to translate. * + * * + * OUTPUT: Returns the aircraft type number that matches the ASCII name provided. If no * + * match could be found, then AIRCRAFT_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +AircraftType AircraftTypeClass::From_Name(char const *name) +{ + if (name) { + for (AircraftType classid = AIRCRAFT_FIRST; classid < AIRCRAFT_COUNT; classid++) { + if (stricmp(Pointers[classid]->IniName, name) == 0) { + return(classid); + } + } + } + return(AIRCRAFT_NONE); +} + + +/*********************************************************************************************** + * AircraftTypeClass::One_Time -- Performs one time initialization of the aircraft type class. * + * * + * This routine is used to perform the onetime initialization of the aircraft type. This * + * includes primarily the shape and other graphic data loading. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This goes to disk and also must only be called ONCE. * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::One_Time(void) +{ + AircraftType index; + + for (index = AIRCRAFT_FIRST; index < AIRCRAFT_COUNT; index++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + AircraftTypeClass const & uclass = As_Reference(index); + + /* + ** Fetch the supporting data files for the unit. + */ + char buffer[_MAX_FNAME]; + if ( Get_Resolution_Factor() ) { + sprintf(buffer, "%sICNH", uclass.IniName); + } else { + sprintf(buffer, "%sICON", uclass.IniName); + } + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + ((void const *&)uclass.CameoData) = MixFileClass::Retrieve(fullname); + + /* + ** Generic shape for all houses load method. + */ + _makepath(fullname, NULL, NULL, uclass.IniName, ".SHP"); + ((void const *&)uclass.ImageData) = MixFileClass::Retrieve(fullname); + } + + LRotorData = MixFileClass::Retrieve("LROTOR.SHP"); + RRotorData = MixFileClass::Retrieve("RROTOR.SHP"); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Create_One_Of -- Creates an aircraft object of the appropriate type. * + * * + * This routine is used to create an aircraft object that matches the aircraft type. It * + * serves as a shortcut to creating an object using the "new" operator and "if" checks. * + * * + * INPUT: house -- The house owner of the aircraft that is to be created. * + * * + * OUTPUT: Returns with a pointer to the aircraft created. If the aircraft could not be * + * created, then a NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * AircraftTypeClass::Create_One_Of(HouseClass * house) const +{ + return(new AircraftClass(Type, house->Class->House)); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * AircraftTypeClass::Prep_For_Add -- Prepares the scenario editor for adding an aircraft objec* + * * + * This routine is used by the scenario editor to prepare for the adding operation. It * + * builds a list of pointers to object types that can be added. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::Prep_For_Add(void) +{ + for (AircraftType index = AIRCRAFT_FIRST; index < AIRCRAFT_COUNT; index++) { + if (As_Reference(index).Get_Image_Data()) { + Map.Add_To_List(&As_Reference(index)); + } + } +} + + +/*********************************************************************************************** + * AircraftTypeClass::Display -- Displays a generic version of the aircraft type. * + * * + * This routine is used by the scenario editor to display a generic version of the object * + * type. This is displayed in the object selection dialog box. * + * * + * INPUT: x,y -- The coordinates to draw the aircraft at (centered). * + * * + * window -- The window to base the coordinates upon. * + * * + * house -- The owner of this generic aircraft. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::Display(int x, int y, WindowNumberType window, HousesType house) const +{ + int shape = 0; + void const * ptr = Get_Cameo_Data(); + if (!ptr) { + ptr = Get_Image_Data(); + shape = 5; + } + CC_Draw_Shape(ptr, shape, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, HouseClass::As_Pointer(house)->Remap_Table(false, true)); +} +#endif + + +/*********************************************************************************************** + * AircraftTypeClass::Occupy_List -- Returns with occupation list for landed aircraft. * + * * + * This determines the occupation list for the aircraft (if it was landed). * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a pointer to a cell offset occupation list for the aircraft. * + * * + * WARNINGS: This occupation list is only valid if the aircraft is landed. * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +short const * AircraftTypeClass::Occupy_List(bool) const +{ + static short const _list[] = {0, REFRESH_EOL}; + return(_list); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Overlap_List -- Determines the overlap list for a landed aircraft. * + * * + * This routine figures out the overlap list for the aircraft as if it were landed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the cell offset overlap list for the aircraft. * + * * + * WARNINGS: This overlap list is only valid when the aircraft is landed. * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +short const * AircraftTypeClass::Overlap_List(void) const +{ + static short const _list[] = {-(MAP_CELL_W-1), -MAP_CELL_W, -(MAP_CELL_W+1), -1, 1, (MAP_CELL_W-1), MAP_CELL_W, (MAP_CELL_W+1), REFRESH_EOL}; + return(_list); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Who_Can_Build_Me -- Determines which object can build the aircraft objec * + * * + * Use this routine to determine which object (factory) can build the aircraft. It * + * determines this by scanning through the available factories, looking for one that is * + * of the proper ownership and is available. * + * * + * INPUT: intheory -- When true, it doesn't consider if the factory is currently busy. It * + * only considers that it is the right type. * + * * + * legal -- Should building prerequisite legality checks be performed as well? * + * For building placements, this is usually false. For sidebar button * + * adding, this is usually true. * + * * + * house -- The house of the desired aircraft to be built. * + * * + * OUTPUT: Returns with a pointer to the object that can build the aircraft. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/30/1994 JLB : Created. * + *=============================================================================================*/ +BuildingClass * AircraftTypeClass::Who_Can_Build_Me(bool , bool legal, HousesType house) const +{ + BuildingClass * anybuilding = NULL; + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + if (building && + !building->IsInLimbo && + building->House->Class->House == house && + building->Mission != MISSION_DECONSTRUCTION && + ((1L << building->ActLike) & Ownable) && + (!legal || building->House->Can_Build(Type, building->ActLike)) && + building->Class->ToBuild == RTTI_AIRCRAFTTYPE) { + + if (building->IsLeader) return(building); + anybuilding = building; + } + } + return(anybuilding); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Repair_Cost -- Fetchs the cost per repair step. * + * * + * This routine will return the cost for every repair step. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the credit expense for every repair step. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/26/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftTypeClass::Repair_Cost(void) const +{ + return(Fixed_To_Cardinal(Cost/(MaxStrength/REPAIR_STEP), REPAIR_PERCENT)); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Repair_Step -- Fetches the number of health points per repair. * + * * + * For every repair event, the returned number of health points is acquired. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of health points to recover each repair step. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/26/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftTypeClass::Repair_Step(void) const +{ + return(REPAIR_STEP); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Max_Pips -- Fetches the maximum number of pips allowed. * + * * + * Use this routine to retrieve the maximum pip count allowed for this aircraft. This is * + * the maximum number of passengers. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the maximum number of pips for this aircraft. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/26/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftTypeClass::Max_Pips(void) const +{ + if (IsTransporter) { + return(Max_Passengers()); + } else { + if (Primary != WEAPON_NONE) { + return(5); + } + } + return(0); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Create_And_Place -- Creates and places aircraft using normal game system * + * * + * This routine is used to create and place an aircraft through the normal game system. * + * Since creation of aircraft in this fashion is prohibited, this routine does nothing. * + * * + * INPUT: na * + * * + * OUTPUT: Always returns a failure code (false). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/07/1995 JLB : Created. * + *=============================================================================================*/ +bool AircraftTypeClass::Create_And_Place(CELL, HousesType) const +{ + return(false); +} + + + + + +/*********************************************************************************************** + * ATC::Init -- load up terrain set dependant sidebar icons * + * * + * * + * * + * INPUT: theater type * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 4/25/96 0:33AM ST : Created * + *=============================================================================================*/ + +void AircraftTypeClass::Init(TheaterType theater) +{ + if (theater != LastTheater){ + if ( Get_Resolution_Factor() ) { + + AircraftType index; + char buffer[_MAX_FNAME]; + char fullname[_MAX_FNAME+_MAX_EXT]; + void const * cameo_ptr; + + for (index = AIRCRAFT_FIRST; index < AIRCRAFT_COUNT; index++) { + AircraftTypeClass const & uclass = As_Reference(index); + + ((void const *&)uclass.CameoData) = NULL; + + sprintf(buffer, "%.4sICNH", uclass.IniName); + _makepath (fullname, NULL, NULL, buffer, Theaters[theater].Suffix); + cameo_ptr = MixFileClass::Retrieve(fullname); + if (cameo_ptr){ + ((void const *&)uclass.CameoData) = cameo_ptr; + } + } + } + } +} + + + + + +/*********************************************************************************************** + * AircraftTypeClass::Dimensions -- Fetches the graphic dimensions of the aircraft type. * + * * + * This routine will fetch the pixel dimensions of this aircraft type. These dimensions * + * are used to control map refresh and select box rendering. * + * * + * INPUT: width -- Reference to variable that will be filled in with aircraft width. * + * * + * height -- Reference to variable that will be filled in with aircraft height. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/07/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::Dimensions(int &width, int &height) const +{ + width = 21; + height = 20; +} + + +RTTIType AircraftTypeClass::What_Am_I(void) const {return RTTI_AIRCRAFTTYPE;}; diff --git a/ABSTRACT.CPP b/ABSTRACT.CPP new file mode 100644 index 0000000..f470692 --- /dev/null +++ b/ABSTRACT.CPP @@ -0,0 +1,109 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\abstract.cpv 2.20 16 Oct 1995 16:49:04 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ABSTRACT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/26/95 * + * * + * Last Update : May 22, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AbstractClass::Distance -- Determines distance to target. * + * AbstractTypeClass::AbstractTypeClass -- Constructor for abstract type objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * AbstractClass::Distance -- Determines distance to target. * + * * + * This will determine the distance (direct line) to the target. The distance is in * + * 'leptons'. This routine is typically used for weapon range checks. * + * * + * INPUT: target -- The target to determine range to. * + * * + * OUTPUT: Returns with the range to the specified target (in leptons). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/17/1994 JLB : Created. * + *=============================================================================================*/ +int AbstractClass::Distance(TARGET target) const +{ + /* + ** Should subtract a fudge-factor distance for building targets. + */ + BuildingClass *obj = As_Building(target); + int dist = Distance(As_Coord(target)); + + /* + ** If the object is a building the adjust it by the average radius + ** of the object. + */ + if (obj) { + dist -= ((obj->Class->Width() + obj->Class->Height()) * (0x100 / 4)); + if (dist < 0) dist = 0; + } + + /* + ** Return the distance to the target + */ + return(dist); +} + + +/*********************************************************************************************** + * AbstractTypeClass::AbstractTypeClass -- Constructor for abstract type objects. * + * * + * This is the constructor for AbstractTypeClass objects. It initializes the INI name and * + * the text name for this object type. * + * * + * INPUT: name -- Text number for the full name of the object. * + * * + * ini -- The ini name for this object type. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +AbstractTypeClass::AbstractTypeClass(int name, char const * ini) +{ + Name = name; + strncpy((char *)IniName, ini, sizeof(IniName)); + ((char &)IniName[sizeof(IniName)-1]) = '\0'; +} + +RTTIType AbstractTypeClass::What_Am_I(void) const {return RTTI_ABSTRACTTYPE;}; +COORDINATE AbstractTypeClass::Coord_Fixup(COORDINATE coord) const {return coord;} +int AbstractTypeClass::Full_Name(void) const {return Name;}; +unsigned short AbstractTypeClass::Get_Ownable(void) const {return 0xffff;}; diff --git a/ABSTRACT.H b/ABSTRACT.H new file mode 100644 index 0000000..944781a --- /dev/null +++ b/ABSTRACT.H @@ -0,0 +1,109 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\abstract.h_v 2.20 16 Oct 1995 16:46:30 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ABSTRACT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/26/95 * + * * + * Last Update : January 26, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef ABSTRACT_H +#define ABSTRACT_H + +DirType Direction(CELL cell1, CELL cell2); +DirType Direction(COORDINATE coord1, COORDINATE coord2); +int Distance(COORDINATE coord1, COORDINATE coord2); +int Distance(CELL coord1, CELL coord2); +COORDINATE As_Coord(TARGET target); + +class AbstractTypeClass; + +class AbstractClass +{ + public: + + /* + ** The coordinate location of the unit. For vehicles, this is the center + ** point. For buildings, it is the upper left corner. + */ + COORDINATE Coord; + + /* + ** The actual object ram-space is located in arrays in the data segment. This flag + ** is used to indicate which objects are free to be reused and which are currently + ** in use by the game. + */ + unsigned IsActive:1; + + /*----------------------------------------------------------------------------------- + ** Constructor & destructors. + */ + AbstractClass(void) {Coord = 0L;}; + virtual ~AbstractClass(void) {}; + + /* + ** Query functions. + */ + virtual HousesType Owner(void) const {return HOUSE_NONE;}; + + /* + ** Coordinate query support functions. + */ + virtual COORDINATE Center_Coord(void) const {return Coord;}; + virtual COORDINATE Target_Coord(void) const {return Coord;}; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + DirType Direction(AbstractClass const * object) const {return ::Direction(Center_Coord(), object->Target_Coord());}; + DirType Direction(COORDINATE coord) const {return ::Direction(Center_Coord(), coord);}; + DirType Direction(TARGET target) const {return ::Direction(Center_Coord(), As_Coord(target));}; + DirType Direction(CELL cell) const {return ::Direction(Coord_Cell(Center_Coord()), cell);}; + int Distance(TARGET target) const; + int Distance(COORDINATE coord) const {return ::Distance(Center_Coord(), coord);}; + int Distance(CELL cell) const {return ::Distance(Coord_Cell(Center_Coord()), cell);}; + int Distance(AbstractClass const * object) const {return ::Distance(Center_Coord(), object->Target_Coord());}; + + /* + ** Object entry and exit from the game system. + */ + virtual MoveType Can_Enter_Cell(CELL , FacingType = FACING_NONE) const {return MOVE_OK;}; + + /* + ** AI. + */ + virtual void AI(void) {}; + +}; + + +#endif diff --git a/ADATA.CPP b/ADATA.CPP new file mode 100644 index 0000000..b2aace5 --- /dev/null +++ b/ADATA.CPP @@ -0,0 +1,2429 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\adata.cpv 2.18 16 Oct 1995 16:49:32 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ADATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 30, 1994 * + * * + * Last Update : August 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AnimTypeClass::One_Time -- Performs one time action for animation types. * + * AnimTypeClass::AnimTypeClass -- Constructor for animation types. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +// Dinosaur death animations +static AnimTypeClass const TricDie( + ANIM_TRIC_DIE, // Animation number. + "TRIC", // Data name of animation. + 32, // Maximum dimension of animation. + 4, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 176, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 20, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const TRexDie( + ANIM_TREX_DIE, // Animation number. + "TREX", // Data name of animation. + 48, // Maximum dimension of animation. + 4, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 144, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 40, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const StegDie( + ANIM_STEG_DIE, // Animation number. + "STEG", // Data name of animation. + 33, // Maximum dimension of animation. + 4, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 176, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 22, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const RaptDie( + ANIM_RAPT_DIE, // Animation number. + "RAPT", // Data name of animation. + 24, // Maximum dimension of animation. + 4, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 144, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 40, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const SAMN( + ANIM_SAM_N, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 4, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMNW( + ANIM_SAM_NW, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 22, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*1, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMW( + ANIM_SAM_W, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 40, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*2, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMSW( + ANIM_SAM_SW, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 58, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*3, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMS( + ANIM_SAM_S, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 76, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*4, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMSE( + ANIM_SAM_SE, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 94, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*5, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAME( + ANIM_SAM_E, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 112, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*6, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMNE( + ANIM_SAM_NE, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 130, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*7, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const LZSmoke( + ANIM_LZ_SMOKE, // Animation number. + "SMOKLAND", // Data name of animation. + 32, // Maximum dimension of animation. + 72, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 72, // Loop start frame number. + 91, // Ending frame of loop back. + -1, // Number of animation stages. + 255, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +/* +** Flammable object burning animations. Primarily used on trees and buildings. +*/ +static AnimTypeClass const BurnSmall( + ANIM_BURN_SMALL, // Animation number. + "BURN-S", // Data name of animation. + 11, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0008, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const BurnMed( + ANIM_BURN_MED, // Animation number. + "BURN-M", // Data name of animation. + 14, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0010, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const BurnBig( + ANIM_BURN_BIG, // Animation number. + "BURN-L", // Data name of animation. + 23, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0018, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +/* +** Flammable object burning animations that trail into smoke. Used for +** buildings and the gunboat. +*/ +static AnimTypeClass const OnFireSmall( + ANIM_ON_FIRE_SMALL, // Animation number. + "BURN-S", // Data name of animation. + 11, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0008, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_SMOKE_M +); +static AnimTypeClass const OnFireMed( + ANIM_ON_FIRE_MED, // Animation number. + "BURN-M", // Data name of animation. + 14, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0010, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_ON_FIRE_SMALL +); +static AnimTypeClass const OnFireBig( + ANIM_ON_FIRE_BIG, // Animation number. + "BURN-L", // Data name of animation. + 23, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0018, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_ON_FIRE_MED +); + +/* +** Flame thrower animations. These are direction specific. +*/ +static AnimTypeClass const FlameN( + ANIM_FLAME_N, // Animation number. + "FLAME-N", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + true, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const FlameNW( + ANIM_FLAME_NW, // Animation number. + "FLAME-NW", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + true, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const FlameW( + ANIM_FLAME_W, // Animation number. + "FLAME-W", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + true, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const FlameSW( + ANIM_FLAME_SW, // Animation number. + "FLAME-SW", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + true, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const FlameS( + ANIM_FLAME_S, // Animation number. + "FLAME-S", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + true, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const FlameSE( + ANIM_FLAME_SE, // Animation number. + "FLAME-SE", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + true, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const FlameE( + ANIM_FLAME_E, // Animation number. + "FLAME-E", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + true, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const FlameNE( + ANIM_FLAME_NE, // Animation number. + "FLAME-NE", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + true, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +/* +** Chem sprayer animations. These are direction specific. +*/ +static AnimTypeClass const ChemN( + ANIM_CHEM_N, // Animation number. + "CHEM-N", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const ChemNW( + ANIM_CHEM_NW, // Animation number. + "CHEM-NW", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const ChemW( + ANIM_CHEM_W, // Animation number. + "CHEM-W", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const ChemSW( + ANIM_CHEM_SW, // Animation number. + "CHEM-SW", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const ChemS( + ANIM_CHEM_S, // Animation number. + "CHEM-S", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const ChemSE( + ANIM_CHEM_SE, // Animation number. + "CHEM-SE", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const ChemE( + ANIM_CHEM_E, // Animation number. + "CHEM-E", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const ChemNE( + ANIM_CHEM_NE, // Animation number. + "CHEM-NE", // Data name of animation. + 0, // Maximum dimension of animation. + 9, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Grenade( + ANIM_GRENADE, // Animation number. + "VEH-HIT2", // Data name of animation. + 21, // Maximum dimension of animation. + 1, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_GUN20, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const FBall1( + ANIM_FBALL1, // Animation number. + "FBALL1", // Data name of animation. + 67, // Maximum dimension of animation. + 6, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_XPLOS, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Frag1( + ANIM_FRAG1, // Animation number. + "FRAG1", // Data name of animation. + 45, // Maximum dimension of animation. + 3, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + true, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_XPLOBIG4, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Frag3( + ANIM_FRAG2, // Animation number. + "FRAG3", // Data name of animation. + 41, // Maximum dimension of animation. + 3, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_XPLOBIG6, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const VehHit1( + ANIM_VEH_HIT1, // Animation number. + "VEH-HIT1", // Data name of animation. + 30, // Maximum dimension of animation. + 4, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + true, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_XPLOS, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const VehHit2( + ANIM_VEH_HIT2, // Animation number. + "VEH-HIT2", // Data name of animation. + 21, // Maximum dimension of animation. + 1, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + true, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_XPLOS, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const VehHit3( + ANIM_VEH_HIT3, // Animation number. + "VEH-HIT3", // Data name of animation. + 19, // Maximum dimension of animation. + 3, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + true, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_XPLOS, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const ArtExp1( + ANIM_ART_EXP1, // Animation number. + "ART-EXP1", // Data name of animation. + 41, // Maximum dimension of animation. + 1, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_XPLOSML2, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Napalm1( + ANIM_NAPALM1, // Animation number. + "NAPALM1", // Data name of animation. + 21, // Maximum dimension of animation. + 5, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_FLAMER1, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Napalm2( + ANIM_NAPALM2, // Animation number. + "NAPALM2", // Data name of animation. + 41, // Maximum dimension of animation. + 5, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_FLAMER1, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Napalm3( + ANIM_NAPALM3, // Animation number. + "NAPALM3", // Data name of animation. + 78, // Maximum dimension of animation. + 5, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_FLAMER1, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const SmokePuff( + ANIM_SMOKE_PUFF, // Animation number. + "SMOKEY", // Data name of animation. + 24, // Maximum dimension of animation. + 2, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Piff( + ANIM_PIFF, // Animation number. + "PIFF", // Data name of animation. + 13, // Maximum dimension of animation. + 1, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const PiffPiff( + ANIM_PIFFPIFF, // Animation number. + "PIFFPIFF", // Data name of animation. + 20, // Maximum dimension of animation. + 2, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Fire3( + ANIM_FIRE_SMALL, // Animation number. + "FIRE3", // Data name of animation. + 23, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0008, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 2, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Fire1( + ANIM_FIRE_MED2, // Animation number. + "FIRE1", // Data name of animation. + 23, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0010, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 3, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Fire4( + ANIM_FIRE_TINY, // Animation number. + "FIRE4", // Data name of animation. + 7, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0008, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 3, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Fire2( + ANIM_FIRE_MED, // Animation number. + "FIRE2", // Data name of animation. + 23, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0010, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 3, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const OilFieldBurn( + ANIM_OILFIELD_BURN, // Animation number. + "FLMSPT", // Data name of animation. + 42, // Maximum dimension of animation. + 58, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 33, // Loop start frame number. + 99, // Ending frame of loop back. + 66, // Number of animation stages. + 65535, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Gunfire( + ANIM_MUZZLE_FLASH, // Animation number. + "GUNFIRE", // Data name of animation. + 16, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. +// 2, // Number of times the animation loops. + 1, // Number of animation stages. +// 2, // Number of animation stages. + 1, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +#ifdef NEVER +static AnimTypeClass const E1RotFire( + ANIM_E1_ROT_FIRE, // Animation number. + "E1ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 28, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const E1RotGrenade( + ANIM_E1_ROT_GRENADE, // Animation number. + "E1ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 24, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const E1RotGun( + ANIM_E1_ROT_GUN, // Animation number. + "E1ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 16, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const E1RotExp( + ANIM_E1_ROT_EXP, // Animation number. + "E1ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 20, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const E2RotFire( + ANIM_E2_ROT_FIRE, // Animation number. + "E2ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 28, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const E2RotGrenade( + ANIM_E2_ROT_GRENADE, // Animation number. + "E2ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 24, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const E2RotGun( + ANIM_E2_ROT_GUN, // Animation number. + "E2ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 16, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const E2RotExp( + ANIM_E2_ROT_EXP, // Animation number. + "E2ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 20, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const E3RotFire( + ANIM_E3_ROT_FIRE, // Animation number. + "E3ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 28, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const E3RotGrenade( + ANIM_E3_ROT_GRENADE, // Animation number. + "E3ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 24, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const E3RotGun( + ANIM_E3_ROT_GUN, // Animation number. + "E3ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 16, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const E3RotExp( + ANIM_E3_ROT_EXP, // Animation number. + "E3ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 20, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const E4RotFire( + ANIM_E4_ROT_FIRE, // Animation number. + "E4ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 28, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const E4RotGrenade( + ANIM_E4_ROT_GRENADE, // Animation number. + "E4ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 24, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const E4RotGun( + ANIM_E4_ROT_GUN, // Animation number. + "E4ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 16, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const E4RotExp( + ANIM_E4_ROT_EXP, // Animation number. + "E4ROT", // Data name of animation. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Is a flat on the ground animation? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 30, // Delay between frames. + 20, // Starting frame number. + 0, // Loop start frame number. + 0, // Loopback frame number. + 4, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +#endif + +static AnimTypeClass const SmokeM( + ANIM_SMOKE_M, // Animation number. + "SMOKE_M", // Data name of animation. + 28, // Maximum dimension of animation. + 30, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 67, // Loop start frame number. + -1, // Loopback frame number. + -1, // Number of animation stages. + 6, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +/* +** Mini-gun fire effect -- used by guard towers. +*/ +static AnimTypeClass const GUNN( + ANIM_GUN_N, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNNW( + ANIM_GUN_NW, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 6, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNW( + ANIM_GUN_W, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 12, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNSW( + ANIM_GUN_SW, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNS( + ANIM_GUN_S, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 24, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNSE( + ANIM_GUN_SE, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 30, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNE( + ANIM_GUN_E, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 36, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNNE( + ANIM_GUN_NE, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 42, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const IonCannon( + ANIM_ION_CANNON, // Animation number. + "IONSFX", // Data name of animation. + 48, // Maximum dimension of animation. + 11, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + true, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 15, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_ION_CANNON, // Sound effect to play. + ANIM_ART_EXP1 +); + +static AnimTypeClass const AtomBomb( + ANIM_ATOM_BLAST, // Animation number. + "ATOMSFX", // Data name of animation. + 72, // Maximum dimension of animation. + 19, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + true, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NUKE_EXPLODE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const AtomDoor( + ANIM_ATOM_DOOR, // Animation number. + "ATOMDOOR", // Data name of animation. + 48, // Maximum dimension of animation. + 19, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + true, // Forms a crater? + true, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const CDeviator( + ANIM_CRATE_DEVIATOR, // Animation number. + "DEVIATOR", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CDollar( + ANIM_CRATE_DOLLAR, // Animation number. + "DOLLAR", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CEarth( + ANIM_CRATE_EARTH, // Animation number. + "EARTH", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CEmpulse( + ANIM_CRATE_EMPULSE, // Animation number. + "EMPULSE", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CInvun( + ANIM_CRATE_INVUN, // Animation number. + "INVUN", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CMine( + ANIM_CRATE_MINE, // Animation number. + "MINE", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CRapid( + ANIM_CRATE_RAPID, // Animation number. + "RAPID", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CStealth( + ANIM_CRATE_STEALTH, // Animation number. + "STEALTH2", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CMissile( + ANIM_CRATE_MISSILE, // Animation number. + "MISSILE2", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); + +static AnimTypeClass const MoveFlash( + ANIM_MOVE_FLASH, // Animation number. + "MOVEFLSH", // Data name of animation. + 24, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Normalized animation rate? + true, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); + +static AnimTypeClass const ChemBall( + ANIM_CHEM_BALL, // Animation number. + "CHEMBALL", // Data name of animation. + 21, // Maximum dimension of animation. + 5, // Biggest animation stage. + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0x0000, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_FLAMER1, // Sound effect to play. + ANIM_NONE +); + + + +AnimTypeClass const * const AnimTypeClass::Pointers[ANIM_COUNT] = { + &FBall1, + &Grenade, + &Frag1, + &Frag3, + &VehHit1, + &VehHit2, + &VehHit3, + &ArtExp1, + &Napalm1, + &Napalm2, + &Napalm3, + &SmokePuff, + &Piff, + &PiffPiff, + &FlameN, + &FlameNE, + &FlameE, + &FlameSE, + &FlameS, + &FlameSW, + &FlameW, + &FlameNW, + &ChemN, + &ChemNE, + &ChemE, + &ChemSE, + &ChemS, + &ChemSW, + &ChemW, + &ChemNW, + &Fire3, + &Fire2, + &Fire1, + &Fire4, + &Gunfire, +#ifdef NEVER + &E1RotFire, + &E1RotGrenade, + &E1RotGun, + &E1RotExp, + &E2RotFire, + &E2RotGrenade, + &E2RotGun, + &E2RotExp, + &E3RotFire, + &E3RotGrenade, + &E3RotGun, + &E3RotExp, + &E4RotFire, + &E4RotGrenade, + &E4RotGun, + &E4RotExp, +#endif + &SmokeM, + &BurnSmall, + &BurnMed, + &BurnBig, + &OnFireSmall, + &OnFireMed, + &OnFireBig, + &SAMN, + &SAMNE, + &SAME, + &SAMSE, + &SAMS, + &SAMSW, + &SAMW, + &SAMNW, + &GUNN, + &GUNNE, + &GUNE, + &GUNSE, + &GUNS, + &GUNSW, + &GUNW, + &GUNNW, + &LZSmoke, + &IonCannon, + &AtomBomb, + &CDeviator, + &CDollar, + &CEarth, + &CEmpulse, + &CInvun, + &CMine, + &CRapid, + &CStealth, + &CMissile, + &AtomDoor, + &MoveFlash, + &OilFieldBurn, + &TricDie, + &TRexDie, + &StegDie, + &RaptDie, + &ChemBall +}; + + +/*********************************************************************************************** + * AnimTypeClass::AnimTypeClass -- Constructor for animation types. * + * * + * This is the constructor for static objects that elaborate the various animation types * + * allowed in the game. Each animation in the game is of one of these types. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1994 JLB : Created. * + *=============================================================================================*/ +AnimTypeClass::AnimTypeClass(AnimType anim, char const *name, int size, int biggest, + bool isnormal, bool iswhitetrans, bool isscorcher, bool iscrater, bool issticky, bool ground, + bool istrans, bool isflame, unsigned int damage, + int delaytime, int start, int loopstart, int loopend, int stages, int loops, + VocType sound, AnimType chainto) : + ObjectTypeClass(true, false, false, true, false, false, true, true, TXT_NONE, name, ARMOR_NONE, 0) +{ + Biggest = biggest; + ChainTo = chainto; + Damage = damage; + Delay = (unsigned char)delaytime; + IsCraterForming = iscrater; + IsFlameThrower = isflame; + IsGroundLayer = ground; + IsNormalized = isnormal; + IsScorcher = isscorcher; + IsSticky = issticky; + IsTranslucent = istrans; + IsWhiteTrans = iswhitetrans; + LoopEnd = loopend; + LoopStart = loopstart; + Loops = (unsigned char)loops; + Size = size; + Sound = sound; + Stages = stages; + Start = start; + Type = anim; +} + + +/*********************************************************************************************** + * AnimTypeClass::One_Time -- Performs one time action for animation types. * + * * + * This will load the animation shape data. It is called by the game initialization * + * process. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine should be called ONLY once. * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void AnimTypeClass::One_Time(void) +{ + AnimType index; + + for (index = ANIM_FIRST; index < ANIM_COUNT; index++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + + _makepath(fullname, NULL, NULL, As_Reference(index).IniName, ".SHP"); + + RawFileClass file(fullname); + if (file.Is_Available()) { + ((void const *&)As_Reference(index).ImageData) = Load_Alloc_Data(file); + } else { + ((void const *&)As_Reference(index).ImageData) = MixFileClass::Retrieve(fullname); + } + } +} + + diff --git a/AIRCRAFT.CPP b/AIRCRAFT.CPP new file mode 100644 index 0000000..d5310b6 --- /dev/null +++ b/AIRCRAFT.CPP @@ -0,0 +1,3495 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\aircraft.cpv 2.12 19 Jun 1995 09:27:22 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : AIRCRAFT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 22, 1994 * + * * + * Last Update : August 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AircraftClass::AI -- Processes the normal non-graphic AI for the aircraft. * + * AircraftClass::Active_Click_With -- Handles clicking over specified cell. * + * AircraftClass::Active_Click_With -- Handles clicking over specified object. * + * AircraftClass::AircraftClass -- The constructor for aircraft objects. * + * AircraftClass::As_Target -- Returns aircraft as a target number. * + * AircraftClass::Can_Enter_Cell -- Determines if the aircraft can land at this location. * + * AircraftClass::Cell_Seems_Ok -- Checks to see if a cell is good to enter. * + * AircraftClass::Desired_Load_Dir -- Determines where passengers should line up. * + * AircraftClass::Draw_It -- Renders an aircraft object at the location specified. * + * AircraftClass::Enter_Idle_Mode -- Gives the aircraft an appropriate mission. * + * AircraftClass::Exit_Object -- Unloads passenger from aircraft. * + * AircraftClass::Fire_At -- Handles firing a projectile from an aircraft. * + * AircraftClass::Fire_Coord -- Calculates the point of origin for a bullet. * + * AircraftClass::Fire_Direction -- Determines the direction of fire. * + * AircraftClass::Good_Fire_Location -- Searches for and finds a good spot to fire from. * + * AircraftClass::Good_LZ -- Locates a good spot ot land. * + * AircraftClass::In_Which_Layer -- Determine which render layer the aircraft lies. * + * AircraftClass::Init -- Initialize the aircraft system to an empty state. * + * AircraftClass::Is_LZ_Clear -- Determines if landing zone is free for landing. * + * AircraftClass::Mark -- Flags cells under the aircraft so that they will be redrawn. * + * AircraftClass::Mission_Attack -- Handles the attack mission for aircraft. * + * AircraftClass::Mission_Enter -- Control aircraft to fly to the helipad or repair center. * + * AircraftClass::Mission_Guard -- Handles aircraft in guard mode. * + * AircraftClass::Mission_Guard_Area -- Handles the aircraft guard area logic. * + * AircraftClass::Mission_Hunt -- Maintains hunt AI for the aircraft. * + * AircraftClass::Mission_Move -- Handles movement mission. * + * AircraftClass::Mission_Retreat -- Handles the aircraft logic for leaving the battlefield. * + * AircraftClass::Mission_Unload -- Handles unloading cargo. * + * AircraftClass::New_LZ -- Find a good landing zone. * + * AircraftClass::Overlap_List -- Returns with list of cells the aircraft overlaps. * + * AircraftClass::Pip_Count -- Returns the number of "objects" in aircraft. * + * AircraftClass::Player_Assign_Mission -- Handles player input to assign a mission. * + * AircraftClass::Pose_Dir -- Fetches the natural landing facing. * + * AircraftClass::Process_Fly_To -- Handles state machine for flying to destination. * + * AircraftClass::Process_Landing -- Landing process state machine handler. * + * AircraftClass::Process_Take_Off -- State machine support for taking off. * + * AircraftClass::Read_INI -- Reads aircraft object data from an INI file. * + * AircraftClass::Rearm_Delay -- Returns the delay between shots for this aircraft. * + * AircraftClass::Receive_Message -- Handles receipt of radio messages. * + * AircraftClass::Response_Attack -- Gives audio response to attack order. * + * AircraftClass::Response_Move -- Gives audio response to move request. * + * AircraftClass::Response_Select -- Gives audio response when selected. * + * AircraftClass::Scatter -- Causes the aircraft to move away a bit. * + * AircraftClass::Set_Speed -- Sets the speed for the aircraft. * + * AircraftClass::Sort_Y -- Figures the sorting coordinate. * + * AircraftClass::Take_Damage -- Applies damage to the aircraft. * + * AircraftClass::Threat_Range -- Returns with a range to scan for targets. * + * AircraftClass::Unlimbo -- Removes an aircraft from the limbo state. * + * AircraftClass::What_Action -- Determines what action to perform. * + * AircraftClass::What_Action -- Determines what action to perform. * + * AircraftClass::Write_INI -- Writes the current aircraft objects to an INI file. * + * AircraftClass::operator delete -- Deletes the aircraft object. * + * AircraftClass::operator new -- Allocates a new aircraft object from the pool * + * AircraftClass::~AircraftClass -- Destructor for aircraft object. * + * AircraftClass::Validate -- validates aircraft pointer * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * AircraftClass::VTable; + + +/*********************************************************************************************** + * AircraftClass::Validate -- validates aircraft pointer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int AircraftClass::Validate(void) const +{ + int num; + + num = Aircraft.ID(this); + if (num < 0 || num >= AIRCRAFT_MAX) { + Validate_Error("AIRCRAFT"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * AircraftClass::As_Target -- Returns aircraft as a target number. * + * * + * This routine will convert the aircraft into a target number. This target number can * + * then be assigned to a targeting or navigation computer. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the aircraft as a target number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +TARGET AircraftClass::As_Target(void) const +{ + Validate(); + return(Build_Target(KIND_AIRCRAFT, Aircraft.ID(this))); +} + + +/*********************************************************************************************** + * AircraftClass::operator new -- Allocates a new aircraft object from the pool * + * * + * This routine will allocate an aircraft object from the free aircraft object pool. If * + * there are no free object available, then this routine will fail (return NULL). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocate aircraft object or NULL if none were * + * available. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void * AircraftClass::operator new(size_t) +{ + void * ptr = Aircraft.Allocate(); + if (ptr) { + ((AircraftClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * AircraftClass::operator delete -- Deletes the aircraft object. * + * * + * This routine will return the aircraft object back to the free aircraft object pool. * + * * + * INPUT: ptr -- Pointer to the aircraft object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::operator delete(void *ptr) +{ + if (ptr) { + ((AircraftClass *)ptr)->IsActive = false; + } + Aircraft.Free((AircraftClass *)ptr); +} + + +/*********************************************************************************************** + * AircraftClass::AircraftClass -- The constructor for aircraft objects. * + * * + * This routine is the constructor for aircraft objects. An aircraft object can be * + * created and possibly placed into the game system by this routine. * + * * + * INPUT: classid -- The type of aircraft to create. * + * * + * house -- The owner of this aircraft. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +AircraftClass::AircraftClass(AircraftType classid, HousesType house) : + Class(&AircraftTypeClass::As_Reference(classid)), + FootClass(house) +{ + /* + ** For two shooters, clear out the second shot flag -- it will be set the first time + ** the object fires. For non two shooters, set the flag since it will never be cleared + ** and the second shot flag tells the system that normal rearm times apply -- this is + ** what is desired for non two shooters. + */ + if (Class->IsTwoShooter) { + IsSecondShot = false; + } else { + IsSecondShot = true; + } + Ammo = Class->MaxAmmo; + AttacksRemaining = 3; + Altitude = FLIGHT_LEVEL; + IsLanding = false; + IsTakingOff = false; + IsHovering = false; + IsHoming = false; + Strength = Class->MaxStrength; + NavCom = TARGET_NONE; + SecondaryFacing = PrimaryFacing; + Jitter = 0; + + /* + ** Keep count of the number of units created. Dont track cargo planes as they are created + ** automatically, not bought. + */ + if (classid != AIRCRAFT_CARGO && GameToPlay == GAME_INTERNET){ + House->AircraftTotals->Increment_Unit_Total((int)classid); + } +} + + +/*********************************************************************************************** + * AircraftClass::Unlimbo -- Removes an aircraft from the limbo state. * + * * + * This routine is used to transition the aircraft from the limbo to the non limbo state. * + * It occurs when the aircraft is placed on the map for whatever reason. When it is * + * unlimboed, only then will normal game processing recognize it. * + * * + * INPUT: coord -- The coordinate that the aircraft should appear at. * + * * + * dir -- The direction it should start facing. * + * * + * strength (optional) -- sets initial strength * + * * + * mission (optional) -- sets initial mission * + * * + * OUTPUT: bool; Was the aircraft unlimboed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Unlimbo(COORDINATE coord, DirType dir) +{ + Validate(); + if (FootClass::Unlimbo(coord, dir)) { + + /* + ** Ensure that the owning house knows about the + ** new object. + */ + House->AScan |= (1L << Class->Type); + House->ActiveAScan |= (1L << Class->Type); + + /* + ** Forces the body of the helicopter to face the correct direction. + */ + SecondaryFacing = dir; + + /* + ** Start rotor animation. + */ + Set_Rate(1); + Set_Stage(0); + + /* + ** Presume it starts in flight? + */ + if (Altitude == FLIGHT_LEVEL) { + Set_Speed(0xFF); + } else { + Set_Speed(0); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Draw_It -- Renders an aircraft object at the location specified. * + * * + * This routine is used to display the aircraft object at the coordinates specified. * + * The tactical map display uses this routine for all aircraft rendering. * + * * + * INPUT: x,y -- The coordinates to render the aircraft at. * + * * + * window -- The window that the coordinates are based upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Draw_It(int x, int y, WindowNumberType window) +{ + Validate(); + void const * shapefile; // Working shape file pointer. + int shapenum = 0; + int facing = Facing_To_32(SecondaryFacing); + + + /* + ** Verify the legality of the unit class. + */ + shapefile = Class->Get_Image_Data(); + if (!shapefile) return; + shapenum = UnitClass::BodyShape[facing]; + + /* + ** The orca attack helicopter uses a special shape set when it is travelling + ** forward above a certain speed. + */ + if (*this == AIRCRAFT_ORCA && Get_Speed() >= MPH_MEDIUM_FAST) { + shapenum += 32; + } + + /* + ** If there is a door on this aircraft (Chinook), then adjust the + ** shape number to match the door open state. + */ + if (!Is_Door_Closed()) { + shapenum = 32 + Door_Stage(); + } + + /* + ** Helicopters that are flying have a "bobbing" effect. + */ + int jitter = 0; + if (Altitude == FLIGHT_LEVEL && !Class->IsFixedWing) { + Jitter++; + + static int _jitter[] = {0,0,0,0,1,1,1,0,0,0,0,0,-1,-1,-1,0}; + jitter = _jitter[Jitter % 16]; + } + + /* + ** Special manual shadow draw code. + */ + if (Visual_Character() <= VISUAL_DARKEN) { + CC_Draw_Shape(shapefile, shapenum, x+1, y+2, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, Map.FadingShade, NULL); + } + + /* + ** Actually draw the root body of the unit. + */ + Techno_Draw_Object(shapefile, shapenum, x, (y-Altitude)+jitter, window); +// CC_Draw_Shape(shapefile, shapenum, x, (y-Altitude)+jitter, window, SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, House->Remap_Table(IsBlushing, true), Map.UnitShadow); + + /* + ** Draw rotor effects. The rotor art can be either generic or custom. Custom rotor + ** art has a different rotor set for each facing. Rotor shapes occur after the first + ** 32 shapes of the helicopter body. + */ + if (Class->IsRotorEquipped) { + ShapeFlags_Type flags = SHAPE_CENTER|SHAPE_WIN_REL; + + /* + ** The rotor shape number depends on whether the helicopter is idling + ** or not. A landed helicopter uses slow moving "idling" blades. + */ + if (Altitude == 0) { + shapenum = (Fetch_Stage()%8)+4; + flags = flags | SHAPE_GHOST; + } else { + shapenum = Fetch_Stage()%4; + flags = flags | SHAPE_FADING|SHAPE_PREDATOR; + } + + if (*this == AIRCRAFT_TRANSPORT) { + int _stretch[FACING_COUNT] = {8, 9, 10, 9, 8, 9, 10, 9}; + + /* + ** Dual rotors offset along flight axis. + */ + short xx = x; + short yy = y-Altitude; + FacingType face = Dir_Facing(SecondaryFacing); + Move_Point(xx, yy, SecondaryFacing.Current(), _stretch[face]); + CC_Draw_Shape(Class->RRotorData, shapenum, xx, yy-2, window, flags, NULL, Map.UnitShadow); + + Move_Point(xx, yy, SecondaryFacing.Current()+DIR_S, _stretch[face]*2); + CC_Draw_Shape(Class->LRotorData, shapenum, xx, yy-2, window, flags, NULL, Map.UnitShadow); + + } else { + + /* + ** Single rotor centered about shape. + */ + CC_Draw_Shape(Class->RRotorData, shapenum, x, (y-Altitude)-2, window, flags, NULL, Map.UnitShadow); + } + } + + FootClass::Draw_It(x, y-Altitude, window); +} + + +/*********************************************************************************************** + * AircraftClass::Read_INI -- Reads aircraft object data from an INI file. * + * * + * This routine is used to read the aircraft object data from the INI file buffer * + * specified. This is used by the scenario loader code to interpret the INI file and * + * create the specified objects therein. * + * * + * INPUT: buffer -- Pointer to the INI buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Read_INI(char *buffer) +{ + AircraftClass *air; // Working unit pointer. + char *tbuffer; // Accumulation buffer of unit IDs. + HousesType inhouse; // Unit house. + AircraftType classid; // Unit class. + int len; // Length of data in buffer. + char buf[128]; + + len = strlen(buffer) + 2; + tbuffer = buffer + len; + + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); + while (*tbuffer != '\0') { + + WWGetPrivateProfileString(INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, buffer); + inhouse = HouseTypeClass::From_Name(strtok(buf, ",")); + if (inhouse != HOUSE_NONE) { + classid = AircraftTypeClass::From_Name(strtok(NULL, ",")); + + if (classid != AIRCRAFT_NONE) { + + air = new AircraftClass(classid, inhouse); + if (air) { + COORDINATE coord; + int strength; + DirType dir; + + /* + ** Read the raw data. + */ + strength = atoi(strtok(NULL, ",")); + coord = Cell_Coord((CELL)atoi(strtok(NULL, ","))); + dir = (DirType)atoi(strtok(NULL, ",")); + + if (!Map.In_Radar(Coord_Cell(coord))) { + delete air; + } else { + + air->Strength = Fixed_To_Cardinal(air->Class->MaxStrength, strength); + if (air->Unlimbo(coord, dir)) { + air->Assign_Mission(AircraftClass::Mission_From_Name(strtok(NULL, ",\n\r"))); + } else { + delete air; + } + } + } + } + } + tbuffer += strlen(tbuffer)+1; + } +} + + +/*********************************************************************************************** + * AircraftClass::Write_INI -- Writes the current aircraft objects to an INI file. * + * * + * This routine is used to output the current list of aircraft objects to the INI file * + * buffer specified. It is typically used by the scenario editor. * + * * + * INPUT: buffer -- Pointer to the buffer that holds the INI file data. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Write_INI(char *buffer) +{ + int index; + char uname[10]; + char buf[128]; + char *tbuffer; // Accumulation buffer of unit IDs. + + /* + ** First, clear out all existing unit data from the ini file. + */ + tbuffer = buffer + strlen(buffer) + 2; + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-strlen(buffer), buffer); + while (*tbuffer != '\0') { + WWWritePrivateProfileString(INI_Name(), tbuffer, NULL, buffer); + tbuffer += strlen(tbuffer)+1; + } + + /* + ** Write the unit data out. + */ + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * unit; + + unit = Aircraft.Ptr(index); + if (!unit->IsInLimbo) { + + sprintf(uname, "%03d", index); + sprintf(buf, "%s,%s,%d,%u,%d,%s", + unit->House->Class->IniName, + unit->Class->IniName, + unit->Health_Ratio(), + Coord_Cell(unit->Coord), + unit->PrimaryFacing.Current(), + MissionClass::Mission_Name(unit->Mission) + ); + WWWritePrivateProfileString(INI_Name(), uname, buf, buffer); + } + } +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Hunt -- Maintains hunt AI for the aircraft. * + * * + * Hunt AI consists of finding a target and attacking it. If there is no target assigned * + * and this unit doesn't automatically hunt for more targets, then it will change * + * mission to a more passive (land and await further orders) type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of ticks before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Mission_Hunt(void) +{ + Validate(); + if (Class->IsFixedWing) { + enum { + LOOK_FOR_TARGET, + FLY_TO_TARGET, + DROP_BOMBS + }; + switch (Status) { + + /* + ** Acquiring target stage. + */ + case LOOK_FOR_TARGET: + if (Target_Legal(TarCom)) { + Status = FLY_TO_TARGET; + return(1); + } else { + Assign_Target(Greatest_Threat(THREAT_NORMAL)); + + /* + ** If there is no target, then this aircraft should just do its normal thing. + */ + if (!Target_Legal(TarCom)) { + Enter_Idle_Mode(); + } + } + break; + + /* + ** Homing in on target stage. + */ + case FLY_TO_TARGET: + if (Target_Legal(TarCom)) { + IsHoming = true; + if (!PrimaryFacing.Is_Rotating()) { + PrimaryFacing.Set_Desired(Direction(TarCom)); + } + if (Distance(TarCom) < 0x0380) { + IsHoming = false; + Status = DROP_BOMBS; + return(1); + } + } else { + Status = LOOK_FOR_TARGET; + } + break; + + /* + ** Dropping a stream of bombs phase. + */ + case DROP_BOMBS: + if (!Ammo) { + AttacksRemaining--; + if (!AttacksRemaining) { + Assign_Mission(MISSION_RETREAT); + Commence(); + } else { + Ammo = Class->MaxAmmo; + Status = LOOK_FOR_TARGET; + } + } + if (!Target_Legal(TarCom)) { + Status = LOOK_FOR_TARGET; + } else { + Fire_At(TarCom, 0); + Map[::As_Cell(TarCom)].Incoming(Coord, true); + return(5); + } + break; + } + } else { + if (!Ammo) { + Enter_Idle_Mode(); + } else { + Assign_Mission(MISSION_ATTACK); + return(1); +// return(FootClass::Mission_Hunt()); + } + } + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * AircraftClass::AI -- Processes the normal non-graphic AI for the aircraft. * + * * + * This handles the non-graphic AI processing for the aircraft. This usually entails * + * maintenance and other AI functions. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::AI(void) +{ + Validate(); + /* + ** A Mission change can always occur if the aircraft is landed or flying. + */ + if (!IsLanding && !IsTakingOff) { + Commence(); + } + + FootClass::AI(); + + /* + ** A Mission change can always occur if the aircraft is landed or flying. + */ + if (!IsLanding && !IsTakingOff) { + Commence(); + } + + /* + ** Handle any body rotation at this time. Body rotation can occur even if the + ** flying object is not actually moving. + */ + if (PrimaryFacing.Is_Rotating()) { + if (PrimaryFacing.Rotation_Adjust(Class->ROT)) { + Mark(); + } + } + if (Class->IsFixedWing) { + SecondaryFacing = PrimaryFacing; + } + if (SecondaryFacing.Is_Rotating()) { + if (SecondaryFacing.Rotation_Adjust(Class->ROT)) { + Mark(); + } + } + if (Physics(Coord, PrimaryFacing) != RESULT_NONE) { + Mark(); + } + + /* + ** Perform sighting every so often as controlled by the sight timer. + */ + if (IsOwnedByPlayer && Class->SightRange && SightTimer.Expired()) { + Map.Sight_From(Coord_Cell(Coord), Class->SightRange, false); + SightTimer = TICKS_PER_SECOND; + } + + /* + ** Handle landing and taking off logic. Helicopters are prime users of this technique. The + ** aircraft will either gain or lose altitude as appropriate. As the aircraft transitions + ** between flying level and ground level, it will be moved into the appropriate render + ** layer. + */ + if (Is_Door_Closed() && (IsLanding || IsTakingOff)) { + Mark(); + LayerType layer = In_Which_Layer(); + + if (IsLanding) { + if (Altitude) Altitude--; + if (!Altitude) { + IsLanding = false; + Set_Speed(0); + if (Target_Legal(NavCom) && As_Techno(NavCom) == Contact_With_Whom()) { + if (In_Radio_Contact() && Transmit_Message(RADIO_IM_IN) != RADIO_ROGER) { + Scatter(0, true); + } + } + } + } + if (IsTakingOff) { + Altitude++; + if (Altitude >= FLIGHT_LEVEL) { + Altitude = FLIGHT_LEVEL; + IsTakingOff = false; + } + } + + /* + ** Make adjustments for altitude by moving from one layer to another as + ** necessary. + */ + if (layer != In_Which_Layer()) { + + /* + ** When the aircraft is about to enter the ground layer, perform on last + ** check to see if it is legal to enter that location. If not, then + ** start the take off process. Let the normal logic handle this + ** change of plans. + */ + bool ok = true; + if (In_Which_Layer() == LAYER_GROUND) { + if (!Is_LZ_Clear(::As_Target(Coord_Cell(Coord)))) { + IsTakingOff = true; + Altitude++; + ok = false; + } + } + + if (ok) { + /* + ** If landing in a cell that already contains an object, then + ** the landing attempt must be aborted. + */ + Map.Remove(this, layer); + Map.Submit(this, In_Which_Layer()); + + /* + ** When the aircraft is close to the ground, it should exist as a ground object. + ** This aspect is controlled by the Place_Down and Pick_Up functions. + */ + if (In_Which_Layer() == LAYER_GROUND) { + Assign_Destination(TARGET_NONE); // Clear the navcom. + Transmit_Message(RADIO_TETHER); + Map.Place_Down(Coord_Cell(Coord), this); + if (IsOwnedByPlayer) { + Map.Sight_From(Coord_Cell(Coord), 1, false); + } + } else { + Transmit_Message(RADIO_UNTETHER); + Map.Pick_Up(Coord_Cell(Coord), this); + + /* + ** If the navigation computer is not attached to the object this + ** aircraft is in radio contact with, then assume that radio + ** contact is now superfluous. Break radio contact. + */ + if (In_Radio_Contact() && Target_Legal(NavCom) && NavCom != Contact_With_Whom()->As_Target()) { + Transmit_Message(RADIO_OVER_OUT); + } + } + } + } + } + + /* + ** Always flag the map draw process to occur if there is an aircraft in the view. + ** This ensures that it will be rendered even if there is nothing else that flagged + ** the map to be redrawn. + */ + if (Map.In_View(Coord_Cell(Coord))) { + Map.Flag_To_Redraw(false); + Map.DisplayClass::IsToRedraw = true; + } + + /* + ** When aircraft leave the edge of the map, they might get destroyed. This occurs if the + ** aircraft is a non-player produced unit and it has completed its mission. A transport + ** helicopter that has already delivered reinforcements is a good example of this. + */ + if (!Map.In_Radar(Coord_Cell(Coord))) { + if (Mission == MISSION_RETREAT /*|| (*this == AIRCRAFT_CARGO && !Is_Something_Attached())*/) { + + /* + ** Check to see if there are any civilians aboard. If so, then flag the house + ** that the civilian evacuation trigger event has been fulfilled. + */ + while (Is_Something_Attached()) { + FootClass * obj = Detach_Object(); + if (obj->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)obj)->Class->IsCivilian && !((InfantryClass *)obj)->IsTechnician) { + House->IsCivEvacuated = true; + } + + /* + ** Transport planes that leave can only be because they carry purchased + ** equipment and must be have their cost refunded. + */ + if (*this == AIRCRAFT_CARGO) { + House->Refund_Money(obj->Class_Of().Cost_Of()); + } + delete obj; + } + Stun(); + delete this; + return; + } + } else { + IsLocked = true; +// House->NewAScan |= (1L << Class->Type); + +#ifdef NEVER + /* + ** Transport helicopters must ensure that their passengers are properly + ** considered "alive" by setting the appropriate scan bits. + */ + FootClass const * foot = Attached_Object(); + while (foot) { + switch (foot->What_Am_I()) { + case RTTI_UNIT: + House->NewUScan |= (1L << ((UnitTypeClass const &)Class_Of()).Type); + break; + + case RTTI_INFANTRY: + House->NewIScan |= (1L << ((InfantryTypeClass const &)Class_Of()).Type); + break; + } + + foot = (FootClass const *)foot->Next; + } +#endif + } +} + + +/*********************************************************************************************** + * AircraftClass::Mark -- Flags cells under the aircraft so that they will be redrawn. * + * * + * This routine is used to flag the cells under the aircraft so that those cells will * + * be redrawn during the next map drawing process. This is a necessary step whenever the * + * aircraft moves or changes shape. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Mark(MarkType mark) +{ + Validate(); + if (FootClass::Mark(mark)) { + Map.Refresh_Cells(Coord_Cell(Coord), Occupy_List()); + Map.Refresh_Cells(Coord_Cell(Coord), Overlap_List()); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Overlap_List -- Returns with list of cells the aircraft overlaps. * + * * + * When aircraft are flying, they can overlap quite a number of cells. These cells can * + * be determined from the coordinate where the aircraft is centered and the size of the * + * aircraft's shape. Landed aircraft are a special case and are usually much smaller * + * than when flying. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to a cell offset list that specifies all cells that * + * the aircraft overlaps given the aircraft's current state. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +short const *AircraftClass::Overlap_List(void) const +{ + Validate(); + static short const _list[] = { + -(MAP_CELL_W-1), -MAP_CELL_W, -(MAP_CELL_W+1), + -1, 0, 1, + (MAP_CELL_W-1), MAP_CELL_W, (MAP_CELL_W+1), + -((MAP_CELL_W*2)-1), -(MAP_CELL_W*2), -((MAP_CELL_W*2)+1), + -((MAP_CELL_W*3)-1), -(MAP_CELL_W*3), -((MAP_CELL_W*3)+1), + REFRESH_EOL + }; + + if (Altitude) { + return(_list); + //return Coord_Spillage_List(Coord, 25); + } + return(Class->Overlap_List()); +} + + +/*********************************************************************************************** + * AircraftClass::Init -- Initialize the aircraft system to an empty state. * + * * + * This routine is used to clear out the aircraft allocation system. It is called in * + * preparation for a scenario load or save game load. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Init(void) +{ + AircraftClass *ptr; + + Aircraft.Free_All(); + + ptr = new AircraftClass(); + VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0]; + delete ptr; +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Unload -- Handles unloading cargo. * + * * + * This function is used to handle finding, heading toward, landing, and unloading the * + * cargo from the aircraft. Once unloading of cargo has occurred, then the aircraft follows * + * a different mission. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of game ticks to delay before calling this function again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/31/94 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Mission_Unload(void) +{ + Validate(); + if (Class->IsFixedWing) { + enum { + PICK_AIRSTRIP, + FLY_TO_AIRSTRIP, + BUG_OUT + }; + + switch (Status) { + + /* + ** Find a suitable airfield to land at. + */ + case PICK_AIRSTRIP: + if (!Target_Legal(NavCom) || !In_Radio_Contact()) { + + BuildingClass * building = Find_Docking_Bay(STRUCT_AIRSTRIP, false); + if (building) { + if (Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) { + Set_Speed(0xFF); + Assign_Destination(building->As_Target()); + if (Team) { + Team->Target = NavCom; + } + Status = FLY_TO_AIRSTRIP; + } + } + + /* + ** If a suitable airfield could not be found, then just randomly change + ** direction and then try again later. + */ + if (Status == PICK_AIRSTRIP) { + + /* + ** If there are no more airstrips, regardless of busy state, then + ** abort this transport plane completely. + */ + if (!(House->ActiveBScan & STRUCTF_AIRSTRIP)) { + Assign_Mission(MISSION_RETREAT); + } + + /* + ** Pick a new direction and fly off. + */ + PrimaryFacing.Set_Desired(Random_Pick(DIR_N, DIR_MAX)); + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + return(TICKS_PER_SECOND*3); + } + } else { + Status = FLY_TO_AIRSTRIP; + } + break; + + /* + ** Home in on target. When close enough, drop the cargo. + */ + case FLY_TO_AIRSTRIP: + if (!Target_Legal(NavCom) || !In_Radio_Contact()) { + Status = PICK_AIRSTRIP; + } else { + + /* + ** If, for some reason, there is no cargo, then don't stick around. + */ + if (!Is_Something_Attached()) { + Status = BUG_OUT; + return(1); + } + + if (!PrimaryFacing.Is_Rotating()) { + PrimaryFacing.Set_Desired(Direction(As_Movement_Coord(NavCom))); + } + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + + int navdist = Distance(As_Movement_Coord(NavCom)); + Altitude = FLIGHT_LEVEL; + if (navdist < 0x0600) { + Altitude = Fixed_To_Cardinal(FLIGHT_LEVEL, Cardinal_To_Fixed(0x0600, navdist)); + } + + if (navdist < 0x0080) { + FootClass * unit = (FootClass *)Detach_Object(); + + if (unit) { + CELL cell = Contact_With_Whom()->Find_Exit_Cell(unit); + if (cell) { + ScenarioInit++; + if (!unit->Unlimbo(Cell_Coord(cell))) { + Attach(unit); + } else { + + /* + ** Cargo planes announce reinforcements when they unload + ** their cargo. + */ + if (*this == AIRCRAFT_CARGO && House == PlayerPtr) { + Speak(VOX_REINFORCEMENTS); + } + unit->IsALoaner = false; + unit->IsLocked = true; + unit->Scatter(0, true); + } + ScenarioInit--; + + Transmit_Message(RADIO_OVER_OUT); + Assign_Target(TARGET_NONE); + } else { + Attach(unit); + } + +// if (Is_Something_Attached()) { +// Status = PICK_AIRSTRIP; +// } else { + Status = BUG_OUT; +// } + } else { + Status = BUG_OUT; + } + } + return(1); + } + break; + + /* + ** All cargo unloaded, head off the map. + */ + case BUG_OUT: + Assign_Mission(MISSION_RETREAT); + return(1); + } + + } else { + enum { + SEARCH_FOR_LZ, + FLY_TO_LZ, + LAND_ON_LZ, + UNLOAD_PASSENGERS, + TAKE_OFF, + }; + + switch (Status) { + + /* + ** Search for an appropriate destination spot if one isn't already assigned. + */ + case SEARCH_FOR_LZ: + if (Altitude == 0 && (Target_Legal(NavCom) || Coord == As_Coord(NavCom))) { + Status = UNLOAD_PASSENGERS; + } else { + if (!Is_LZ_Clear(NavCom)) { + Assign_Destination(New_LZ(::As_Target(Waypoint[WAYPT_REINF]))); + } else { + if (Altitude == FLIGHT_LEVEL) { + Status = FLY_TO_LZ; + } else { + Status = TAKE_OFF; + } + } + } + break; + + /* + ** Fly to destination. + */ + case FLY_TO_LZ: + if (Is_LZ_Clear(NavCom)) { + int distance = Process_Fly_To(true); + + if (distance < 0x0100) { + SecondaryFacing.Set_Desired(Pose_Dir()); + + if (distance < 0x0010) { + Status = LAND_ON_LZ; + } + return(1); + } else { + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + return(5); + } + } else { + Status = SEARCH_FOR_LZ; + } + break; + + /* + ** Landing phase. Just delay until landing is complete. At that time, + ** transition to the unloading phase. + */ + case LAND_ON_LZ: + if (IsTakingOff) { + Status = TAKE_OFF; + } else { + if (Process_Landing()) { + Status = UNLOAD_PASSENGERS; + } + } + return(1); + + /* + ** Hold while unloading passengers. When passengers are unloaded the order for this + ** transport gets changed to MISSION_RETREAT. + */ + case UNLOAD_PASSENGERS: + if (!IsTethered) { + if (Is_Something_Attached()) { + FootClass * unit = (FootClass *)Detach_Object(); + + /* + ** First thing is to lift the transport off of the map so that the unlimbo + ** process for the passengers is more likely to succeed. + */ + Map.Pick_Up(Coord_Cell(Coord), this); + + if (!Exit_Object(unit)) { + delete unit; + } + + /* + ** Restore the transport back down on the map. + */ + Map.Place_Down(Coord_Cell(Coord), this); + + if (!Is_Something_Attached()) { + Enter_Idle_Mode(); + } + + } else { + + Enter_Idle_Mode(); + } + } + break; + + /* + ** Aircraft is now taking off. Once the aircraft reaches flying altitude then it + ** will either take off or look for another landing spot to try again. + */ + case TAKE_OFF: { + if (Process_Take_Off()) { + if (Is_Something_Attached()) { + Status = SEARCH_FOR_LZ; + + /* + ** Break off radio contact with the helipad it is taking off from. + */ + if (In_Radio_Contact() && Map[Coord_Cell(Coord)].Cell_Building() == Contact_With_Whom()) { + Transmit_Message(RADIO_OVER_OUT); + } + } else { + Enter_Idle_Mode(); + } + } + return(1); + } + } + } + return(10); +} + + +/*********************************************************************************************** + * AircraftClass::Is_LZ_Clear -- Determines if landing zone is free for landing. * + * * + * This routine examines the landing zone (as specified by the target parameter) in order * + * to determine if it is free to be landed upon. Call this routine when it is necessary * + * to double check this. Typically this occurs right before a helicopter lands and also * + * when determining the landing zone in the first place. * + * * + * INPUT: target -- The target that is the "landing zone". * + * * + * OUTPUT: bool; Is the landing zone clear for landing? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/31/94 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Is_LZ_Clear(TARGET target) const +{ + Validate(); + if (!Target_Legal(target)) return(false); + CELL cell = ::As_Cell(target); + if (!Map.In_Radar(cell)) return(false); + + ObjectClass * object = Map[cell].Cell_Object(); + if (object) { + if (object == this) return(true); + + if (In_Radio_Contact() && Contact_With_Whom() == object) { + return(true); + } + return(false); + } + + if (!Map[cell].Is_Generally_Clear()) return(false); + + return(true); +} + + +/*********************************************************************************************** + * AircraftClass::In_Which_Layer -- Determine which render layer the aircraft lies. * + * * + * This routine is used to figure out which rendering layer the aircraft is located in. * + * It can be determined from the aircraft's height. The layer value is used to handle the * + * display sequence. Objects in lower layers appear beneath objects in higher layers. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the layer that the aircraft is currently located in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/01/1994 JLB : Created. * + *=============================================================================================*/ +LayerType AircraftClass::In_Which_Layer(void) const +{ + Validate(); + if (Class->IsFixedWing) return(LAYER_TOP); + + if (Altitude < FLIGHT_LEVEL - (FLIGHT_LEVEL/3)) { + return(LAYER_GROUND); + } + return(LAYER_TOP); +} + + +/*********************************************************************************************** + * AircraftClass::Sort_Y -- Figures the sorting coordinate. * + * * + * This routine is used to determine the coordinate to use for sorting the aircraft. This * + * sorting value is used when the aircraft is on the ground. At that time the aircraft * + * must be rendered in proper relationship to the other ground objects. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to use when sorting the aircraft with other ground * + * objects. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1994 JLB : Created. * + *=============================================================================================*/ +COORDINATE AircraftClass::Sort_Y(void) const +{ + Validate(); + return(Coord_Add(Coord, 0x00800000L)); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Retreat -- Handles the aircraft logic for leaving the battlefield. * + * * + * This mission will be followed when the aircraft decides that it is time to leave the * + * battle. Typically, this occurs when a loaner transport has dropped off its load or when * + * an attack air vehicle has expended its ordinance. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game ticks to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + * 08/13/1995 JLB : Handles aircraft altitude gain after takeoff logic. * + *=============================================================================================*/ +int AircraftClass::Mission_Retreat(void) +{ + Validate(); + if (Class->IsFixedWing) { + if (Class->IsFixedWing && Altitude < FLIGHT_LEVEL) { + Altitude++; + return(3); + } + return(TICKS_PER_SECOND*10); + } + + enum { + TAKE_OFF, + FACE_MAP_EDGE, + KEEP_FLYING + }; + switch (Status) { + + /* + ** Take off if landed. + */ + case TAKE_OFF: + if (Process_Take_Off()) { + Status = FACE_MAP_EDGE; + } + return(1); + + /* + ** Set facing and speed toward the friendly map edge. + */ + case FACE_MAP_EDGE: + Set_Speed(0xFF); + + /* + ** Take advantage of the fact that the source map edge enumerations happen to + ** occur in a clockwise order and are the first four enumerations of the map + ** edge default for the house. If this value is masked and then shifted, a + ** normalized direction value results. Use this value to head the aircraft + ** toward the "friendly" map edge. + */ + PrimaryFacing.Set_Desired((DirType)((House->Edge & 0x03) << 6)); + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + Status = KEEP_FLYING; + break; + + /* + ** Just do nothing since we are headed toward the map edge. When the edge is + ** reached, the aircraft should be automatically eliminated. + */ + case KEEP_FLYING: + break; + + default: + break; + } + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * AircraftClass::Exit_Object -- Unloads passenger from aircraft. * + * * + * This routine is called when the aircraft is to unload a passenger. The passenger must * + * be able to move under its own power. Typical situation is when a transport helicopter * + * is to unload an infantry unit. * + * * + * INPUT: unit -- Pointer to the unit that is to be unloaded from this aircraft. * + * * + * OUTPUT: bool; Was the unit unloaded successfully? * + * * + * WARNINGS: The unload process is merely started by this routine. Radio contact is * + * established with the unloading unit and when the unit is clear of the aircraft * + * the radio contact will be broken and then the aircraft is free to pursue * + * other. * + * * + * HISTORY: * + * 01/10/1995 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Exit_Object(TechnoClass * unit) +{ + Validate(); + static FacingType _toface[FACING_COUNT] = {FACING_S, FACING_SW, FACING_SE, FACING_NW, FACING_NE, FACING_N, FACING_W, FACING_E}; + CELL cell; + + /* + ** Find a free cell to drop the unit off at. + */ + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + cell = Adjacent_Cell(Coord_Cell(Coord), _toface[face]); + if (unit->Can_Enter_Cell(cell) == MOVE_OK) break; + } + + // Should perform a check here to see if no cell could be found. + + /* + ** If the passenger can be placed on the map, then start it moving toward the + ** destination cell and establish radio contact with the transport. This is used + ** to make sure that the transport waits until the passenger is clear before + ** unloading the next passenger or taking off. + */ + if (unit->Unlimbo(Coord, Facing_Dir(_toface[face]))) { + unit->Assign_Mission(MISSION_MOVE); + unit->Assign_Destination(::As_Target(cell)); + if (Transmit_Message(RADIO_HELLO, unit) == RADIO_ROGER) { + Transmit_Message(RADIO_UNLOAD); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Fire_At -- Handles firing a projectile from an aircraft. * + * * + * Sometimes, aircraft firing needs special handling. Example: for napalm bombs, the * + * bomb travels forward at nearly the speed of the delivery aircraft, not necessarily the * + * default speed defined in the BulletTypeClass structure. * + * * + * INPUT: target -- The target that the projectile is heading for. * + * * + * which -- Which weapon to use in the attack. 0=primary, 1=secondary. * + * * + * OUTPUT: Returns with a pointer to the bullet that was created as a result of this attack. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +BulletClass * AircraftClass::Fire_At(TARGET target, int which) +{ + Validate(); + BulletClass * bullet = FootClass::Fire_At(target, which); + + if (bullet) { + + /* + ** Play the sound effect associated with this weapon. + */ + WeaponTypeClass const * weapon = (which == 0) ? &Weapons[Class->Primary] : &Weapons[Class->Secondary]; + Sound_Effect(weapon->Sound, Coord); + + + /* + ** Falling bullets move at a speed proportionate to the delivery craft. + */ + if (bullet->Class->IsDropping) { + bullet->Fly_Speed(40, bullet->Class->MaxSpeed); + } + } + return(bullet); +} + + +/*********************************************************************************************** + * AircraftClass::Take_Damage -- Applies damage to the aircraft. * + * * + * This routine is used to apply damage to the specified aircraft. This is where any * + * special crash animation will be initiated. * + * * + * INPUT: damage -- Reference to the damage that will be applied to the aircraft. * + * This value will be filled in with the actual damage that was * + * applied. * + * * + * distance -- Distance from the source of the explosion to this aircraft. * + * * + * warhead -- The warhead type that the damage occurs from. * + * * + * source -- Pointer to the originator of the damage. This can be used so that * + * proper "thank you" can be delivered. * + * * + * OUTPUT: Returns with the result of the damage as it affects this aircraft. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/26/1995 JLB : Created. * + *=============================================================================================*/ +ResultType AircraftClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source) +{ + Validate(); + ResultType res = RESULT_NONE; + + /* + ** Flying aircraft take half damage. + */ + if (Altitude) { + damage /= 2; + } + + /* + ** In order for a this to be damaged, it must either be a unit + ** with a crew or a sandworm. + */ + res = FootClass::Take_Damage(damage, distance, warhead, source); + + switch (res) { + case RESULT_DESTROYED: + Kill_Cargo(source); + Death_Announcement(); + new AnimClass(ANIM_FBALL1, Target_Coord()); + delete this; + break; + + default: + case RESULT_HALF: + break; + } + + return(res); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Move -- Handles movement mission. * + * * + * This state machine routine is used when an aircraft (usually helicopter) is to move * + * from one location to another. It will handle any necessary take off and landing this * + * may require. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames that should elapse before this routine * + * is called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Mission_Move(void) +{ + Validate(); + if (Class->IsFixedWing) { + + /* + ** Force aircraft in movement mission into a retreat + ** mission so that it leaves the map. + */ + if (*this == AIRCRAFT_A10) { + Assign_Mission(MISSION_RETREAT); + Commence(); + return(1); + } + + enum { + FLY_TO_AIRSTRIP, + BUG_OUT + }; + switch (Status) { + /* + ** Home in on target. When close enough, drop the cargo. + */ + case FLY_TO_AIRSTRIP: + if (!Target_Legal(NavCom) || !In_Radio_Contact()) { + return(TICKS_PER_SECOND); + } else { + + /* + ** If, for some reason, there is no cargo, then don't stick around. + */ + if (!Is_Something_Attached()) { + Status = BUG_OUT; + return(1); + } + + + if (!PrimaryFacing.Is_Rotating()) { + PrimaryFacing.Set_Desired(Direction(NavCom)); + } + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + if (Distance(NavCom) < 0x0080) { + FootClass * unit = (FootClass *)Detach_Object(); + + if (unit) { + ScenarioInit++; + if (!unit->Unlimbo(Coord_Snap(Contact_With_Whom()->Coord))) { + Attach(unit); + } + ScenarioInit--; + + Transmit_Message(RADIO_OVER_OUT); + Assign_Target(TARGET_NONE); + } + Status = BUG_OUT; + } + } + return(1); + + case BUG_OUT: + return(TICKS_PER_SECOND); + } + return(5); + } + + enum { + VALIDATE_LZ, + TAKE_OFF, + FLY_TO_LZ, + LAND + }; + switch (Status) { + + /* + ** Double check and change LZ if necessary. + */ + case VALIDATE_LZ: + if (!Target_Legal(NavCom)) { + Enter_Idle_Mode(); + } else { + if (!Is_LZ_Clear(NavCom) || !Cell_Seems_Ok(As_Cell(NavCom))) { + Assign_Destination(New_LZ(NavCom)); + } else { + Status = TAKE_OFF; + } + } + break; + + /* + ** Take off if necessary. + */ + case TAKE_OFF: + if (!Target_Legal(NavCom)) { + Status = VALIDATE_LZ; + } else { + if (Process_Take_Off()) { + /* + ** After takeoff is complete, break radio contact with any helipad that this + ** helicopter is taking off from. + */ + if (In_Radio_Contact() && Map[Coord_Cell(Coord)].Cell_Building() == Contact_With_Whom()) { + Transmit_Message(RADIO_OVER_OUT); + } + + Status = FLY_TO_LZ; + } + return(1); + } + break; + + /* + ** Fly toward target. + */ + case FLY_TO_LZ: + if (Is_LZ_Clear(NavCom)) { + int distance = Process_Fly_To(true); + + if (distance < 0x0080) { + if (Target_Legal(TarCom)) { + SecondaryFacing.Set_Desired(Direction(TarCom)); + } else { + SecondaryFacing.Set_Desired(Pose_Dir()); + } + + if (distance < 0x0010) { + Status = LAND; + } + return(1); + } + + SecondaryFacing.Set_Desired(::Direction(Fire_Coord(0), As_Coord(NavCom))); + } else { + Assign_Destination(New_LZ(NavCom)); + if (!Target_Legal(NavCom)) { + Status = LAND; + } + } + return(1); + + /* + ** Land on target. + */ + case LAND: + if (IsTakingOff) { + Assign_Destination(New_LZ(NavCom)); + Status = TAKE_OFF; + } + if (Process_Landing()) { + if (MissionQueue == MISSION_NONE) { + Enter_Idle_Mode(); + } + } + return(1); + } + + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * AircraftClass::Enter_Idle_Mode -- Gives the aircraft an appropriate mission. * + * * + * Use this routine when the mission for the aircraft is in doubt. This routine will find * + * an appropriate mission for the aircraft and dispatch it. * + * * + * INPUT: initial -- Is this called when the unit just leaves a factory or is initially * + * or is initially placed on the map? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/05/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Enter_Idle_Mode(bool ) +{ + Validate(); + MissionType mission = MISSION_GUARD; + if (In_Which_Layer() == LAYER_GROUND) { + if (IsALoaner) { + if (Is_Something_Attached()) { + mission = MISSION_UNLOAD; + } else { + if (Team) { + Team->Remove(this); + } + mission = MISSION_RETREAT; + } + } else { +#ifdef NEVER + if (In_Radio_Contact() && Contact_With_Whom() == Map[Coord_Cell(Coord)].Cell_Techno()) { + Transmit_Message(RADIO_IM_IN); + } +#endif + Assign_Destination(TARGET_NONE); + Assign_Target(TARGET_NONE); + mission = MISSION_GUARD; + } + } else { + if (Is_Something_Attached()) { + if (IsALoaner) { + if (Team) { + mission = MISSION_GUARD; + } else { + mission = MISSION_UNLOAD; + Assign_Destination(Good_LZ()); + } + } else { + Assign_Destination(Good_LZ()); + mission = MISSION_MOVE; + } + } else { + + /* + ** If this transport is a loaner and part of a team, then remove it from + ** the team it is attached to. + */ + if (IsALoaner) { + if (Team) { + Team->Remove(this); + } + } + + if (Class->Primary != WEAPON_NONE) { + + /* + ** Weapon equipped helicopters that run out of ammo and were + ** brought in as reinforcements will leave the map. + */ + if (Ammo == 0 && !House->IsHuman && IsALoaner) { + mission = MISSION_RETREAT; + } else { + + /* + ** Normal aircraft try to find a good landing spot to rest. + */ + BuildingClass * building = Find_Docking_Bay(STRUCT_HELIPAD, false); + Assign_Destination(TARGET_NONE); + if (building && Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) { + mission = MISSION_ENTER; + } else { + Assign_Destination(Good_LZ()); + mission = MISSION_MOVE; + } + } + } else { + if (Team) return; + + Assign_Destination(Good_LZ()); + mission = MISSION_MOVE; + } + } + } + Assign_Mission(mission); + Commence(); +} + + +/*********************************************************************************************** + * AircraftClass::Process_Fly_To -- Handles state machine for flying to destination. * + * * + * This support routine is used when the helicopter is to fly to the destination. It can * + * optionally slow the helicopter down as it approaches the destination. * + * * + * INPUT: slowdown -- Should the aircraft be slowed down when it approaches the dest? * + * * + * OUTPUT: Returns with the distance remaining between the aircraft and the destination. * + * * + * WARNINGS: Because the aircraft can be move at a fast speed, the distance to target value * + * will probably never be zero. The likely case will be that the aircraft * + * overshoots the target. * + * * + * HISTORY: * + * 06/14/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Process_Fly_To(bool slowdown) +{ + Validate(); + COORDINATE coord; + if (Is_Target_Building(NavCom)) { + coord = As_Building(NavCom)->Docking_Coord(); + } else { + coord = As_Coord(NavCom); + } + int distance = Distance(coord); + + PrimaryFacing.Set_Desired(Direction(coord)); + + if (slowdown) { + int speed = MIN(distance, 0x0300); + speed = Bound(speed/3, 0x0020, 0x00FF); + if (Speed != speed) { + Set_Speed(speed); + } + } + + if (distance < 0x0010) { + if (slowdown) { + Set_Speed(0); + } + distance = 0; + } + return(distance); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * AircraftClass::Debug_Dump -- Displays the status of the aircraft to the mono monitor. * + * * + * This displays the current status of the aircraft class to the mono monitor. By this * + * display bugs may be tracked down or prevented. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Debug_Dump(MonoClass *mono) const +{ + Validate(); + mono->Set_Cursor(0, 0); + mono->Print( + "ÚName:ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂMission:ÄÄÄÂTarCom:ÂNavCom:ÂRadio:ÂCoord:ÄÄÂAltitudeÂSt:Ä¿\n" + "³ ³ ³ ³ ³ ³ ³ ³ ³\n" + "ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂNÂYÂHealth:ÄÂFdir:ÂÄBdir:ÄÂSpeed:ÂÄÄÄÄÄÁÄÄÄÄÄÄÂCargo:ÄÄÄÄÁÄÄÄÄ´\n" + "³Active........³ ³ ³ ³ ³ ³ ³ ³ ³\n" + "³Limbo.........³ ³ ÃÄÄÄÄÄÄÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n" + "³Owned.........³ ³ ³Last Message: ³\n" + "³Discovered....³ ³ ÃTimer:ÂArm:ÂÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÂFlash:ÂStage:ÂTeam:ÄÄÄÄÂArch:´\n" + "³Selected......³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³\n" + "³Teathered.....³ ³ ÃÄÄÄÄÄÄÁÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÙ\n" + "³Locked on Map.³ ³ ³ \n" + "³ ³ ³ ³ \n" + "³Is A Loaner...³ ³ ³ \n" + "³Is Landing....³ ³ ³ \n" + "³Is Taking Off.³ ³ ³ \n" + "³ ³ ³ ³ \n" + "³ ³ ³ ³ \n" + "³ ³ ³ ³ \n" + "³Recoiling.....³ ³ ³ \n" + "³To Display....³ ³ ³ \n" + "ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÁÄÙ \n"); + mono->Set_Cursor(1, 1);mono->Printf("%s:%s", House->Class->IniName, Class->IniName); + mono->Set_Cursor(36, 3);mono->Printf("%02X:%02X", SecondaryFacing.Current(), SecondaryFacing.Desired()); + mono->Set_Cursor(42, 1);mono->Printf("%04X", NavCom); + mono->Set_Cursor(66, 1);mono->Printf("%d", Altitude); + mono->Set_Cursor(44, 3);mono->Printf("%d", Get_Speed()); + mono->Text_Print("X", 16 + (IsLanding?2:0), 12); + mono->Text_Print("X", 16 + (IsTakingOff?2:0), 13); + FootClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * AircraftClass::Active_Click_With -- Handles clicking over specified object. * + * * + * This routine is used when the player clicks over the speicifed object. It will assign * + * the appropriate mission to the aircraft. * + * * + * INPUT: action -- The action that was nominally determined by the What_Action function. * + * * + * object -- The object over which the mouse was clicked. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will alter the game sequence and causes an event packet to be * + * propogated to all connected machines. * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + Validate(); + switch (action) { + case ACTION_ENTER: + Player_Assign_Mission(MISSION_ENTER, TARGET_NONE, object->As_Target()); + break; + + case ACTION_SELF: + Player_Assign_Mission(MISSION_UNLOAD, TARGET_NONE, TARGET_NONE); + break; + + default: + break; + } + FootClass::Active_Click_With(action, object); +} + + +/*********************************************************************************************** + * AircraftClass::Active_Click_With -- Handles clicking over specified cell. * + * * + * This routine is used when the player clicks the mouse of the specified cell. It will * + * assign the appropriate mission to the aircraft. * + * * + * INPUT: action -- The action nominally determined by What_Action(). * + * * + * cell -- The cell over which the mouse was clicked. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will affect the game sequence and causes an event object to be * + * propogated to all connected machines. * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Active_Click_With(ActionType action, CELL cell) +{ + Validate(); +#ifdef NEVER + switch (action) { + case ACTION_MOVE: + if (Map[cell].IsVisible) { + Player_Assign_Mission(MISSION_MOVE, TARGET_NONE, ::As_Target(cell)); + } + break; + + case ACTION_NOMOVE: + break; + + case ACTION_ATTACK: + Player_Assign_Mission(MISSION_ATTACK, ::As_Target(cell)); + break; + } +#endif + FootClass::Active_Click_With(action, cell); +} + + +/*********************************************************************************************** + * AircraftClass::Player_Assign_Mission -- Handles player input to assign a mission. * + * * + * This routine is called as a result of player input with the intent to change the * + * mission of the aircraft. * + * * + * INPUT: mission -- The mission requested of the aircraft. * + * * + * target -- The value to assign to the aircraft's targeting computer. * + * * + * dest. -- The value to assign to the aircraft's navigation computer. * + * * + * OUTPUT: none * + * * + * WARNINGS: The mission specified will be executed at an indeterminate future game frame. * + * This is controlled by net/modem propogation delay. * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Player_Assign_Mission(MissionType mission, TARGET target, TARGET destination) +{ + Validate(); + if (AllowVoice) { + if (mission == MISSION_ATTACK) { + Response_Attack(); + } else { + Response_Move(); + } + } + Queue_Mission(As_Target(), mission, target, destination); +} + + +/*********************************************************************************************** + * AircraftClass::What_Action -- Determines what action to perform. * + * * + * This routine is used to determine what action will likely be performed if the mouse * + * were clicked over the object specified. The display system calls this routine to * + * control the mouse shape. * + * * + * INPUT: target -- Pointer to the object that the mouse is currently over. * + * * + * OUTPUT: Returns with the action that will occur if the mouse were clicked over the * + * object specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +ActionType AircraftClass::What_Action(ObjectClass * target) const +{ + Validate(); + ActionType action = FootClass::What_Action(target); + + if (action == ACTION_SELF && !How_Many()) { + action = ACTION_NONE; + } + + if (action == ACTION_ATTACK && Class->Primary == WEAPON_NONE) { + action = ACTION_NONE; + } + + if (IsOwnedByPlayer && House->Is_Ally(target) && target->What_Am_I() == RTTI_BUILDING && ((AircraftClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)target) == RADIO_ROGER) { + action = ACTION_ENTER; + } + return(action); +} + + +/*********************************************************************************************** + * AircraftClass::What_Action -- Determines what action to perform. * + * * + * This routine will determine what action would occur if the mouse were clicked over the * + * cell specified. The display system calls this routine to determine what mouse shape * + * to use. * + * * + * INPUT: cell -- The cell over which the mouse is currently positioned. * + * * + * OUTPUT: Returns with the action that will be performed if the mouse were clicked at the * + * specified cell location. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +ActionType AircraftClass::What_Action(CELL cell) const +{ + Validate(); + ActionType action = FootClass::What_Action(cell); + + if (action == ACTION_MOVE && GameToPlay == GAME_NORMAL && !Map[cell].IsVisible) { + action = ACTION_NOMOVE; + } + + if (action == ACTION_ATTACK && Class->Primary == WEAPON_NONE) { + action = ACTION_NONE; + } + return(action); +} + + +/*********************************************************************************************** + * AircraftClass::Pose_Dir -- Fetches the natural landing facing. * + * * + * Use this routine to get the desired facing the aircraft should assume when landing. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the normal default facing the aircraft should have when landed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +DirType AircraftClass::Pose_Dir(void) const +{ + Validate(); + if (*this == AIRCRAFT_TRANSPORT) { + return(DIR_N); + } + return(DIR_NE); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Attack -- Handles the attack mission for aircraft. * + * * + * This routine is the state machine that handles the attack mission for aircraft. It will * + * handling homing in on and firing on the target in the aircraft's targeting computer. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game ticks to pass before this routine must be called * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Mission_Attack(void) +{ + Validate(); + if (Class->IsFixedWing) { + Assign_Mission(MISSION_HUNT); + return(1); + } + + enum { + VALIDATE_AZ, + PICK_ATTACK_LOCATION, + TAKE_OFF, + FLY_TO_POSITION, + FIRE_AT_TARGET, + FIRE_AT_TARGET2, + RETURN_TO_BASE + }; + switch (Status) { + + /* + ** Double check target and validate the attack zone. + */ + case VALIDATE_AZ: + if (!Target_Legal(TarCom)) { + Status = RETURN_TO_BASE; + } else { + Status = PICK_ATTACK_LOCATION; + } + break; + + /* + ** Pick a good location to attack from. + */ + case PICK_ATTACK_LOCATION: + if (!Target_Legal(TarCom)) { + Status = RETURN_TO_BASE; + } else { + Assign_Destination(Good_Fire_Location(TarCom)); + if (Target_Legal(NavCom)) { + Status = TAKE_OFF; + } else { + Status = RETURN_TO_BASE; + } + } + break; + + /* + ** Take off (if necessary). + */ + case TAKE_OFF: + if (!Target_Legal(TarCom)) { + Status = RETURN_TO_BASE; + } else { + if (Process_Take_Off()) { + Status = FLY_TO_POSITION; + + /* + ** Break off radio contact with the helipad it is taking off from. + */ + if (In_Radio_Contact() && Map[Coord_Cell(Coord)].Cell_Building() == Contact_With_Whom()) { + Transmit_Message(RADIO_OVER_OUT); + } + + /* + ** Start flying toward the destination by skewing at first. + ** As the flight progresses, the body will rotate to face + ** the direction of travel. + */ + int diff = SecondaryFacing.Difference(Direction(NavCom)); + diff = Bound(diff, -128, 128); + PrimaryFacing = SecondaryFacing.Current()+diff; + } + return(1); + } + break; + + /* + ** Fly to attack location. + */ + case FLY_TO_POSITION: + if (Target_Legal(TarCom)) { + + /* + ** If the navcom was cleared mysteriously, then try to pick + ** a new attack location. This is a likely event if the player + ** clicks on a new target while in flight to an existing target. + */ + if (!Target_Legal(NavCom)) { + Status = PICK_ATTACK_LOCATION; + return(1); + } + + int distance = Process_Fly_To(true); + + if (distance < 0x0200) { + SecondaryFacing.Set_Desired(Direction(TarCom)); + + if (distance < 0x0010) { + Status = FIRE_AT_TARGET; + Assign_Destination(TARGET_NONE); + } + } else { + SecondaryFacing.Set_Desired(::Direction(Fire_Coord(0), As_Coord(NavCom))); + return(1); + } + } else { + Status = RETURN_TO_BASE; + } + return(1); + + /* + ** Fire at the target. + */ + case FIRE_AT_TARGET: + if (!Target_Legal(TarCom)) { + Status = RETURN_TO_BASE; + return(1); + } + + PrimaryFacing.Set_Desired(Direction(TarCom)); + SecondaryFacing.Set_Desired(Direction(TarCom)); + switch (Can_Fire(TarCom, 0)) { + case FIRE_CLOAKED: + Do_Uncloak(); + break; + + case FIRE_OK: + Fire_At(TarCom, 0); + Map[::As_Cell(TarCom)].Incoming(Coord, true); + Status = FIRE_AT_TARGET2; + break; + + default: + if (!Ammo) { + Status = RETURN_TO_BASE; + } else { + Status = FIRE_AT_TARGET2; + } + break; + } + return(1); + + /* + ** Fire at the target. + */ + case FIRE_AT_TARGET2: + if (!Target_Legal(TarCom)) { + Status = RETURN_TO_BASE; + return(1); + } + + PrimaryFacing.Set_Desired(Direction(TarCom)); + SecondaryFacing.Set_Desired(Direction(TarCom)); + switch (Can_Fire(TarCom, 0)) { + case FIRE_CLOAKED: + Do_Uncloak(); + break; + + case FIRE_REARM: + break; + + case FIRE_OK: + Fire_At(TarCom, 0); + Map[::As_Cell(TarCom)].Incoming(Coord, true); + + if (Ammo) { + Status = PICK_ATTACK_LOCATION; + } else { + Status = RETURN_TO_BASE; + } + break; + + default: + if (!Ammo) { + Status = RETURN_TO_BASE; + } else { + Status = PICK_ATTACK_LOCATION; + } + break; + } + break; + + /* + ** Fly back to landing spot. + */ + case RETURN_TO_BASE: + Assign_Destination(TARGET_NONE); + Enter_Idle_Mode(); + break; + } + + return(TICKS_PER_SECOND/2); +} + + +/*********************************************************************************************** + * AircraftClass::New_LZ -- Find a good landing zone. * + * * + * Use this routine to locate a good landing zone that is nearby the location specified. * + * By using this routine it is possible to assign the same landing zone to several * + * aircraft and they will land nearby without conflict. * + * * + * INPUT: oldlz -- Target value of desired landing zone (usually a cell target value). * + * * + * OUTPUT: Returns with the new good landing zone. It might be the same value passed in. * + * * + * WARNINGS: The landing zone might be a goodly distance away from the ideal if there is * + * extensive blocking terrain in the vicinity. * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +TARGET AircraftClass::New_LZ(TARGET oldlz) const +{ + Validate(); + if (Target_Legal(oldlz) && (!Is_LZ_Clear(oldlz) || !Cell_Seems_Ok(As_Cell(oldlz)))) { + COORDINATE coord = As_Coord(oldlz); + + /* + ** Scan outward in a series of concentric rings up to certain distance + ** in cells. + */ + for (int radius = 0; radius < 16; radius++) { + FacingType modifier = Random_Pick(FACING_N, FACING_NW); + CELL lastcell = -1; + + /* + ** Perform a radius scan out from the original center location. Try to + ** find a cell that is allowed to be a legal LZ. + */ + for (FacingType facing = FACING_N; facing < FACING_COUNT; facing++) { + CELL newcell = Coord_Cell(Coord_Move(coord, Facing_Dir(facing+modifier), radius * ICON_LEPTON_W)); + if (Map.In_Radar(newcell)) { + TARGET newtarget = ::As_Target(newcell); + + if (newcell != lastcell && Is_LZ_Clear(newtarget) && Cell_Seems_Ok(newcell)) { + return(newtarget); + } + lastcell = newcell; + } + } + } + } + return(oldlz); +} + + +/*********************************************************************************************** + * AircraftClass::Fire_Coord -- Calculates the point of origin for a bullet. * + * * + * This routine is used to find the exact coordinate where the bullet should appear if * + * fired from this object. * + * * + * INPUT: which -- Which weapon to consider. * + * * + * OUTPUT: Returns with the coordinate of where the projectile will appear if fired. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/15/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE AircraftClass::Fire_Coord(int ) const +{ + Validate(); + return(Coord_Move(Coord_Add(XYP_Coord(0, -Altitude), Coord), SecondaryFacing, 0x040)); +} + + +COORDINATE AircraftClass::Target_Coord(void) const +{ + Validate(); + return(Coord_Add(XYP_Coord(0, -Altitude), Coord)); +} + + +/*********************************************************************************************** + * AircraftClass::Receive_Message -- Handles receipt of radio messages. * + * * + * This routine receives all radio messages directed at this aircraft. It is used to handle * + * all inter-object coordination. Typically, this would be for transport helicopters and * + * other complex landing operations required of helicopters. * + * * + * INPUT: from -- The source of this radio message. * + * * + * message -- The message itself. * + * * + * param -- An optional parameter that may be used to transfer additional * + * data. * + * * + * OUTPUT: Returns with the radio response from the aircraft. * + * * + * WARNINGS: Some radio messages are handled by the base classes. * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +RadioMessageType AircraftClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + Validate(); + switch (message) { + + case RADIO_PREPARED: + if (Target_Legal(TarCom)) return(RADIO_NEGATIVE); + if ((Altitude == 0 && Ammo == Class->MaxAmmo) || (Altitude > 0 && Ammo > 0)) return(RADIO_ROGER); + return(RADIO_NEGATIVE); + + /* + ** Something disasterous has happened to the object in contact with. Fall back + ** and regroup. This means that any landing process is immediately aborted. + */ + case RADIO_RUN_AWAY: + if (IsLanding) { + IsLanding = false; + IsTakingOff = true; + } + Scatter(0, true); + break; + + /* + ** The ground control requests that this specified landing spot be used. + */ + case RADIO_MOVE_HERE: + FootClass::Receive_Message(from, message, param); + if (Is_Target_Building(param)) { + if (Transmit_Message(RADIO_CAN_LOAD, As_Techno(param)) != RADIO_ROGER) { + return(RADIO_NEGATIVE); + } + Assign_Mission(MISSION_ENTER); + Assign_Destination((TARGET)param); + } else { + Assign_Mission(MISSION_MOVE); + Assign_Destination((TARGET)param); + } + Commence(); + return(RADIO_ROGER); + + /* + ** Ground control is requesting if the aircraft requires navigation direction. + */ + case RADIO_NEED_TO_MOVE: + FootClass::Receive_Message(from, message, param); + if (!Target_Legal(NavCom) && !IsTakingOff && !IsLanding) { + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + + /* + ** This message is sent by the passenger when it determines that it has + ** entered the transport. + */ + case RADIO_IM_IN: + if (How_Many() == Class->Max_Passengers()) { + Close_Door(5, 4); + } + + /* + ** If a civilian has entered the transport, then the transport will immediately + ** fly off the map. + */ + if (from->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)from)->Class->IsCivilian && !((InfantryClass *)from)->IsTechnician) { + Assign_Mission(MISSION_RETREAT); + } + return(RADIO_ATTACH); + + /* + ** Docking maintenance message received. Check to see if new orders should be given + ** to the impatient unit. + */ + case RADIO_DOCKING: + if (Class->IsTransporter && How_Many() < Class->Max_Passengers()) { + FootClass::Receive_Message(from, message, param); + + if (!IsTethered && !IsLanding && !IsTakingOff && Altitude == 0) { + + Open_Door(5, 4); + + /* + ** If the potential passenger needs someplace to go, then figure out a good + ** spot and tell it to go. + */ + if (Transmit_Message(RADIO_NEED_TO_MOVE, from) == RADIO_ROGER) { + CELL cell; + DirType dir = Desired_Load_Dir(from, cell); + + /* + ** If no adjacent free cells are detected, then passenger loading + ** cannot occur. Break radio contact. + */ + if (cell == 0) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + param = (long)::As_Target(cell); + + /* + ** Tell the potential passenger where it should go. If the passenger is + ** already at the staging location, then tell it to move onto the transport + ** directly. + */ + if (Transmit_Message(RADIO_MOVE_HERE, param, from) == RADIO_YEA_NOW_WHAT) { + param = (long)As_Target(); + Transmit_Message(RADIO_TETHER); + if (Transmit_Message(RADIO_MOVE_HERE, param, from) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + Contact_With_Whom()->Unselect(); + } + } + } + } + } + return(RADIO_ROGER); + } + break; + + /* + ** Asks if the passenger can load on this transport. + */ + case RADIO_CAN_LOAD: + if (!In_Radio_Contact() && Class->IsTransporter && How_Many() < Class->Max_Passengers() && from && House->Class->House == from->Owner()) { + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + } + + /* + ** Let the base class take over processing this message. + */ + return(FootClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * AircraftClass::Desired_Load_Dir -- Determines where passengers should line up. * + * * + * This routine is used by the transport helicopter to determine the location where the * + * infantry passengers should line up before loading. * + * * + * INPUT: object -- The object that is trying to load up on this transport. * + * * + * -- Reference to the cell that the passengers should move to before the * + * actual load process may begin. * + * * + * OUTPUT: Returns with the direction that the helicopter should face for the load operation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + * 07/30/1995 JLB : Revamped to scan all adjacent cells. * + *=============================================================================================*/ +DirType AircraftClass::Desired_Load_Dir(ObjectClass * object, CELL & moveto) const +{ + Validate(); + CELL center = Coord_Cell(Center_Coord()); + for (int sweep = FACING_N; sweep < FACING_S; sweep++) { + moveto = Adjacent_Cell(center, FACING_S+sweep); + if (Map.In_Radar(moveto) && (Coord_Cell(object->Center_Coord()) == moveto || Map[moveto].Is_Generally_Clear())) return(DIR_N); + + moveto = Adjacent_Cell(center, FACING_S-sweep); + if (Map.In_Radar(moveto) && (Coord_Cell(object->Center_Coord()) == moveto || Map[moveto].Is_Generally_Clear())) return(DIR_N); + } + return(DIR_N); +} + + +/*********************************************************************************************** + * AircraftClass::Process_Take_Off -- State machine support for taking off. * + * * + * This routine is used by the main game state machine processor. This utility routine * + * handles a helicopter as it transitions from landed to flying state. * + * * + * INPUT: none * + * * + * OUTPUT: Has the helicopter reached flight level now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Process_Take_Off(void) +{ + Validate(); + IsLanding = false; + IsTakingOff = true; + switch (Altitude) { + case 0: + Close_Door(5, 4); + PrimaryFacing = SecondaryFacing; + break; + + case FLIGHT_LEVEL/2: + PrimaryFacing.Set_Desired(Direction(NavCom)); + break; + + case FLIGHT_LEVEL-(FLIGHT_LEVEL/3): + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + Set_Speed(0x20); + break; + + case FLIGHT_LEVEL-(FLIGHT_LEVEL/5): + Set_Speed(0x40); + break; + + case FLIGHT_LEVEL: + Set_Speed(0xFF); + IsTakingOff = false; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Process_Landing -- Landing process state machine handler. * + * * + * This is a support routine that is called by the main state machine routines. This * + * routine is responsible for handling the helicopter as it transitions from flight to * + * landing. * + * * + * INPUT: none * + * * + * OUTPUT: Has the helicopter completely landed now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Process_Landing(void) +{ + Validate(); + IsTakingOff = false; + IsLanding = true; + switch (Altitude) { + case 0: + IsLanding = false; + return(true); + + case FLIGHT_LEVEL/2: + Set_Speed(0); + break; + + case FLIGHT_LEVEL: + break; + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Can_Enter_Cell -- Determines if the aircraft can land at this location. * + * * + * This routine is used when the passability of a cell needs to be determined. This is * + * necessary when scanning for a location that the aircraft can land. * + * * + * INPUT: cell -- The cell location to check for landing. * + * * + * OUTPUT: Returns a value indicating if the cell is a legal landing spot or not. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + *=============================================================================================*/ +MoveType AircraftClass::Can_Enter_Cell(CELL cell, FacingType ) const +{ + Validate(); + if (!Map.In_Radar(cell)) return(MOVE_NO); + + CellClass * cellptr = &Map[cell]; + + if (!cellptr->Cell_Occupier() || + !cellptr->Cell_Occupier()->Is_Techno() || + ((TechnoClass *)cellptr->Cell_Occupier())->House->Is_Ally(House) || + ((TechnoClass *)cellptr->Cell_Occupier())->Cloak != CLOAKED) { + + if (!cellptr->Is_Generally_Clear()) return(MOVE_NO); + } + + if (GameToPlay == GAME_NORMAL && IsOwnedByPlayer && !cellptr->IsVisible) return(MOVE_NO); + + return(MOVE_OK); +} + + +/*********************************************************************************************** + * AircraftClass::Good_Fire_Location -- Searches for and finds a good spot to fire from. * + * * + * Given the specified target, this routine will locate a good spot for the aircraft to * + * fire at the target. * + * * + * INPUT: target -- The target that is desired to be attacked. * + * * + * OUTPUT: Returns with the target location of the place that firing should be made from. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + * 06/14/1995 JLB : Finer resolution on ring scan. * + *=============================================================================================*/ +TARGET AircraftClass::Good_Fire_Location(TARGET target) const +{ + Validate(); + if (Target_Legal(target)) { + int range = Weapon_Range(0); + COORDINATE tcoord = As_Coord(target); + CELL bestcell = 0; + CELL best2cell = 0; + int bestval = -1; + int best2val = -1; + + for (int r = range-0x0180; r > 0x0180; r -= 0x0100) { + for (int face = 0; face < 255; face += 16) { + COORDINATE newcoord = Coord_Move(tcoord, (DirType)face, r); + CELL newcell = Coord_Cell(newcoord); + + if (Map.In_Radar(newcell) && (GameToPlay != GAME_NORMAL || Map[newcell].IsVisible) && Cell_Seems_Ok(newcell, true)) { + int dist = Distance(newcoord); + if (bestval == -1 || dist < bestval) { + best2val = bestval; + best2cell = bestcell; + bestval = dist; + bestcell = newcell; + } + } + } + if (bestval != -1) break; + } + + if (best2val == -1) { + best2cell = bestcell; + } + + /* + ** If it found a good firing location, then return this location as + ** a target value. + */ + if (bestval != -1) { + if (Random_Pick(0, 1) == 0) { + return(::As_Target(bestcell)); + } else { + return(::As_Target(best2cell)); + } + } + } + return(TARGET_NONE); +} + + +/*********************************************************************************************** + * AircraftClass::Cell_Seems_Ok -- Checks to see if a cell is good to enter. * + * * + * This routine examines the navigation computers of other aircraft in order to see if the * + * specified cell is safe to fly to. The intent of this routine is to avoid unneccessary * + * mid-air collisions. * + * * + * INPUT: cell -- The cell to examine for clear airspace. * + * * + * strict -- Should the scan consider the aircraft, that is making this check, a * + * blocking aircraft. Typically, the aircraft itself is not considered * + * a blockage -- an aircraft can always exist where it is currently * + * located. A strict check is useful for helicopters that need to move * + * around at the slightest provocation. * + * * + * OUTPUT: Is the specified cell free from airspace conflicts? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Cell_Seems_Ok(CELL cell, bool strict) const +{ + Validate(); + /* + ** Make sure that no other aircraft are heading to the selected location. If they + ** are, then don't consider the location as valid. + */ + TARGET astarget = ::As_Target(cell); + bool ok = true; + for (int index = 0; index < Aircraft.Count(); index++) { + AircraftClass * air = Aircraft.Ptr(index); + if (air && (strict || air != this) && !air->IsInLimbo) { + if (Coord_Cell(air->Coord) == cell || air->NavCom == astarget) { + return(false); + } + } + } + return(true); +} + + +/*********************************************************************************************** + * AircraftClass::Pip_Count -- Returns the number of "objects" in aircraft. * + * * + * This routine is used by the render logic to draw the little container "pips". This * + * corresponds to the number of passengers for a transport helicopter or the number of * + * shots remaining for an attack helicopter. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of "pips" to render on the aircraft. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/11/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Pip_Count(void) const +{ + Validate(); + int retval = 0; + + if (Class->IsTransporter) { + retval = How_Many(); + } else { + if (Ammo) { + retval = Cardinal_To_Fixed(Class->MaxAmmo, Ammo); + retval = Fixed_To_Cardinal(Class->Max_Pips(), retval); + if (!retval) retval = 1; + } + } + return(retval); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Enter -- Control aircraft to fly to the helipad or repair center. * + * * + * This routine is used when the aircraft needs to fly for either rearming or repairing. * + * It tries to establish contact with the support building. Once contact is established * + * the ground controller takes care of commanding the aircraft. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before this routine should be called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + * 07/04/1995 JLB : Ground controller gives orders. * + *=============================================================================================*/ +int AircraftClass::Mission_Enter(void) +{ + Validate(); + enum { + INITIAL, + TAKEOFF, + ALTITUDE, + TRAVEL, + LANDING + }; + switch (Status) { + case INITIAL: + if (Altitude < FLIGHT_LEVEL || IsLanding) { + Status = TAKEOFF; + } else { + Status = ALTITUDE; + } + break; + + case TAKEOFF: + if (Process_Take_Off()) { + /* + ** After takeoff is complete, break radio contact with any helipad that this + ** helicopter is taking off from. + */ + if (In_Radio_Contact() && Map[Coord_Cell(Coord)].Cell_Building() == Contact_With_Whom()) { + Transmit_Message(RADIO_OVER_OUT); + } + Status = ALTITUDE; + } + break; + + case ALTITUDE: + /* + ** Establish radio contact with the building this helicopter is trying + ** to land at. + */ + if (In_Radio_Contact()) { + Status = TRAVEL; + } else { + TechnoClass * tech = As_Techno(NavCom); + if (tech && Transmit_Message(RADIO_CAN_LOAD, tech) == RADIO_ROGER) { + Transmit_Message(RADIO_HELLO, tech); + Transmit_Message(RADIO_DOCKING); + Status = TRAVEL; + } else { + Assign_Destination(TARGET_NONE); + Enter_Idle_Mode(); + } + } + break; + + case TRAVEL: + Transmit_Message(RADIO_DOCKING); + if (!In_Radio_Contact()) { + Assign_Destination(TARGET_NONE); + Enter_Idle_Mode(); + } else { + int distance = Process_Fly_To(true); + + if (distance < 0x0080) { + if (Target_Legal(TarCom)) { + SecondaryFacing.Set_Desired(Direction(TarCom)); + } else { + SecondaryFacing.Set_Desired(Pose_Dir()); + } + + if (distance < 0x0010) { + Status = LANDING; + } + break; + } else { + SecondaryFacing.Set_Desired(::Direction(Fire_Coord(0), As_Coord(NavCom))); + } + return(3); + } + break; + + case LANDING: + if (IsTakingOff) { + Assign_Destination(TARGET_NONE); + Enter_Idle_Mode(); + } + if (Process_Landing()) { + switch (Transmit_Message(RADIO_IM_IN)) { + case RADIO_ROGER: + Assign_Mission(MISSION_GUARD); + break; + + case RADIO_ATTACH: + Limbo(); + Contact_With_Whom()->Attach(this); + break; + + default: + Enter_Idle_Mode(); + } + } + break; + } + return(1); +} + + +/*********************************************************************************************** + * AircraftClass::Good_LZ -- Locates a good spot ot land. * + * * + * This routine is used when helicopters need a place to land, but there are no obvious * + * spots (i.e., helipad) available. It will try to land near a friendly helipad or friendly * + * building if there are no helipads anywhere. In the event that there are no friendly * + * buildings anywhere on the map, then just land right where it is flying. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the target location where this aircraft should land. This value may * + * not be a clear cell, but the normal landing logic will resolve that problem. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + *=============================================================================================*/ +TARGET AircraftClass::Good_LZ(void) const +{ + Validate(); + /* + ** Scan through all of the buildings and try to land near + ** the helipad (if there is one) or the nearest friendly building. + */ + CELL bestcell; + int bestdist = -1; + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + if (building && !building->IsInLimbo && building->House == House) { + int dist = Distance(building); + if (*building == STRUCT_HELIPAD) { + dist /= 4; + } + if (bestdist == -1 || dist < bestdist) { + bestdist = dist; + bestcell = Coord_Cell(building->Center_Coord()); + } + } + } + + /* + ** Return with the suitable location if one was found. + */ + if (bestdist != -1) { + return(::As_Target(bestcell)); + } + + /* + ** No good location was found. Just try to land here. + */ + return(::As_Target(Coord_Cell(Coord))); +} + + +/*********************************************************************************************** + * AircraftClass::Set_Speed -- Sets the speed for the aircraft. * + * * + * This routine will set the speed for the aircraft. The speed is specified as a fraction * + * of full speed. * + * * + * INPUT: speed -- The fixed point fractional speed setting. 0x00 is stopped, 0xFF is full * + * speed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Set_Speed(int speed) +{ + Validate(); + Fly_Speed(speed, Class->MaxSpeed); +} + + +/*********************************************************************************************** + * AircraftClass::Fire_Direction -- Determines the direction of fire. * + * * + * This routine will determine what direction a projectile would take if it were fired * + * from the aircraft. This is the direction that the aircraft's body is facing. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the direction of projectile fire. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +DirType AircraftClass::Fire_Direction(void) const +{ + Validate(); + return(SecondaryFacing.Current()); +} + + +/*********************************************************************************************** + * AircraftClass::~AircraftClass -- Destructor for aircraft object. * + * * + * This is the destructor for aircraft. It will limbo the aircraft if it isn't already * + * and also removes the aircraft from any team it may be attached to. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 JLB : Created. * + *=============================================================================================*/ +AircraftClass::~AircraftClass(void) +{ + if (GameActive && Class) { + /* + ** If there are any cargo members, delete them. + */ + while (Is_Something_Attached()) { + delete Detach_Object(); + } + + Limbo(); + } + + if (GameActive && Class && Team) Team->Remove(this); +} + + +/*********************************************************************************************** + * AircraftClass::Scatter -- Causes the aircraft to move away a bit. * + * * + * This routine will cause the aircraft to move away from its current location and then * + * enter some idle mode. Typically this is called when the aircraft is attacked while on * + * the ground. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Scatter(COORDINATE , bool ) +{ + Validate(); + if (IsLanding || Altitude == 0) { + IsLanding = false; + IsTakingOff = true; + } + Enter_Idle_Mode(); +} + + +/*********************************************************************************************** + * AircraftClass::Rearm_Delay -- Returns the delay between shots for this aircraft. * + * * + * Aircraft have a faster rearm delay than their weapon would otherwise indicate. This is * + * necessary to give helicopters a combat edge while still allowing them to share the * + * weapon types used by ground units. * + * * + * INPUT: second -- Is this for the second shot? The second shot uses the full rearm * + * delay. The first shot, if part of a two shot weapon, is given an * + * abbreviated rearm time. * + * * + * OUTPUT: Returns with the game frames to delay before the next shot can fire. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Rearm_Delay(bool second) const +{ + Validate(); + return(FootClass::Rearm_Delay(second)/2); +} + + +/*********************************************************************************************** + * AircraftClass::Threat_Range -- Returns with a range to scan for targets. * + * * + * This routine returns with the distance to scan for targets according to the type of * + * search requested. The search type is typically the weapon (or short) range and vicinity * + * distances. This is used by Guard and Guard Area missions. Aircraft never consider their * + * weapon range in this determination since they can fly so fast, weapon range is * + * meaningless. * + * * + * INPUT: control -- The range control parameter; * + * -1 = range doesn't matter -- return -1 for compatability reasons. * + * 0 = short range scan * + * 1 = long range scan * + * * + * OUTPUT: Returns with the lepton distance to use for the scan operation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Threat_Range(int control) const +{ + Validate(); + if (control == -1) return(-1); + + int range = 20 * ICON_LEPTON_W; + if (control == 1) { + range *= 2; + } + return(range); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Guard -- Handles aircraft in guard mode. * + * * + * Aircraft don't like to be in guard mode if in flight. If this situation is detected, * + * then figure out what the aircraft should be doing and go do it. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: This routine typically calls the normal guard logic for ground units. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Mission_Guard(void) +{ + Validate(); + if (Altitude == FLIGHT_LEVEL) { + + /* + ** If part of a team, then do nothing, since the team + ** handler will take care of giving this aircraft a + ** mission. + */ + if (Team) { + if (Target_Legal(NavCom)) { + Assign_Mission(MISSION_MOVE); + } + return(TICKS_PER_SECOND); + } + + if (Class->Primary == WEAPON_NONE) { + Assign_Destination(::As_Target(Coord_Cell(Coord))); + Assign_Mission(MISSION_MOVE); + } else { + Enter_Idle_Mode(); + } + return(1); + } + if (House->IsHuman) return(TICKS_PER_SECOND); + + /* + ** Special case to force the GDI helicopter to be brain dead in the Nod + ** mission where it is supposed to be captured. + */ + if (GameToPlay == GAME_NORMAL && Scenario == 7 && House->Class->House == HOUSE_GOOD) { + return(TICKS_PER_SECOND*20); + } + + /* + ** If the aircraft is very badly damaged, then it will search for a + ** repair bay first. + */ + if (House->Available_Money() >= 100 && Health_Ratio() <= 0x0080) { + if (!In_Radio_Contact() || + (Altitude == 0 && + (Contact_With_Whom()->What_Am_I() != RTTI_BUILDING || *((BuildingClass *)Contact_With_Whom()) != STRUCT_REPAIR))) { + + + BuildingClass * building = Find_Docking_Bay(STRUCT_REPAIR, true); + if (building) { + Assign_Destination(building->As_Target()); + Assign_Target(TARGET_NONE); + Assign_Mission(MISSION_ENTER); + return(1); + } + } + } + + /* + ** If the aircraft cannot attack anything because of lack of ammo, + ** abort any normal guard logic in order to look for a helipad + ** to rearm. + */ + if (Ammo == 0 && Class->Primary != WEAPON_NONE) { + if (!In_Radio_Contact()) { + BuildingClass * building = Find_Docking_Bay(STRUCT_HELIPAD, false); + if (building) { + Assign_Destination(building->As_Target()); + Assign_Target(TARGET_NONE); + Assign_Mission(MISSION_ENTER); + return(1); + } + } +// return(TICKS_PER_SECOND*3); + } + + /* + ** If the aircraft already has a target, then attack it if possible. + */ + if (Target_Legal(TarCom)) { + Assign_Mission(MISSION_ATTACK); + return(1); + } + + /* + ** Transport helicopters don't really do anything but just sit there. + */ + if (Class->Primary == WEAPON_NONE) { + return(TICKS_PER_SECOND*3); + } + + /* + ** Computer controlled helicopters will defend themselves by bouncing around + ** and looking for a free helipad. + */ + if (Altitude == 0 && !In_Radio_Contact()) { + Scatter(0, true); + return(TICKS_PER_SECOND*3); + } + + return(FootClass::Mission_Guard()); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Guard_Area -- Handles the aircraft guard area logic. * + * * + * This routine handles area guard logic for aircraft. Aircraft require special handling * + * for this mode since they are to guard area only if they are in a position to do so. * + * Otherwise they just defend themselves. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Mission_Guard_Area(void) +{ + Validate(); + if (Altitude == FLIGHT_LEVEL) { + Enter_Idle_Mode(); + return(1); + } + if (House->IsHuman) return(TICKS_PER_SECOND); + + if (Altitude == 0 && !In_Radio_Contact()) { + Scatter(0, true); + return(TICKS_PER_SECOND*3); + } + + if (Target_Legal(TarCom)) { + Assign_Mission(MISSION_ATTACK); + return(1); + } + return(FootClass::Mission_Guard_Area()); +} + + +/*********************************************************************************************** + * AircraftClass::Response_Attack -- Gives audio response to attack order. * + * * + * This routine is used to give an audio response to an attack order. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Response_Attack(void) +{ + Validate(); + static VocType _response[] = { + VOC_AFFIRM, + VOC_ACKNOWL, + VOC_YESSIR, + VOC_YESSIR, + VOC_YESSIR + }; + VocType response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + if (AllowVoice) { + Sound_Effect(response, 0, -(Aircraft.ID(this)+1)); + } +} + + +/*********************************************************************************************** + * AircraftClass::Response_Move -- Gives audio response to move request. * + * * + * This routine is used to give an audio response to movement orders. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Response_Move(void) +{ + Validate(); + static VocType _response[] = { + VOC_MOVEOUT, + VOC_MOVEOUT, + VOC_MOVEOUT, + VOC_ACKNOWL, + VOC_AFFIRM, + VOC_AFFIRM + }; + VocType response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + if (AllowVoice) { + Sound_Effect(response, 0, -(Aircraft.ID(this)+1)); + } +} + + +/*********************************************************************************************** + * AircraftClass::Response_Select -- Gives audio response when selected. * + * * + * This routine is called when an audio response for selection is desired. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Response_Select(void) +{ + Validate(); + static VocType _response[] = { + VOC_VEHIC, + VOC_UNIT, + VOC_YESSIR, + VOC_YESSIR, + VOC_YESSIR, + VOC_AWAIT + }; + VocType response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + if (AllowVoice) { + Sound_Effect(response, 0, -(Aircraft.ID(this)+1)); + } +} diff --git a/AIRCRAFT.H b/AIRCRAFT.H new file mode 100644 index 0000000..9bbd655 --- /dev/null +++ b/AIRCRAFT.H @@ -0,0 +1,254 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\aircraft.h_v 2.17 16 Oct 1995 16:47:48 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : AIRCRAFT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 22, 1994 * + * * + * Last Update : November 28, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef AIRCRAFT_H +#define AIRCRAFT_H + +#include "radio.h" +#include "fly.h" +#include "target.h" + + +class AircraftClass : public FootClass, public FlyClass +{ + public: + /* + ** This is a pointer to the class control structure for the aircraft. + */ + AircraftTypeClass const * const Class; + + //----------------------------------------------------------------------------- + void * operator new(size_t); + void operator delete(void *); + operator AircraftType(void) const {return Class->Type;}; + AircraftClass(void) : Class(0) {}; + AircraftClass(AircraftType classid, HousesType house); + virtual ~AircraftClass(void); + virtual RTTIType What_Am_I(void) const {return RTTI_AIRCRAFT;}; + + static void Init(void); + enum {FLIGHT_LEVEL=24}; + + virtual int Mission_Attack(void); + virtual int Mission_Unload(void); + virtual int Mission_Hunt(void); + virtual int Mission_Retreat(void); + virtual int Mission_Move(void); + virtual int Mission_Enter(void); + virtual int Mission_Guard(void); + virtual int Mission_Guard_Area(void); + + /* + ** State machine support routines. + */ + bool Process_Take_Off(void); + bool Process_Landing(void); + int Process_Fly_To(bool slowdown); + + /* + ** Query functions. + */ + virtual int Threat_Range(int control) const; + virtual int Rearm_Delay(bool second) const; + virtual MoveType Can_Enter_Cell(CELL cell, FacingType facing=FACING_NONE) const; + virtual LayerType In_Which_Layer(void) const; + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual ActionType What_Action(ObjectClass * target) const; + virtual ActionType What_Action(CELL cell) const; + virtual DirType Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const; + virtual int Pip_Count(void) const; + TARGET Good_Fire_Location(TARGET target) const; + bool Cell_Seems_Ok(CELL cell, bool landing=false) const; + DirType Pose_Dir(void) const; + TARGET Good_LZ(void) const; + virtual DirType Fire_Direction(void) const; + + /* + ** Landing zone support functionality. + */ + bool Is_LZ_Clear(TARGET target) const; + TARGET New_LZ(TARGET oldlz) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Sort_Y(void) const; + virtual COORDINATE Fire_Coord(int which) const; + virtual COORDINATE Target_Coord(void) const; + + /* + ** Object entry and exit from the game system. + */ + virtual bool Unlimbo(COORDINATE , DirType facing = DIR_N); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual bool Exit_Object(TechnoClass *); + virtual bool Mark(MarkType mark=MARK_CHANGE); + virtual short const * Overlap_List(void) const; + virtual void Draw_It(int x, int y, WindowNumberType window); + virtual void Set_Speed(int speed); + + /* + ** User I/O. + */ + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual void Active_Click_With(ActionType action, CELL cell); + virtual void Player_Assign_Mission(MissionType mission, TARGET target=TARGET_NONE, TARGET destination=TARGET_NONE); + virtual void Response_Select(void); + virtual void Response_Move(void); + virtual void Response_Attack(void); + + /* + ** Combat related. + */ +// virtual bool Target_Something_Nearby(ThreatType threat=THREAT_NORMAL); + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source); + virtual BulletClass * Fire_At(TARGET target, int which); + virtual TARGET As_Target(void) const; + + /* + ** AI. + */ + virtual void AI(void); + virtual void Enter_Idle_Mode(bool initial = false); + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual void Scatter(COORDINATE threat, bool forced=false); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** File I/O. + */ + static void Read_INI(char *buffer); + static void Write_INI(char *buffer); + static char * INI_Name(void) {return "AIRCRAFT";}; + bool Load(FileClass & file); + bool Save(FileClass & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + public: + + /* + ** This is the facing used for the body of the aircraft. Typically, this is the same + ** as the PrimaryFacing, but in the case of helicopters, it can be different. + */ + FacingClass SecondaryFacing; + + private: + + /* + ** Aircraft can be in either state of landing, taking off, or in steady altitude. + ** These flags are used to control transition between flying and landing. It is + ** necessary to handle the transition in this manner so that it occurs smoothly + ** during the graphic processing section. + */ + unsigned IsLanding:1; + unsigned IsTakingOff:1; + + /* + ** It is very common for aircraft to be homing in on a target. When this flag is + ** true, the aircraft will constantly adjust its facing toward the TarCom. When the + ** target is very close (one cell away or less), then this flag is automatically cleared. + ** This is because the homing algorithm is designed to get the aircraft to the destination + ** but no more. Checking when this flag is cleared is a way of flagging transition into + ** a new mode. Example: Transport helicopters go into a hovering into correct position + ** mode when the target is reached. + */ + unsigned IsHoming:1; + + /* + ** Helicopters that are about to land must hover into a position exactly above the landing + ** zone. When this flag is true, the aircraft will be adjusted so that it is exactly over + ** the TarCom. The facing of the aircraft is not altered by this movement. The affect + ** like the helicopter is hovering and shifting sideways to position over the landing + ** zone. When the position is over the landing zone, then this flag is set to false. + */ + unsigned IsHovering:1; + + /* + ** This is the jitter tracker to be used when the aircraft is a helicopter and + ** is flying. It is most noticable when the helicopter is hovering. + */ + unsigned char Jitter; + + public: + /* + ** This is the altitude of the aircraft. It is expressed in pixels that + ** the shadow is offset to the south. If the altitude reaches zero, then + ** the aircraft has landed. The altitude for normal aircraft is at + ** Flight_Level(). + */ + int Altitude; + + private: + + /* + ** This timer controls when the aircraft will reveal the terrain around itself. + ** When this timer expires and this aircraft has a sight range, then the + ** look around process will occur. + */ + TCountDownTimerClass SightTimer; + + /* + ** Most attack aircraft can make several attack runs. This value contains the + ** number of attack runs the aircraft has left. When this value reaches + ** zero then the aircraft is technically out of ammo. + */ + char AttacksRemaining; + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; +}; + +#endif diff --git a/ALLOC.CPP b/ALLOC.CPP new file mode 100644 index 0000000..05bfff9 --- /dev/null +++ b/ALLOC.CPP @@ -0,0 +1,590 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : ALLOC.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : February 1, 1992 * + * * + * Last Update : March 9, 1995 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Alloc -- Allocates system RAM. * + * Ram_Free -- Determines the largest free chunk of RAM. * + * Free -- Free an Alloc'ed block of RAM. * + * Resize_Alloc -- Change the size of an allocated block. * + * Heap_Size -- Size of the heap we have. * + * Total_Ram_Free -- Total amount of free RAM. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include +#include + + +#ifndef WWMEM_H +#include "wwmem.h" +#endif + + +extern "C" unsigned long Largest_Mem_Block ( void ) ; + +// +// use double-word alignment for allocs +// +#define LONG_ALIGNMENT 1 + +/* +** Define the equates necessary to call a DPMI interrupt. +*/ +#define DPMI_INT 0x0031 +#define DPMI_LOCK_MEM 0x0600 +#define DPMI_UNLOCK_MEM 0x0601 +#define LOGGING FALSE +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +unsigned long MinRam=0L; // Record of least memory at worst case. +unsigned long MaxRam=0L; // Record of total allocated at worst case. +static unsigned long TotalRam = 0L; +static unsigned long Memory_Calls = 0L; +static unsigned long RequestedSystemRam = 8*1024*1024; +static unsigned long LargestRamBlock = 0L; + +void (*Memory_Error)(void) = NULL; +void (*Memory_Error_Exit)(char *string) = NULL; + +/*************************************************************************** + * DPMI_LOCK -- handles locking a block of DPMI memory * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +#include"mono.h" +void DPMI_Lock(VOID const *ptr, long const size) +{ + union REGS regs; + struct SREGS sregs; + + /* + ** Lock memory + ** AX = 0x600 + ** BX:CX = starting linear address of memory to lock + ** SI:DI = size of region to lock (in bytes) + ** - If Failure, carry flag is set. + */ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_LOCK_MEM; + regs.x.ebx = ((long)ptr & 0xffff0000) >> 16; + regs.x.ecx = ((long)ptr & 0x0000ffff); + regs.x.esi = ((long)size & 0xffff0000) >> 16; + regs.x.edi = ((long)size & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI +// if (regs.x.cflag) { +// } +#if(0) + char *temp = (char *)ptr; + char hold; + for (int lp = 0; lp < size; lp += 2048) { + hold = *temp; + temp += 2048; + } +#endif + +} + +/*************************************************************************** + * DPMI_UNLOCK -- Handles unlocking a locked block of DPMI * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +void DPMI_Unlock(void const *ptr, long const size) +{ + union REGS regs; + struct SREGS sregs; + + /* + ** Unlock the memory + */ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_UNLOCK_MEM; // DPMI function to call + regs.x.ebx = ((long)ptr & 0xffff0000) >> 16; + regs.x.ecx = ((long)ptr & 0x0000ffff); + regs.x.esi = ((long)size & 0xffff0000) >> 16; + regs.x.edi = ((long)size & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI +// if (regs.x.cflag) { +// } + +} + +/*************************************************************************** + * Alloc -- Allocates system RAM. * + * * + * This is the basic RAM allocation function. It is used for all * + * memory allocations needed by the system or the main program. * + * * + * INPUT: bytes_to_alloc -- LONG value of the number of bytes to alloc. * + * * + * flags -- Memory allocation control flags. * + * MEM_NORMAL: No special flags. * + * MEM_CLEAR: Zero out memory block. * + * MEM_NEW: Called by a new. * + * * + * OUTPUT: Returns with pointer to allocated block. If NULL was returned * + * it indicates a failure to allocate. Note: NULL will never be * + * returned if the standard library allocation error routine is * + * used. * + * * + * WARNINGS: If you replace the standard memory allocation error routine * + * and make it so that Alloc CAN return with a NULL, be sure * + * and check for this in your code. * + * * + * HISTORY: * + * 09/03/1991 JLB : Documented. * + * 08/09/1993 JLB : Updated with EMS memory support. * + * 04/28/1994 JAW : Updated to 32bit Protected mode. * + * 03/09/1995 JLB : Fixed * + *=========================================================================*/ +void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags) +{ + union REGS regs ; + struct SREGS sregs ; + unsigned char *retval=NULL; // Pointer to allocated block. + unsigned long original_size; // Original allocation size. + unsigned long bytesfree; // Number of free bytes. + long *longptr=NULL; // Pointer used to store selector + static unsigned char _allocinit=0; + + + // + // Init memory system by finding largest block to alloc + // then allocate it to get one large heap and free it. + // There may be more memory available from DPMI but we only are + // for now allocating and freeing the first largest block. + // + if ( !_allocinit ) { + unsigned long largestblock = Largest_Mem_Block(); + largestblock -= 1024; // subtract for heap header and misc + largestblock &= 0xffff0000; // forcing to 64K boundary + + if ( largestblock ) { + LargestRamBlock = MIN( largestblock, RequestedSystemRam ); + unsigned char *lptr = (unsigned char *)malloc( LargestRamBlock ); + if ( lptr ) { + free( (void *)lptr ); + } + } + + /* + ** Initialize the total ram available value. + */ + TotalRam = Total_Ram_Free(MEM_NORMAL); + + _allocinit = 1; + } + + /* + ** Save the original allocated space size so that we can clear the + ** exact amount of RAM if they specified MEM_CLEAR. + */ + original_size = bytes_to_alloc; + + /* + ** Reserve one byte for the header of the memory we allocated. + ** We will store the flags variable there for later use. + */ +#if (LONG_ALIGNMENT) + bytes_to_alloc += (flags & MEM_LOCK) ? 8 : 4; +#else + bytes_to_alloc += (flags & MEM_LOCK) ? 5 : 1; +#endif + + + // Try to allocate the memory out of the protected mode memory + // chain if we did not require a real mode allocation. If this + // fails we will have to try to allocate it out of real mode memory. + // Real mode memory is a last resort because some types of applications + // require real mode memory. + if (!(flags & MEM_REAL)) { + retval = (unsigned char*)malloc(bytes_to_alloc); + } + + // Try to allocate the memory out of the real mode memory using DPMI + // service 0x100. Note that retval will be null if we are requesting + // real mode memory so that we do not have to explicitly check for the + // real mode flag. Remember we need to reserve room for the dos + // selector value at the beginning of our allocated block so rather than + // adding fifteen and rounding, we need to add 19 and round. + if (!retval) { + flags = (MemoryFlagType)(flags | MEM_REAL); + regs.x.eax = 0x100; + regs.x.ebx = (bytes_to_alloc + 19) >> 4; + if (regs.x.ebx & 0xFFFF0000) { + retval = NULL; + } else { + segread ( & sregs ) ; + int386x ( 0x31 , & regs, & regs , & sregs ) ; + if (regs.x.cflag) + retval = NULL; + else { +#if (LONG_ALIGNMENT) + longptr = (long *)(((regs.x.eax & 0xFFFF) << 4)+ 4); +#else + longptr = (long *)(((regs.x.eax & 0xFFFF) << 4)+ 1); +#endif + *longptr++ = regs.x.edx & 0xFFFF; + retval = (unsigned char *)longptr; + } + } + } + + // If the alloc failed then we need to signify a memory error. + if (retval == NULL) { + if (Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + // If the memory needs to be DPMI locked then we should store the + // original size in the header before we store the flags. + if (flags & MEM_LOCK) { + longptr = (long *)retval; + *longptr++ = original_size; + retval = (unsigned char *)longptr; + } + + + // Now that we know the alloc was sucessful (and for an extra byte + // more than the user wanted) we need to stick in the memory flags. +#if (LONG_ALIGNMENT) + if ( !(flags & (MEM_LOCK|MEM_REAL)) ) { + // + // WARNING!!!!!!!!!! + // USE this only with the WATCOM malloc ALLOCATION!!!!!!!!! + // it reads the actual block size before the ptr returned. + // then eors and uses the upper word for a validation later on free. + // + longptr = (long *)retval; + *longptr = ((*(longptr - 1)) ^ 0xffffffff) & 0xffff0000; + *retval++ = flags; + *retval++ = (unsigned char)(flags ^ 0xff); + retval += 2; + } + else { + *retval++ = flags; + *retval++ = (unsigned char)(flags ^ 0xff); + *retval++ = 0; + *retval++ = 0; + } +#else + *retval++ = (unsigned char)(flags | (((flags ^ 0x07) & 0x07) << 5)); +#endif + + // If the memory needed to be DPMI locked then set it up so it + // is locked. + if (flags & MEM_LOCK) { + DPMI_Lock(retval, original_size); + } + + /* Clear the space if they wanted it clear */ + + if (flags & MEM_CLEAR) { + unsigned char *ptr; // Working memory block pointer. + + ptr = retval; + memset(ptr, '\0', original_size); + } + + bytesfree = Total_Ram_Free(MEM_NORMAL); + if (bytesfree < MinRam) { + MinRam = bytesfree; + } + if (TotalRam-bytesfree > MaxRam) { + MaxRam = TotalRam-bytesfree; + } + + Memory_Calls++; + +#if(LOGGING) + int val = _heapchk(); + + FILE *file = fopen("mem.txt","at"); + fprintf(file, "%P Alloc size = %d, Actual Size = %d, flags = %d, heap = %d\n", + retval, + original_size, + bytes_to_alloc, + flags, + val); + fclose(file); +#endif + + return(retval); +} + + +/*************************************************************************** + * Free -- Free an Alloc'ed block of RAM. * + * * + * FUNCTION: * + * * + * INPUT: A pointer to a block of RAM from Alloc. * + * * + * OUTPUT: None. * + * * + * WARNINGS: Don't use this for an Alloc_Block'ed RAM block. * + * * + * HISTORY: * + * 05/25/1990 : Created. * + ***************************************************************************/ +void Free(void const *pointer) +{ + union REGS regs ; + struct SREGS sregs ; + + void const *original = pointer; + char string[80]; + + if (pointer) { + /* + ** Get a pointer to the flags that we stored off. + */ +#if (LONG_ALIGNMENT) + unsigned char *byteptr = ((unsigned char *)pointer) - 4; + + // + // validate the flags with and eor of the flags + // + if ( *byteptr != ((*(byteptr + 1)) ^ 0xff) ) { + if (Memory_Error_Exit != NULL) { + sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); + Memory_Error_Exit( string ); + } + } + else { + if ( !(*byteptr & (MEM_LOCK|MEM_REAL)) ) { + unsigned short *wordptr = (unsigned short *)(byteptr - 2); + + // + // WARNING!!!!!!!!!! + // USE this only with the WATCOM malloc ALLOCATION!!!!!!!!! + // it reads the actual block size before the ptr to be freed. + // then compares with the EOR to the value stored during allocation. + // + if ( *wordptr != ((*(wordptr + 2)) ^ 0xffff) ) { + if (Memory_Error_Exit != NULL) { + sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); + Memory_Error_Exit( string ); + } + } + } + else if ( *(byteptr + 2) || *(byteptr + 3) ) { + if (Memory_Error_Exit != NULL) { + sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); + Memory_Error_Exit( string ); + } + } + } +// if ( *byteptr != (*(byteptr + 1) ^ 0xff) || +// *(byteptr + 2) || *(byteptr + 3) ) { +// if (Memory_Error_Exit != NULL) { +// sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); +// Memory_Error_Exit( string ); +// } +// } +#else + unsigned char *byteptr = ((unsigned char *)pointer) - 1; + + if ( (*byteptr & 0xe0) != (((*byteptr ^ 0x07) & 0x07) << 5) ) { + if (Memory_Error_Exit != NULL) { + sprintf( string, "Error freeing pointer %p. Header invalid!!!\n", pointer ); + Memory_Error_Exit( string ); + } + } +#endif + + /* + ** Check to see if this was locked me and if it was unlock it. + */ + if (*byteptr & MEM_LOCK) { + long *longptr = ((long *)byteptr) - 1; + DPMI_Unlock(pointer, *longptr); + pointer = (void *)longptr; + } else + pointer = (void *)byteptr; + +#if(LOGGING) + int val = _heapchk(); + FILE *file = fopen("mem.txt","at"); + fprintf(file, "%P Free flags = %d, Heap = %d\n", + original, + *byteptr, + val); + fclose(file); +#endif + + // If the pointer is a real mode pointer than it will point to the + // first megabyte of system memory. If it does than we need to + // use DPMI to free it. + if (*byteptr & MEM_REAL) { + regs.x.eax = 0x101; + regs.x.edx = *(((long *)pointer) - 1); + segread ( & sregs ) ; + int386x(0x31, ®s, ®s, &sregs); + } else { + free((void *)pointer); + } + Memory_Calls--; + } +} + + +/*************************************************************************** + * Resize_Alloc -- Change the size of an allocated block. * + * * + * This routine will take a previously allocated block and change its * + * size without unnecessarily altering its contents. * + * * + * INPUT: pointer -- Pointer to the original memory allocation. * + * * + * new_size -- Size in bytes that it will be converted to. * + * * + * OUTPUT: Returns with a pointer to the new allocation. * + * * + * WARNINGS: ??? * + * * + * HISTORY: * + * 02/01/1992 JLB : Commented. * + *=========================================================================*/ +void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes) +{ + + unsigned long *temp; +// unsigned long diff, flags; + + temp = (unsigned long*)original_ptr; + + /* ReAlloc the space */ + temp = (unsigned long *)realloc(temp, new_size_in_bytes); + if (temp == NULL) { + if (Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + return(temp); +} + + +/*************************************************************************** + * Ram_Free -- Determines the largest free chunk of RAM. * + * * + * Use this routine to determine the largest free chunk of available * + * RAM for allocation. It also performs a check of the memory chain. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the size of the largest free chunk of RAM. * + * * + * WARNINGS: This does not return the TOTAL memory free, only the * + * largest free chunk. * + * * + * HISTORY: * + * 09/03/1991 JLB : Commented. * + *=========================================================================*/ +long Ram_Free(MemoryFlagType) +{ + return(_memmax()); +// return Largest_Mem_Block(); +} + + +/*************************************************************************** + * Heap_Size -- Size of the heap we have. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +long Heap_Size(MemoryFlagType ) +{ + if (!TotalRam) { + TotalRam = Total_Ram_Free(MEM_NORMAL); + } + return(TotalRam); +} + + +/*************************************************************************** + * Total_Ram_Free -- Total amount of free RAM. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + * 03/09/1995 JLB : Uses prerecorded heap size maximum. * + *=========================================================================*/ +long Total_Ram_Free(MemoryFlagType ) +{ + return(_memavl()); +// return Largest_Mem_Block () ; +} + diff --git a/ANIM.CPP b/ANIM.CPP new file mode 100644 index 0000000..43b070a --- /dev/null +++ b/ANIM.CPP @@ -0,0 +1,1175 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\anim.cpv 2.18 16 Oct 1995 16:48:48 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Dune * + * * + * File Name : ANIM.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 3, 1991 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AnimClass::AI -- This is the low level anim processor. * + * AnimClass::Adjust_Coord -- Adjusts anim coordinates * + * AnimClass::AnimClass -- The constructor for animation objects. * + * AnimClass::As_Target -- Converts the animation into a target value. * + * AnimClass::Attach_To -- Attaches animation to object specified. * + * AnimClass::Center_Coord -- Determine center of animation. * + * AnimClass::Detach -- Remove animation if attached to target. * + * AnimClass::Draw_It -- Draws the animation at the location specified. * + * AnimClass::In_Which_Layer -- Determines what render layer the anim should be in. * + * AnimClass::Init -- Performs pre-scenario initialization. * + * AnimClass::Mark -- Signals to map that redrawing is necessary. * + * AnimClass::Middle -- Processes any middle events. * + * AnimClass::Occupy_List -- Determines the occupy list for the animation. * + * AnimClass::Overlap_List -- Determines the overlap list for the animation. * + * AnimClass::Render -- Draws an animation object. * + * AnimClass::Sort_Y -- Returns with the sorting coordinate for the animation. * + * AnimClass::Start -- Processes initial animation side effects. * + * AnimClass::delete -- Returns an anim object back to the free pool. * + * AnimClass::new -- Allocates an anim object from the pool. * + * AnimClass::~AnimClass -- Destructor for anim objects. * + * AnimClass::Validate -- validates anim pointer * + * Shorten_Attached_Anims -- Reduces attached animation durations. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * AnimClass::VTable; + + +/*********************************************************************************************** + * AnimClass::Validate -- validates anim pointer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int AnimClass::Validate(void) const +{ + int num; + + num = Anims.ID(this); + if (num < 0 || num >= ANIM_MAX) { + Validate_Error("ANIM"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * Shorten_Attached_Anims -- Reduces attached animation durations. * + * * + * This routine is used to reduce the amount of time any attached animations will process. * + * Typical use of this is when an object is on fire and the object should now be destroyed * + * but the attached animations are to run until completion before destruction can follow. * + * This routine will make the animation appear to run its course, but in as short of time * + * as possible. The shortening effect is achieved by reducing the number of times the * + * animation will loop. * + * * + * INPUT: obj -- Pointer to the object that all attached animations will be processed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/11/1994 JLB : Created. * + *=============================================================================================*/ +void Shorten_Attached_Anims(ObjectClass * obj) +{ + if (obj) { + for (int index = 0; index < Anims.Count(); index++) { + AnimClass & anim = *Anims.Ptr(index); + + if (anim.Object == obj) { + anim.Loops = 0; + } + } + } +} + + +/*********************************************************************************************** + * AnimClass::Sort_Y -- Returns with the sorting coordinate for the animation. * + * * + * This routine is used by the sorting system. Animations that are located in the ground * + * layer will be sorted by this the value returned from this function. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the sort coordinate to use for this animation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 12/15/1994 JLB : Handles flat anims (infantry decay anims). * + *=============================================================================================*/ +COORDINATE AnimClass::Sort_Y(void) const +{ + Validate(); + if (Object) { + return(Coord_Add(Object->Sort_Y(), 0x00010000L)); + } + if (*this == ANIM_MOVE_FLASH) { + return(Coord_Add(Center_Coord(), XYP_COORD(0, -24))); + } + if (*this == ANIM_LZ_SMOKE) { + return(Coord_Add(Center_Coord(), XYP_COORD(0, 14))); + } + return(Coord); +} + + +/*********************************************************************************************** + * AnimClass::Center_Coord -- Determine center of animation. * + * * + * This support function will return the "center" of the animation. The actual coordinate * + * of the animation may be dependant on if the the animation is attached to an object. * + * In such a case, it must factor in the object's location. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the coordinate of the center of the animation. The coordinate is in real * + * game coordinates -- taking into consideration if the animation is attached. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +COORDINATE AnimClass::Center_Coord(void) const +{ + Validate(); + if (Object) { + return(Coord_Add(Coord, Object->Center_Coord())); + } + return(Coord); +} + + +/*********************************************************************************************** + * AnimClass::Render -- Draws an animation object. * + * * + * This is the working routine that renders the animation shape. It gets called once * + * per animation per frame. It needs to be fast. * + * * + * INPUT: bool; Should the animation be rendered in spite of render flag? * + * * + * OUTPUT: bool; Was the animation rendered? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +bool AnimClass::Render(bool forced) +{ + Validate(); + if (Delay) return(false); + IsToDisplay = true; + return(ObjectClass::Render(forced)); +} + + +/*********************************************************************************************** + * AnimClass::Draw_It -- Draws the animation at the location specified. * + * * + * This routine is used to render the animation object at the location specified. This is * + * how the map imagery gets updated. * + * * + * INPUT: x,y -- The pixel coordinates to draw the animation at. * + * * + * window -- The to base the draw coordinates upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 05/19/1995 JLB : Added white translucent effect. * + *=============================================================================================*/ +#pragma off (unreferenced) +void AnimClass::Draw_It(int x, int y, WindowNumberType window) +{ + Validate(); + if (!IsInvisible) { + void const * shapefile = Class->Get_Image_Data(); + if (shapefile) { + void const * transtable = NULL; + int shapenum = Class->Start + Fetch_Stage(); + void const * remap = NULL; + + /* + ** Some animations require special fixups. + */ + switch (Class->Type) { + case ANIM_ION_CANNON: + y -= Get_Build_Frame_Height(shapefile) >> 1; + y += 12; + break; + + case ANIM_RAPT_DIE: + case ANIM_STEG_DIE: + case ANIM_TREX_DIE: + case ANIM_TRIC_DIE: + case ANIM_ATOM_BLAST: + transtable = Map.UnitShadow; + break; + } + + /* + ** If the translucent table hasn't been determined yet, then check to see if it + ** should use the white or normal translucent tables. + */ + if (!transtable && Class->IsWhiteTrans) transtable = Map.WhiteTranslucentTable; + if (!transtable && Class->IsTranslucent) transtable = Map.TranslucentTable; + + /* + ** Set the shape flags to properly take into account any fading or ghosting + ** table necessary. + */ + ShapeFlags_Type flags = SHAPE_CENTER|SHAPE_WIN_REL; + if (IsAlternate) { + flags = flags | SHAPE_FADING; + remap = Map.RemapTables[HOUSE_GOOD][0]; + } + if (transtable) flags = flags | SHAPE_GHOST; + + /* + ** Draw the animation shape. + */ + CC_Draw_Shape(shapefile, shapenum, x, y, window, flags, remap, transtable); + } + } +} + + +/*********************************************************************************************** + * AnimClass::Mark -- Signals to map that redrawing is necessary. * + * * + * This routine is used by the animation logic system to inform the map that the cells * + * under the animation must be rerendered. * + * * + * INPUT: * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +bool AnimClass::Mark(MarkType mark) +{ + Validate(); + if (ObjectClass::Mark(mark)) { + Map.Refresh_Cells(Coord_Cell(Center_Coord()), Overlap_List()); + ObjectClass::Mark(mark); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * AnimClass::Overlap_List -- Determines the overlap list for the animation. * + * * + * Use this routine to fetch the overlap list for the animation. This overlap list is the * + * cells that this animation spills over. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to the overlap list for this particular instance of the * + * animation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +short const * AnimClass::Overlap_List(void) const +{ + Validate(); + static short const OverlapN[] = {0, -MAP_CELL_W, -(MAP_CELL_W+1), -(MAP_CELL_W-1), -(2*MAP_CELL_W), -(2*MAP_CELL_W-1), -(2*MAP_CELL_W+1), REFRESH_EOL}; + static short const OverlapNW[] = {0, -1, -MAP_CELL_W, -(MAP_CELL_W+1), -(MAP_CELL_W+2), -(MAP_CELL_W*2+2), -(MAP_CELL_W*2+1), REFRESH_EOL}; + static short const OverlapW[] = {0, -1, -2, -(MAP_CELL_W+1), -(MAP_CELL_W+2), REFRESH_EOL}; + static short const OverlapSW[] = {0, -1, MAP_CELL_W, (MAP_CELL_W-1), (MAP_CELL_W-2), (MAP_CELL_W*2-2), (MAP_CELL_W*2-1), REFRESH_EOL}; + static short const OverlapS[] = {0, MAP_CELL_W-1, MAP_CELL_W, MAP_CELL_W+1, 2*MAP_CELL_W+1, 2*MAP_CELL_W, 2*MAP_CELL_W-1, REFRESH_EOL}; + static short const OverlapSE[] = {0, 1, MAP_CELL_W, (MAP_CELL_W+1), (MAP_CELL_W+2), (MAP_CELL_W*2+2), (MAP_CELL_W*2+1), REFRESH_EOL}; + static short const OverlapE[] = {0, 1, 2, -(MAP_CELL_W-1), -(MAP_CELL_W-2), REFRESH_EOL}; + static short const OverlapNE[] = {0, 1, -MAP_CELL_W, -(MAP_CELL_W-1), -(MAP_CELL_W-2), -(MAP_CELL_W*2-2), -(MAP_CELL_W*2-1), REFRESH_EOL}; + static short const OverlapIon[] = { + (-MAP_CELL_W * 7) - 1, (-MAP_CELL_W * 7), (-MAP_CELL_W * 7) + 1, + (-MAP_CELL_W * 6) - 1, (-MAP_CELL_W * 6), (-MAP_CELL_W * 6) + 1, + (-MAP_CELL_W * 5) - 1, (-MAP_CELL_W * 5), (-MAP_CELL_W * 5) + 1, + (-MAP_CELL_W * 4) - 1, (-MAP_CELL_W * 4), (-MAP_CELL_W * 4) + 1, + (-MAP_CELL_W * 3) - 1, (-MAP_CELL_W * 3), (-MAP_CELL_W * 3) + 1, + (-MAP_CELL_W * 2) - 1, (-MAP_CELL_W * 2), (-MAP_CELL_W * 2) + 1, + (-MAP_CELL_W * 1) - 1, (-MAP_CELL_W * 1), (-MAP_CELL_W * 1) + 1, + (-MAP_CELL_W * 0) - 1, (-MAP_CELL_W * 0), (-MAP_CELL_W * 0) + 1, + REFRESH_EOL + }; + + static short const OverlapAtom[] = { + (-MAP_CELL_W * 2) - 1, (-MAP_CELL_W * 2), (-MAP_CELL_W * 2) + 1, + (-MAP_CELL_W * 1) - 1, (-MAP_CELL_W * 1), (-MAP_CELL_W * 1) + 1, + (-MAP_CELL_W * 0) - 1, (-MAP_CELL_W * 0), (-MAP_CELL_W * 0) + 1, + ( MAP_CELL_W * 1) - 1, ( MAP_CELL_W * 1), ( MAP_CELL_W * 1) + 1, + ( MAP_CELL_W * 2) - 1, ( MAP_CELL_W * 2), ( MAP_CELL_W * 2) + 1, + REFRESH_EOL + }; + + switch (Class->Type) { + case ANIM_CHEM_N: + case ANIM_FLAME_N: + return(OverlapN); + + case ANIM_CHEM_NW: + case ANIM_FLAME_NW: + return(OverlapNW); + + case ANIM_CHEM_W: + case ANIM_FLAME_W: + return(OverlapW); + + case ANIM_CHEM_SW: + case ANIM_FLAME_SW: + return(OverlapSW); + + case ANIM_CHEM_S: + case ANIM_FLAME_S: + return(OverlapS); + + case ANIM_CHEM_SE: + case ANIM_FLAME_SE: + return(OverlapSE); + + case ANIM_CHEM_E: + case ANIM_FLAME_E: + return(OverlapE); + + case ANIM_CHEM_NE: + case ANIM_FLAME_NE: + return(OverlapNE); + + case ANIM_ION_CANNON: + return(OverlapIon); + + case ANIM_ATOM_BLAST: + return(OverlapAtom); + + default: + break; + } + return(Coord_Spillage_List(Center_Coord(), Class->Size)); +} + + +/*********************************************************************************************** + * AnimClass::Occupy_List -- Determines the occupy list for the animation. * + * * + * Animations always occupy only the cell that their center is located over. As such, this * + * routine always returns a simple (center cell) occupation list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the occupation list for the animation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +short const * AnimClass::Occupy_List(void) const +{ + Validate(); + static short _simple[] = {REFRESH_EOL}; + + return(_simple); +} + + +/*********************************************************************************************** + * AnimClass::Init -- Performs pre-scenario initialization. * + * * + * This routine is used to initialize the animation system prior to a scenario being loaded * + * or reloaded. It effectively removes all animations from the system. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void AnimClass::Init(void) +{ + AnimClass *ptr; + + Anims.Free_All(); + + ptr = new AnimClass(); + VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0]; + delete ptr; +} + + +/*********************************************************************************************** + * AnimClass::new -- Allocates an anim object from the pool. * + * * + * This routine is used to allocate a free anim class object from the preallocated pool * + * in the near heap. If there are no free animation objects, then null is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to a free anim object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void *AnimClass::operator new(size_t) +{ + void * ptr = Anims.Allocate(); + if (ptr) { + ((AnimClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * AnimClass::delete -- Returns an anim object back to the free pool. * + * * + * This routine is used to return an anim object back to the pool of free anim objects. * + * Anim objects so returned are available to be reallocated for the next animation. * + * * + * INPUT: ptr -- Pointer to the anim object to return to the pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void AnimClass::operator delete(void *ptr) +{ + if (ptr) { + ((AnimClass *)ptr)->IsActive = false; + } + Anims.Free((AnimClass *)ptr); +} + + +/*********************************************************************************************** + * AnimClass::AnimClass -- The constructor for animation objects. * + * * + * This routine is used as the constructor of animation objects. It initializes and adds * + * the animation object to the display and logic systems. * + * * + * INPUT: animnum -- The animation number to start. * + * * + * coord -- The location of the animation. * + * * + * timedelay-- The delay before the animation starts. * + * * + * loop -- The number of times to loop this animation. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 08/03/1994 JLB : Added a delayed affect parameter. * + *=============================================================================================*/ +AnimClass::AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay, unsigned char loop, bool alt) : + Class(&AnimTypeClass::As_Reference(animnum)) +{ + Object = 0; + Owner = HOUSE_NONE; + + if (Class->Stages == -1) { + ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data()); + } + if (Class->LoopEnd == -1) { + ((int&)Class->LoopEnd) = Class->Stages; + } + if (Class->IsNormalized) { + Set_Rate(Options.Normalize_Delay(Class->Delay)); + } else { + Set_Rate(Class->Delay); + } + Set_Stage(0); + + Accum = 0; + coord = Adjust_Coord(coord); + Unlimbo(coord); + + /* + ** Drop zone smoke always reveals the map around itself. + */ + if (*this == ANIM_LZ_SMOKE) { + Map.Sight_From(Coord_Cell(coord), 4, false); + } + + /* + ** Determine the time before the first animation process. For time delayed + ** animations, this is the value passed as a parameter. + */ + Delay = timedelay; + + Loops = (unsigned char)(MAX(loop, 1) * Class->Loops); + Loops = (unsigned char)MAX(Loops, 1); + + IsToDelete = false; + IsBrandNew = true; + IsAlternate = alt; + IsInvisible = false; + + /* + ** If the animation starts immediately, then play the associated sound effect now. + */ + if (!Delay) { + Start(); + } +} + + +/*********************************************************************************************** + * AnimClass::~AnimClass -- Destructor for anim objects. * + * * + * This destructor handles removing the animation object from the system. It might require * + * informing any object this animation is attached to that it is no longer attached. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/29/1994 JLB : Created. * + *=============================================================================================*/ +AnimClass::~AnimClass(void) +{ + Validate(); + if (GameActive) { + + /* + ** If this anim is attached to another object + ** then check to see if this is the last anim attached to it. If this + ** is the case, then inform the object that it is no longer attached to + ** an animation. + */ + if (Object) { + ObjectClass * to = Object; + + Object = 0; + + /* + ** Scan for any other animations that are attached to the object that + ** this animation is attached to. If there are no others, then inform the + ** attached object of this fact. + */ + for (int index = 0; index < Anims.Count(); index++) { + if (Anims.Ptr(index)->Object == to) break; + } + + /* + ** Tell the object that it is no longer being damaged. + */ + if (index != Anims.Count()) { + to->Fire_Out(); + } + to->Mark(MARK_OVERLAP_UP); + to->IsAnimAttached = false; + to->Mark(MARK_OVERLAP_DOWN); + Object = to; + } + + Limbo(); + Object = 0; + } +} + + +/*********************************************************************************************** + * AnimClass::AI -- This is the low level anim processor. * + * * + * This routine is called once per frame per animation. It handles transition between * + * animation frames and marks the map for redraw as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Speed is of upmost importance. * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void AnimClass::AI(void) +{ + Validate(); + /* + ** For ground level based animations (ones that can run slowly as well as + ** occur behind other ground objects) always cause the cell to be redrawn. + */ + if (!Delay && Class->IsGroundLayer) { + Map.Refresh_Cells(Coord_Cell(Center_Coord()), Overlap_List()); + } + + /* + ** Special case check to make sure that building on top of a smoke marker + ** causes the smoke marker to vanish. + */ + if (Class->Type == ANIM_LZ_SMOKE && Map[Coord_Cell(Center_Coord())].Cell_Building()) { + IsToDelete = true; + } + + /* + ** Delete this animation and bail early if the animation is flagged to be deleted + ** immediately. + */ + if (IsToDelete) { + delete this; + return; + } + + /* + ** If this is a brand new animation, then don't process it the first logic pass + ** since it might end up skipping the first animation frame before it has had a + ** chance to draw it. + */ + if (IsBrandNew) { + IsBrandNew = false; + return; + } + + if (Delay) { + Delay--; + if (!Delay) { + Start(); + } + } else { + + /* + ** This is necessary because there is no recording of animations on the map + ** and thus the animation cannot be intelligently flagged for redraw. Most + ** animations move fast enough that they would need to be redrawn every + ** game frame anyway so this isn't TOO bad. + */ + Mark(MARK_CHANGE); + + if (StageClass::Graphic_Logic()) { + int stage = Fetch_Stage(); + + /* + ** If this animation is attached to another object and it is a + ** damaging kind of animation, then do the damage to the other + ** object. + */ + if (Object && Class->Damage) { + unsigned int accum = Accum; + + accum += Class->Damage; + + if (accum > 255) { + + /* + ** Administer the damage. If the object was destroyed by this anim, + ** then the attached damaging anim is also destroyed. + */ + int damage = accum >> 8; + if (Object->Take_Damage(damage, 0, WARHEAD_FIRE) == RESULT_DESTROYED) { + //Object = 0; + delete this; + return; + } + } + Accum = (unsigned char)(accum & 0x00FF); + } + + /* + ** During the biggest stage (covers the most ground), perform any ground altering + ** action required. This masks craters and scorch marks, so that they appear + ** naturally rather than "popping" into existance while in plain sight. + */ + if (Class->Start+stage == Class->Biggest) { + Middle(); + } + + /* + ** Check to see if the last frame has been displayed. If so, then the + ** animation either ends or loops. + */ + if ((Loops <= 1 && stage >= Class->Stages) || (Loops > 1 && stage >= Class->LoopEnd-Class->Start)) { + + /* + ** Determine if this animation should loop another time. If so, then start the loop + ** but if not, then proceed into the animation termination handler. + */ + if (Loops) Loops--; + if (Loops) { + Set_Stage(Class->LoopStart); + } else { + + /* + ** The animation should end now, but first check to see if + ** it needs to chain into another animation. If so, then the + ** animation isn't technically over. It metamorphoses into the + ** new form. + */ + if (Class->ChainTo != ANIM_NONE) { +// AnimTypeClass const * aptr = &AnimTypeClass::As_Reference(Class->ChainTo); + + ((AnimTypeClass const *&)Class) = &AnimTypeClass::As_Reference(Class->ChainTo); + + if (Class->IsNormalized) { + Set_Rate(Options.Normalize_Delay(Class->Delay)); + } else { + Set_Rate(Class->Delay); + } + Set_Stage(Class->Start); + } else { + delete this; + } + } + } + } + } +} + + +/*********************************************************************************************** + * AnimClass::Attach_To -- Attaches animation to object specified. * + * * + * An animation can be "attached" to an object. In such cases, the animation is rendered * + * as an offset from the center of the object it is attached to. This allows affects such * + * as fire or smoke to be consistently placed on the vehicle it is associated with. * + * * + * INPUT: obj -- Pointer to the object to attach the animation to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +void AnimClass::Attach_To(ObjectClass * obj) +{ + Validate(); + if (!obj) return; + + obj->Mark(MARK_OVERLAP_UP); + obj->IsAnimAttached = true; + obj->Mark(MARK_OVERLAP_DOWN); + Map.Remove(this, In_Which_Layer()); + Object = obj; + Map.Submit(this, In_Which_Layer()); + Coord = Coord_Sub(Coord, obj->Center_Coord()); +} + + +/*********************************************************************************************** + * AnimClass::In_Which_Layer -- Determines what render layer the anim should be in. * + * * + * Use this routine to find out which display layer (ground or air) that the animation * + * should be in. This information is used to place the animation into the correct display * + * list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the layer that the animation should exist in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/25/1994 JLB : Created. * + *=============================================================================================*/ +LayerType AnimClass::In_Which_Layer(void) const +{ + Validate(); + if (Object || Class->IsGroundLayer) { + return(LAYER_GROUND); + } + return(LAYER_AIR); +} + + +/*********************************************************************************************** + * AnimClass::Start -- Processes initial animation side effects. * + * * + * This routine is called when the animation first starts. Sometimes there are side effects * + * associated with this animation that must occur immediately. Typically, this is the * + * sound effect assigned to this animation. If this animation is supposed to attach itself * + * to any object at its location, then do so at this time as well. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + *=============================================================================================*/ +void AnimClass::Start(void) +{ + Validate(); + CELL cell = Coord_Cell(Coord); + + Mark(); + + /* + ** Play the sound effect for this animation. + */ + Sound_Effect(Class->Sound, Coord); + + /* + ** If the stage where collateral effects occur is the first stage of the animation, then + ** perform this action now. Subsiquent checks against this stage value starts with the + ** second frame of the animation. + */ + if (!Class->Biggest) { + Middle(); + } + + /* + ** If this is the kind of animation that sticks to whatever object is in the same + ** location, then attach the animation to the object. If the animation is already + ** attached, then do nothing. + */ + if (!Object && Class->IsSticky && Map.In_Radar(cell)) { + UnitClass * unit = Map[cell].Cell_Unit(); + + if (unit && *unit == UNIT_GUNBOAT) { + Attach_To(unit); + } + } +} + + +/*********************************************************************************************** + * AnimClass::Middle -- Processes any middle events. * + * * + * This routine is called when the animation as reached its largest stage. Typically, this * + * routine is used to cause scorches or craters to appear at a cosmetically pleasing * + * moment. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + *=============================================================================================*/ +void AnimClass::Middle(void) +{ + Validate(); + CELL cell = Coord_Cell(Center_Coord()); + CellClass * cellptr = &Map[cell]; + + if (Class->Type == ANIM_ATOM_BLAST) { + + /* + ** Find someone to blame the explosion on. This is necessary in + ** order to properly enact retribution and record the kill for + ** score purposes. + */ + BuildingClass * building = NULL; + TechnoClass * backup = NULL; + if (Owner != HOUSE_NONE) { + for (int index = 0; index < Logic.Count(); index++) { + ObjectClass * obj = Logic[index]; + + if (obj && obj->Is_Techno() && obj->Owner() == Owner) { + backup = (TechnoClass *)obj; + if (obj->What_Am_I() == RTTI_BUILDING && *((BuildingClass *)obj) == STRUCT_TEMPLE) { + building = (BuildingClass *)obj; + break; + } + } + } + + if (!building) building = (BuildingClass *)backup; + } + + int radius = 3; + int rawdamage = 200; + if (GameToPlay == GAME_NORMAL) { + radius = 4; + rawdamage = 1000; + Fade_Palette_To(WhitePalette, 30, NULL); + } + for (int x = -radius; x <= radius; x++) { + for (int y = -radius; y <= radius; y++) { + int xpos = Cell_X(cell) + x; + int ypos = Cell_Y(cell) + y; + + /* + ** If the potential damage cell is outside of the map bounds, + ** then don't process it. This unusual check method ensures that + ** damage won't wrap from one side of the map to the other. + */ + if ((unsigned)xpos > MAP_CELL_W) { + continue; + } + if ((unsigned)ypos > MAP_CELL_H) { + continue; + } + CELL tcell = XY_Cell(xpos, ypos); + if (!Map.In_Radar(tcell)) continue; + + int damage = rawdamage / ((ABS(radius)/2)+1); + Explosion_Damage(Cell_Coord(tcell), damage, building, WARHEAD_FIRE); + new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), Cell_Coord(tcell)); + } + } + Shake_Screen(3); + if (GameToPlay == GAME_NORMAL) { + Fade_Palette_To(GamePalette, 15, NULL); + } + } + + /* + ** If this animation leaves scorch marks (e.g., napalm), then do so at this time. + */ + if (Class->IsScorcher) { + new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), Center_Coord()); + } + + /* + ** Some animations leave a crater when they occur. Artillery is a good example. + ** Craters always remove the Tiberium where they occur. + */ + if (Class->IsCraterForming) { + + /* + ** Craters reduce the level of Tiberium in the cell. + */ + cellptr->Reduce_Tiberium(6); + + /* + ** If there already is a crater in the cell, then just expand the + ** crater. + */ + new SmudgeClass(SMUDGE_CRATER1, Center_Coord()); + } + + /* + ** Flame throwers leave scorch marks in unusual positions. In addition, they leave fire + ** shards in unusual positions as well. + */ + if (Class->IsFlameThrower) { + COORDINATE c2 = Coord_Move(Center_Coord(), (DirType)((Class->Type - ANIM_FLAME_N)<<5), 0x00E0); + CELL cell = Coord_Cell(c2); + COORDINATE c3 = Map.Closest_Free_Spot(Coord_Move(Center_Coord(), (DirType)((Class->Type - ANIM_FLAME_N)<<5), 0x0140), true); + + c2 = Map.Closest_Free_Spot(c2, true); + if (c3 && (Random_Pick(0, 1) == 1)) { + if (!Map[Coord_Cell(c3)].Cell_Terrain()) { + new AnimClass(ANIM_FIRE_SMALL, c3, 0, 2); + } + } + if (c2 && (Random_Pick(0, 1) == 1)) { + if (!Map[Coord_Cell(c2)].Cell_Terrain()) { + new AnimClass(ANIM_FIRE_SMALL, c2, 0, 2); + } + } + new SmudgeClass(SMUDGE_SCORCH1, c2); + if (c3 && (Random_Pick(0, 1) == 1)) { + if (!Map[Coord_Cell(c3)].Cell_Terrain()) { + new AnimClass(ANIM_SMOKE_M, c3); + } + } + } + + AnimClass * newanim; + + /* + ** If this animation spawns side effects during its lifetime, then + ** do so now. + */ + switch (Class->Type) { + case ANIM_ION_CANNON: { + BuildingClass * building = NULL; + TechnoClass * backup = NULL; + if (Owner != HOUSE_NONE) { + for (int index = 0; index < Logic.Count(); index++) { + ObjectClass * obj = Logic[index]; + + if (obj && obj->Is_Techno() && obj->Owner() == Owner && !obj->IsInLimbo) { + backup = (TechnoClass *)obj; + if (obj->What_Am_I() == RTTI_BUILDING && *((BuildingClass *)obj) == STRUCT_EYE) { + building = (BuildingClass *)obj; + break; + } + } + } + + if (!building) building = (BuildingClass *)backup; + } + Explosion_Damage(Center_Coord(), 600, building, WARHEAD_PB); + } + break; + + case ANIM_NAPALM1: + case ANIM_NAPALM2: + case ANIM_NAPALM3: + new AnimClass(ANIM_FIRE_SMALL, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x0040), true), 0, ((Random_Pick(0, 1) == 1) ? 1 : 2)); + if (Random_Pick(0, 1) == 1) { + new AnimClass(ANIM_FIRE_SMALL, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x00A0), true), 0, ((Random_Pick(0, 1) == 1) ? 1 : 2)); + } + if (Random_Pick(0, 1) == 1) { + new AnimClass(ANIM_FIRE_MED, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x0070), true), 0, ((Random_Pick(0, 1) == 1) ? 1 : 2)); + } + break; + + case ANIM_FIRE_MED: + case ANIM_FIRE_MED2: + newanim = new AnimClass(ANIM_FIRE_SMALL, Center_Coord(), 0, ((Random_Pick(0, 1) == 1) ? 1 : 2)); + if (newanim && Object) { + newanim->Attach_To(Object); + } + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * AnimClass::As_Target -- Converts the animation into a target value. * + * * + * This support routine is used to convert the animation (as a pointer) into a target * + * value (which is a number). * + * * + * INPUT: none * + * * + * OUTPUT: Returns the animation as a target value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +TARGET AnimClass::As_Target(void) const +{ + Validate(); + return(Build_Target(KIND_ANIMATION, Anims.ID(this))); +} + + +/*************************************************************************** + * AnimClass::Adjust_Coord -- Adjusts anim coordinates * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/17/1995 PWG : Created. * + *=========================================================================*/ +COORDINATE AnimClass::Adjust_Coord(COORDINATE coord) +{ + Validate(); + int x,y; + + switch (Class->Type) { + case ANIM_ATOM_DOOR: + x = -1; + y = -36; + break; + + default: + return(coord); + } + COORDINATE addval = XYPixel_Coord(x, y); + coord = Coord_Add(coord, addval); + return(coord); +} + + +/*********************************************************************************************** + * AnimClass::Detach -- Remove animation if attached to target. * + * * + * This routine is called when the specified target is being removed from the game. If this * + * animation happens to be attached to this object, then the animation must be remove as * + * well. * + * * + * INPUT: target -- The target that is about to be destroyed. * + * * + * all -- Is the target being destroyed RIGHT NOW? If not, then it will be * + * destroyed soon. In that case, the animation should continue to remain * + * attached for cosmetic reasons. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + * 07/02/1995 JLB : Detach is a precursor to animation destruction. * + *=============================================================================================*/ +void AnimClass::Detach(TARGET target, bool all) +{ + Validate(); + if (Object && Object->As_Target() == target && all) { + Map.Remove(this, In_Which_Layer()); + Object = NULL; + IsToDelete = true; + } +} diff --git a/ANIM.H b/ANIM.H new file mode 100644 index 0000000..0cfc6a2 --- /dev/null +++ b/ANIM.H @@ -0,0 +1,174 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\anim.h_v 2.20 16 Oct 1995 16:45:40 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ANIM.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 30, 1994 * + * * + * Last Update : May 30, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef ANIM_H +#define ANIM_H + +#include "type.h" + +/********************************************************************************************** +** This is the class that controls the shape animation objects. Shape animation objects are +** displayed over the top of the game map. Typically, they are used for explosion and fire +** effects. +*/ +class AnimClass : public ObjectClass, private StageClass { + public: + + static void * AnimClass::operator new(size_t size); + static void AnimClass::operator delete(void *ptr); + AnimClass(void) : Class(0) {Owner=HOUSE_NONE;Object=0;}; // Default constructor does nothing. + AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay=0, unsigned char loop=1, bool alt=false); + virtual ~AnimClass(void); + operator AnimType(void) const {return Class->Type;}; + virtual RTTIType What_Am_I(void) const {return RTTI_ANIM;}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + static void Init(void); + + void Attach_To(ObjectClass *obj); + void Make_Invisible(void) {IsInvisible = true;}; + + virtual bool Can_Place_Here(COORDINATE ) const {return true;} + virtual bool Mark(MarkType mark=MARK_CHANGE); + virtual bool Render(bool forced); + virtual COORDINATE Center_Coord(void) const; + virtual COORDINATE Sort_Y(void) const; + virtual LayerType In_Which_Layer(void) const; + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual short const * Occupy_List(void) const; + virtual short const * Overlap_List(void) const; + virtual void Draw_It(int x, int y, WindowNumberType window); + virtual void AI(void); + virtual TARGET As_Target(void) const; + virtual void Detach(TARGET target, bool all); + + /* + ** File I/O. + */ + bool Load(FileClass & file); + bool Save(FileClass & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + /* + ** If this animation is attached to an object, then this points to that object. An + ** animation that is attached will follow that object as it moves. This is important + ** for animations such as flames and smoke. + */ + ObjectClass * Object; + + /* + ** If this animation has an owner, then it will be recorded here. An owner + ** is used when damage is caused by this animation during the middle of its + ** animation. + */ + HousesType Owner; + + /* + ** This counter tells how many more times the animation should loop before it + ** terminates. + */ + unsigned char Loops; + + protected: + void Middle(void); + void Start(void); + + private: + /* + ** Define a function to make adjustments for where special animations + ** are going to render. + */ + COORDINATE Adjust_Coord(COORDINATE coord); + + /* + ** Delete this animation at the next opportunity. This is flagged when the + ** animation is to be prematurely ended as a result of some outside event. + */ + unsigned IsToDelete:1; + + /* + ** If the animation has just been created, then don't do any animation + ** processing until it has been through the render loop at least once. + */ + unsigned IsBrandNew:1; + + // Use alternate color when drawing? + unsigned IsAlternate:1; + + /* + ** If this animation is invisible, then this flag will be true. An invisible + ** animation is one that is created for the sole purpose of keeping all + ** machines syncronised. It will not be displayed. + */ + unsigned IsInvisible:1; + + /* + ** This points to the type of animation object this is. + */ + AnimTypeClass const * const Class; + + /* + ** Is this animation in a temporary suspended state? If so, then it won't + ** be rendered until this flag is false. The flag will be set to false + ** after the first countdown timer reaches 0. + */ + unsigned char Delay; + + /* + ** If this is an animation that damages whatever it is attached to, then this + ** value holds the accumulation of fractional damage points. When the accumulated + ** fractions reach 256, then one damage point is applied to the attached object. + */ + unsigned char Accum; + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; +}; + + + +#endif diff --git a/AUDIO.CPP b/AUDIO.CPP new file mode 100644 index 0000000..6ec4f75 --- /dev/null +++ b/AUDIO.CPP @@ -0,0 +1,544 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\audio.cpv 2.17 16 Oct 1995 16:50:20 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : AUDIO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : May 4, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Is_Speaking -- Checks to see if the eva voice is still playing. * + * Sound_Effect -- General purpose sound player. * + * Sound_Effect -- Plays a sound effect in the tactical map. * + * Speak -- Computer speaks to the player. * + * Speak_AI -- Handles starting the EVA voices. * + * Stop_Speaking -- Forces the EVA voice to stop talking. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** +** Controls what special effects may occur on the sound effect. +*/ +typedef enum { + IN_NOVAR, // No variation or alterations allowed. + IN_JUV, // Juvenile sound effect alternate option. + IN_VAR, // Infantry variance response modification. +} ContextType; + +static struct { + char const *Name; // Digitized voice file name. + int Priority; // Playback priority of this sample. + ContextType Where; // In what game context does this sample exist. +} SoundEffectName[VOC_COUNT] = { + + /* + ** Special voices (typically associated with the commando). + */ + {"BOMBIT1", 20, IN_NOVAR}, // VOC_RAMBO_PRESENT "I've got a present for ya" + {"CMON1", 20, IN_NOVAR}, // VOC_RAMBO_CMON "c'mon" + {"GOTIT1", 20, IN_NOVAR}, // VOC_RAMBO_UGOTIT "you got it" * + {"KEEPEM1", 20, IN_NOVAR}, // VOC_RAMBO_COMIN "keep 'em commin'" + {"LAUGH1", 20, IN_NOVAR}, // VOC_RAMBO_LAUGH "hahaha" + {"LEFTY1", 20, IN_NOVAR}, // VOC_RAMBO_LEFTY "that was left handed" * + {"NOPRBLM1", 20, IN_NOVAR}, // VOC_RAMBO_NOPROB "no problem" +// {"OHSH1", 20, IN_NOVAR}, // VOC_RAMBO_OHSH "oh shiiiiii...." + {"ONIT1", 20, IN_NOVAR}, // VOC_RAMBO_ONIT "I'm on it" + {"RAMYELL1", 20, IN_NOVAR}, // VOC_RAMBO_YELL "ahhhhhhh" + {"ROKROLL1", 20, IN_NOVAR}, // VOC_RAMBO_ROCK "time to rock and roll" + {"TUFFGUY1", 20, IN_NOVAR}, // VOC_RAMBO_TUFF "real tuff guy" * + {"YEAH1", 20, IN_NOVAR}, // VOC_RAMBO_YEA "yea" * + {"YES1", 20, IN_NOVAR}, // VOC_RAMBO_YES "yes" * + {"YO1", 20, IN_NOVAR}, // VOC_RAMBO_YO "yo" + + /* + ** Civilian voices (technicians too). + */ + {"GIRLOKAY", 20, IN_NOVAR}, // VOC_GIRL_OKAY + {"GIRLYEAH", 20, IN_NOVAR}, // VOC_GIRL_YEAH + {"GUYOKAY1", 20, IN_NOVAR}, // VOC_GUY_OKAY + {"GUYYEAH1", 20, IN_NOVAR}, // VOC_GUY_YEAH + + /* + ** Infantry and vehicle responses. + */ + {"2DANGR1", 10, IN_VAR}, // VOC_2DANGER "negative, too dangerous" + {"ACKNO", 10, IN_VAR}, // VOC_ACKNOWL "acknowledged" + {"AFFIRM1", 10, IN_VAR}, // VOC_AFFIRM "affirmative" + {"AWAIT1", 10, IN_VAR}, // VOC_AWAIT1 "awaiting orders" +// {"BACKUP", 10, IN_VAR}, // VOC_BACKUP "send backup" +// {"HELP", 10, IN_VAR}, // VOC_HELP "send help" + {"MOVOUT1", 10, IN_VAR}, // VOC_MOVEOUT "movin' out" + {"NEGATV1", 10, IN_VAR}, // VOC_NEGATIVE "negative" + {"NOPROB", 10, IN_VAR}, // VOC_NO_PROB "not a problem" + {"READY", 10, IN_VAR}, // VOC_READY "ready and waiting" + {"REPORT1", 10, IN_VAR}, // VOC_REPORT "reporting" + {"RITWAWA", 10, IN_VAR}, // VOC_RIGHT_AWAY "right away sir" + {"ROGER", 10, IN_VAR}, // VOC_ROGER "roger" +// {"SIR1", 10, IN_VAR}, // VOC_SIR1 "sir?" +// {"SQUAD1", 10, IN_VAR}, // VOC_SQUAD1 "squad reporting" +// {"TARGET1", 10, IN_VAR}, // VOC_PRACTICE "target practice" + {"UGOTIT", 10, IN_VAR}, // VOC_UGOTIT "you got it" + {"UNIT1", 10, IN_VAR}, // VOC_UNIT1 "unit reporting" + {"VEHIC1", 10, IN_VAR}, // VOC_VEHIC1 "vehicle reporting" + {"YESSIR1", 10, IN_VAR}, // VOC_YESSIR "yes sir" + + /* + ** Sound effects that have a juvenile counterpart. + */ + {"BAZOOK1", 1, IN_JUV}, // VOC_BAZOOKA Gunfire + {"BLEEP2", 1, IN_JUV}, // VOC_BLEEP Clean metal bing + {"BOMB1", 1, IN_JUV}, // VOC_BOMB1 Crunchy parachute bomb type explosion + {"BUTTON", 1, IN_JUV}, // VOC_BUTTON Dungeon Master button click + {"COMCNTR1", 10, IN_JUV}, // VOC_RADAR_ON Elecronic static with beeps + {"CONSTRU2", 10, IN_JUV}, // VOC_CONSTRUCTION construction sounds + {"CRUMBLE", 1, IN_JUV}, // VOC_CRUMBLE muffled crumble sound + {"FLAMER2", 4, IN_JUV}, // VOC_FLAMER1 flame thrower + {"GUN18", 4, IN_JUV}, // VOC_RIFLE rifle shot + {"GUN19", 4, IN_JUV}, // VOC_M60 machine gun burst -- 6 rounds + {"GUN20", 4, IN_JUV}, // VOC_GUN20 bat hitting heavy metal door + {"GUN5", 4, IN_JUV}, // VOC_M60A medium machine gun burst + {"GUN8", 4, IN_JUV}, // VOC_MINI mini gun burst + {"GUNCLIP1", 1, IN_JUV}, // VOC_RELOAD gun clip reload + {"HVYDOOR1", 5, IN_JUV}, // VOC_SLAM metal plates slamming together + {"HVYGUN10", 1, IN_JUV}, // VOC_HVYGUN10 loud sharp cannon + {"ION1", 1, IN_JUV}, // VOC_ION_CANNON partical beam + {"MGUN11", 1, IN_JUV}, // VOC_MGUN11 alternate tripple burst + {"MGUN2", 1, IN_JUV}, // VOC_MGUN2 M-16 tripple burst + {"NUKEMISL", 1, IN_JUV}, // VOC_NUKE_FIRE long missile sound + {"NUKEXPLO", 1, IN_JUV}, // VOC_NUKE_EXPLODE long but not loud explosion + {"OBELRAY1", 1, IN_JUV}, // VOC_LASER humming star wars laser beam + {"OBELPOWR", 1, IN_JUV}, // VOC_LASER_POWER warming-up sound of star wars laser beam + {"POWRDN1", 1, IN_JUV}, // VOC_RADAR_OFF doom door slide + {"RAMGUN2", 1, IN_JUV}, // VOC_SNIPER silenced rifle fire + {"ROCKET1", 1, IN_JUV}, // VOC_ROCKET1 rocket launch variation #1 + {"ROCKET2", 1, IN_JUV}, // VOC_ROCKET2 rocket launch variation #2 + {"SAMMOTR2", 1, IN_JUV}, // VOC_MOTOR dentists drill + {"SCOLD2", 1, IN_JUV}, // VOC_SCOLD cannot perform action feedback tone + {"SIDBAR1C", 1, IN_JUV}, // VOC_SIDEBAR_OPEN xylophone clink + {"SIDBAR2C", 1, IN_JUV}, // VOC_SIDEBAR_CLOSE xylophone clink + {"SQUISH2", 1, IN_JUV}, // VOC_SQUISH2 crushing infantry + {"TNKFIRE2", 1, IN_JUV}, // VOC_TANK1 sharp tank fire with recoil + {"TNKFIRE3", 1, IN_JUV}, // VOC_TANK2 sharp tank fire + {"TNKFIRE4", 1, IN_JUV}, // VOC_TANK3 sharp tank fire + {"TNKFIRE6", 1, IN_JUV}, // VOC_TANK4 big gun tank fire + {"TONE15", 0, IN_JUV}, // VOC_UP credits counting up + {"TONE16", 0, IN_JUV}, // VOC_DOWN credits counting down + {"TONE2", 1, IN_JUV}, // VOC_TARGET target sound + {"TONE5", 10, IN_JUV}, // VOC_SONAR sonar echo + {"TOSS", 1, IN_JUV}, // VOC_TOSS air swish + {"TRANS1", 1, IN_JUV}, // VOC_CLOAK stealth tank + {"TREEBRN1", 1, IN_JUV}, // VOC_BURN burning crackle + {"TURRFIR5", 1, IN_JUV}, // VOC_TURRET muffled gunfire + {"XPLOBIG4", 5, IN_JUV}, // VOC_XPLOBIG4 very long muffled explosion + {"XPLOBIG6", 5, IN_JUV}, // VOC_XPLOBIG6 very long muffled explosion + {"XPLOBIG7", 5, IN_JUV}, // VOC_XPLOBIG7 very long muffled explosion + {"XPLODE", 1, IN_JUV}, // VOC_XPLODE long soft muffled explosion + {"XPLOS", 4, IN_JUV}, // VOC_XPLOS short crunchy explosion + {"XPLOSML2", 5, IN_JUV}, // VOC_XPLOSML2 muffled mechanical explosion + + /* + ** Generic sound effects (no variations). + */ + {"NUYELL1", 10, IN_NOVAR}, // VOC_SCREAM1 short infantry scream + {"NUYELL3", 10, IN_NOVAR}, // VOC_SCREAM3 short infantry scream + {"NUYELL4", 10, IN_NOVAR}, // VOC_SCREAM4 short infantry scream + {"NUYELL5", 10, IN_NOVAR}, // VOC_SCREAM5 short infantry scream + {"NUYELL6", 10, IN_NOVAR}, // VOC_SCREAM6 short infantry scream + {"NUYELL7", 10, IN_NOVAR}, // VOC_SCREAM7 short infantry scream + {"NUYELL10", 10, IN_NOVAR}, // VOC_SCREAM10 short infantry scream + {"NUYELL11", 10, IN_NOVAR}, // VOC_SCREAM11 short infantry scream + {"NUYELL12", 10, IN_NOVAR}, // VOC_SCREAM12 short infantry scream + {"YELL1", 1, IN_NOVAR}, // VOC_YELL1 long infantry scream + + {"MYES1", 10, IN_NOVAR}, // VOC_YES "Yes?" + {"MCOMND1", 10, IN_NOVAR}, // VOC_COMMANDER "Commander?" + {"MHELLO1", 10, IN_NOVAR}, // VOC_HELLO "Hello?" + {"MHMMM1", 10, IN_NOVAR}, // VOC_HMMM "Hmmm?" +// {"MHASTE1", 10, IN_NOVAR}, // VOC_PROCEED1 "I will proceed, post haste." +// {"MONCE1", 10, IN_NOVAR}, // VOC_PROCEED2 "I will proceed, at once." +// {"MIMMD1", 10, IN_NOVAR}, // VOC_PROCEED3 "I will proceed, immediately." +// {"MPLAN1", 10, IN_NOVAR}, // VOC_EXCELLENT1 "That is an excellent plan." +// {"MPLAN2", 10, IN_NOVAR}, // VOC_EXCELLENT2 "Yes, that is an excellent plan." + {"MPLAN3", 10, IN_NOVAR}, // VOC_EXCELLENT3 "A wonderful plan." +// {"MACTION1", 10, IN_NOVAR}, // VOC_EXCELLENT4 "Astounding plan of action commander." +// {"MREMARK1", 10, IN_NOVAR}, // VOC_EXCELLENT5 "Remarkable contrivance." + {"MCOURSE1", 10, IN_NOVAR}, // VOC_OF_COURSE "Of course." + {"MYESYES1", 10, IN_NOVAR}, // VOC_YESYES "Yes yes yes." + {"MTIBER1", 10, IN_NOVAR}, // VOC_QUIP1 "Mind the Tiberium." +// {"MMG1", 10, IN_NOVAR}, // VOC_QUIP2 "A most remarkable Metasequoia Glyptostroboides." + {"MTHANKS1", 10, IN_NOVAR}, // VOC_THANKS "Thank you." + + {"CASHTURN", 1, IN_NOVAR}, // VOC_CASHTURN Sound of money being piled up. + {"BLEEP2", 10, IN_NOVAR}, // VOC_BLEEPY3 Clean computer bleep sound. + {"DINOMOUT", 10, IN_NOVAR}, // VOC_DINOMOUT Movin' out in dino-speak. + {"DINOYES", 10, IN_NOVAR}, // VOC_DINOYES Yes Sir in dino-speak. + {"DINOATK1", 10, IN_NOVAR}, // VOC_DINOATK1 Dino attack sound. + {"DINODIE1", 10, IN_NOVAR}, // VOC_DINODIE1 Dino die sound. +}; + + +/*********************************************************************************************** + * Sound_Effect -- Plays a sound effect in the tactical map. * + * * + * This routine is used when a sound effect occurs in the game world. It handles fading * + * the sound according to distance. * + * * + * INPUT: voc -- The sound effect number to play. * + * * + * coord -- The world location that the sound originates from. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/12/1994 JLB : Created. * + * 01/05/1995 JLB : Reduces sound more dramatically when off screen. * + *=============================================================================================*/ +void Sound_Effect(VocType voc, COORDINATE coord, int variation) +{ + unsigned distance; + CELL cell_pos; + int pan_value; + + if (!Options.Volume || voc == VOC_NONE || !SoundOn || SampleType == SAMPLE_NONE) { + return; + } + if (coord) { + cell_pos = Coord_Cell(coord); + } + + distance = 0xFF; + pan_value = 0; + if (coord && !Map.In_View(cell_pos)) { + distance = Map.Cell_Distance(cell_pos, Coord_Cell(Map.TacticalCoord)); + distance = (unsigned int)MIN((int)distance, (int)MAP_CELL_W); + distance = Cardinal_To_Fixed(MAP_CELL_W, distance); + distance = MIN(distance, 0xFFu); + distance ^= 0xFF; + + distance /= 2; + distance = MAX(distance, 25); + + pan_value = Cell_X(cell_pos); + pan_value -= Coord_XCell(Map.TacticalCoord) + (Lepton_To_Cell(Map.TacLeptonWidth) >> 1); + if (ABS(pan_value) > Lepton_To_Cell(Map.TacLeptonWidth >> 1)) { + pan_value *= 0x8000; + pan_value /= (MAP_CELL_W >> 2); + pan_value = Bound(pan_value, -0x7FFF, 0x7FFF); +// pan_value = MAX((int)pan_value, (int)-0x7FFF); +// pan_value = MIN((int)pan_value, 0x7FFF); + } else { + pan_value = 0; + } + } + + Sound_Effect(voc, (VolType)Fixed_To_Cardinal(distance, Options.Volume), variation, pan_value); +} + + +/*********************************************************************************************** + * Sound_Effect -- General purpose sound player. * + * * + * This is used for general purpose sound effects. These are sounds that occur outside * + * of the game world. They do not have a corresponding game world location as their source. * + * * + * INPUT: voc -- The sound effect number to play. * + * * + * volume -- The volume to assign to this sound effect. * + * * + * OUTPUT: Returns with the sound handle (-1 if no sound was played). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/12/1994 JLB : Created. * + * 11/12/1994 JLB : Handles cache logic. * + * 05/04/1995 JLB : Variation adjustments. * + *=============================================================================================*/ +int Sound_Effect(VocType voc, VolType volume, int variation, signed short pan_value) +{ + char name[_MAX_FNAME+_MAX_EXT]; // Working filename of sound effect. + + if (!Options.Volume || voc == VOC_NONE || !SoundOn || SampleType == SAMPLE_NONE) { + return(-1); + } + + /* + ** Fetch a pointer to the sound effect data. Modify the sound as appropriate and desired. + */ + char const * ext = ".AUD"; + if (Special.IsJuvenile && SoundEffectName[voc].Where == IN_JUV) { + ext = ".JUV"; + } else { + if (SoundEffectName[voc].Where == IN_VAR) { + + /* + ** For infantry, use a variation on the response. For vehicles, always + ** use the vehicle response table. + */ + if (variation < 0) { + if (ABS(variation) % 2) { + ext = ".V00"; + } else { + ext = ".V02"; + } + } else { + if (variation % 2) { + ext = ".V01"; + } else { + ext = ".V03"; + } + } + } + } + _makepath(name, NULL, NULL, SoundEffectName[voc].Name, ext); + void const * ptr = MixFileClass::Retrieve(name); + + /* + ** If the sound data pointer is not null, then presume that it is valid. + */ + if (ptr) { + return(Play_Sample(ptr, Fixed_To_Cardinal(SoundEffectName[voc].Priority, (int)volume), (int)volume, pan_value)); + } + return(-1); +} + + +/* +** This elaborates all the EVA speech voices. +*/ +char const * Speech[VOX_COUNT] = { + "ACCOM1", // mission accomplished + "FAIL1", // your mission has failed + "BLDG1", // unable to comply, building in progress + "CONSTRU1", // construction complete + "UNITREDY", // unit ready + "NEWOPT1", // new construction options + "DEPLOY1", // cannot deploy here + "GDIDEAD1", // GDI unit destroyed + "NODDEAD1", // Nod unit destroyed + "CIVDEAD1", // civilian killed +// "EVAYES1", // affirmative +// "EVANO1", // negative +// "UPUNIT1", // upgrade complete, new unit available +// "UPSTRUC1", // upgrade complete, new structure available + "NOCASH1", // insufficient funds + "BATLCON1", // battle control terminated + "REINFOR1", // reinforcements have arrived + "CANCEL1", // canceled + "BLDGING1", // building + "LOPOWER1", // low power + "NOPOWER1", // insufficient power + "MOCASH1", // need more funds + "BASEATK1", // our base is under attack + "INCOME1", // incoming missile + "ENEMYA", // enemy planes approaching + "NUKE1", // nuclear warhead approaching +// "RADOK1", // radiation levels are acceptable +// "RADFATL1", // radiation levels are fatal + "NOBUILD1", // unable to build more + "PRIBLDG1", // primary building selected +// "REPDONE1", // repairs completed + "NODCAPT1", // Nod building captured + "GDICAPT1", // GDI building captured +// "SOLD1", // structure sold + "IONCHRG1", // ion cannon charging + "IONREDY1", // ion cannon ready + "NUKAVAIL", // nuclear weapon available + "NUKLNCH1", // nuclear weapon launched + "UNITLOST", // unit lost + "STRCLOST", // structure lost + "NEEDHARV", // need harvester + "SELECT1", // select target + "AIRREDY1", // airstrike ready + "NOREDY1", // not ready + "TRANSSEE", // Nod transport sighted + "TRANLOAD", // Nod transport loaded + "ENMYAPP1", // enemy approaching + "SILOS1", // silos needed + "ONHOLD1", // on hold + "REPAIR1", // repairing + "ESTRUCX", // enemy structure destroyed + "GSTRUC1", // GDI structure destroyed + "NSTRUC1", // NOD structure destroyed + "ENMYUNIT", // Enemy unit destroyed +// "GUKILL1", // gold unit destroyed +// "GSTRUD1", // gold structure destroyed +// "GONLINE1", // gold player online +// "GLEFT1", // gold player has departed +// "GOLDKILT", // gold player destroyed +// "GOLDWIN", // gold player is victorious +// "RUKILL1", // red unit destroyed +// "RSTRUD1", // red structure destroyed +// "RONLINE1", // red player online +// "RLEFT1", // red player has departed +// "REDKILT", // red player destroyed +// "REDWIN", // red player is victorious +// "GYUKILL1", // grey unit destroyed +// "GYSTRUD1", // grey structure destroyed +// "GYONLINE", // grey player online +// "GYLEFT1", // grey player has departed +// "GREYKILT", // grey player destroyed +// "GREYWIN", // grey player is victorious +// "OUKILL1", // orange unit destroyed +// "OSTRUD1", // orange structure destroyed +// "OONLINE1", // orange player online +// "OLEFT1", // orange player has departed +// "ORANKILT", // orange player destroyed +// "ORANWIN", // orange player is victorious +// "GNUKILL1", // green unit destroyed +// "GNSTRUD1", // green structure destroyed +// "GNONLINE", // green player online +// "GNLEFT1", // green player has departed +// "GRENKILT", // green player destroyed +// "GRENWIN", // green player is victorious +// "BUKILL1", // blue unit destroyed +// "BSTRUD1", // blue structure destroyed +// "BONLINE1", // blue player online +// "BLEFT1", // blue player has departed +// "BLUEKILT", // blue player destroyed +// "BLUEWIN" // blue player is victorious +}; +static VoxType CurrentVoice = VOX_NONE; + + +/*********************************************************************************************** + * Speak -- Computer speaks to the player. * + * * + * This routine is used to have the game computer (EVA) speak to the player. * + * * + * INPUT: voice -- The voice number to speak (see defines.h). * + * * + * OUTPUT: Returns with the handle of the playing speech (-1 if no voice started). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/12/1994 JLB : Created. * + *=============================================================================================*/ +void Speak(VoxType voice) +{ + if (Options.Volume && SampleType != 0 && voice != VOX_NONE && voice != SpeakQueue && voice != CurrentVoice && SpeakQueue == VOX_NONE) { + SpeakQueue = voice; + } +} + + +/*********************************************************************************************** + * Speak_AI -- Handles starting the EVA voices. * + * * + * This starts the EVA voice talking as well. If there is any speech request in the queue, * + * it will be started when the current voice is finished. Call this routine as often as * + * possible (once per game tick is sufficient). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +void Speak_AI(void) +{ + static VoxType _last = VOX_NONE; + if (SampleType == 0) return; + + if (!Is_Sample_Playing(SpeechBuffer)) { + CurrentVoice = VOX_NONE; + if (SpeakQueue != VOX_NONE) { + if (SpeakQueue != _last) { + char name[_MAX_FNAME+_MAX_EXT]; + + _makepath(name, NULL, NULL, Speech[SpeakQueue], ".AUD"); + if (CCFileClass(name).Read(SpeechBuffer, SPEECH_BUFFER_SIZE)) { + Play_Sample(SpeechBuffer, 254, Options.Volume); + } + _last = SpeakQueue; + } else { + Play_Sample(SpeechBuffer, 254, Options.Volume); + } + SpeakQueue = VOX_NONE; + } + } +} + + +/*********************************************************************************************** + * Stop_Speaking -- Forces the EVA voice to stop talking. * + * * + * Use this routine to immediately stop the EVA voice from speaking. It also clears out * + * the pending voice queue. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +void Stop_Speaking(void) +{ + SpeakQueue = VOX_NONE; + if (SampleType != 0) { + Stop_Sample_Playing(SpeechBuffer); + } +} + + +/*********************************************************************************************** + * Is_Speaking -- Checks to see if the eva voice is still playing. * + * * + * Call this routine when the EVA voice being played needs to be checked. A typical use * + * of this would be when some action needs to be delayed until the voice has finished -- * + * say the end of the game. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the EVA voice still playing? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/12/1995 JLB : Created. * + *=============================================================================================*/ +bool Is_Speaking(void) +{ + Speak_AI(); + if (SampleType != 0 && (SpeakQueue != VOX_NONE || Is_Sample_Playing(SpeechBuffer))) { + return(true); + } + return(false); +} diff --git a/AUDIO.H b/AUDIO.H new file mode 100644 index 0000000..70db62d --- /dev/null +++ b/AUDIO.H @@ -0,0 +1,99 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\audio.h_v 2.18 16 Oct 1995 16:45:34 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : AUDIO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 21, 1994 * + * * + * Last Update : June 21, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef AUDIO_H +#define AUDIO_H + +#include "memory.h" + +class AudioClass { + char const * Name; // Name of audio asset. + void const * Data; // Loaded audio data. + int Handle; // Handle of asset (as it is playing). + MemoryClass *Mem; // Pointer to memory handler class. + unsigned IsMIDI:1; // Is this a midi file? + + public: + AudioClass(void); + AudioClass(char const *name, MemoryClass &mem); + virtual ~AudioClass(void); + + bool Load(char const *name = 0); + bool Free(void); + bool Play(int volume = 0xFF); + bool Stop(void); + bool Pause(void); + bool Resume(void); + bool Set_Name(char const *name); + bool Is_Playing(void) const; + bool Is_Loaded(void) const; + bool Is_MIDI(void) const; +}; + +inline AudioClass::AudioClass(void) +{ + Name = 0; + Data = 0; + Mem = 0; + Handle = -1; +}; + +inline AudioClass::AudioClass(char const *name, MemoryClass &mem) +{ + if (mem) { + Mem = &mem; + } else { + Mem = &::Mem; // Uses global default memory handler. + } + Name = strdup(name); + Data = 0; + Handle = -1; +}; + +inline AudioClass::~AudioClass(void) +{ + if (GameActive) { + if (Name) free(Name); + if (Data) Mem->Free(Data); + Name = 0; + Data = 0; + Handle = -1; + } +}; + + +#endif diff --git a/BASE.CPP b/BASE.CPP new file mode 100644 index 0000000..6e84169 --- /dev/null +++ b/BASE.CPP @@ -0,0 +1,524 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\base.cpv 1.9 16 Oct 1995 16:48:56 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BASE.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : 03/27/95 * + * * + * Last Update : March 27, 1995 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BaseClass::Get_Building -- Returns ptr to the built building for the given node * + * BaseClass::Get_Node -- Returns ptr to the node corresponding to given object * + * BaseClass::Is_Built -- Tells if given item in the list has been built yet * + * BaseClass::Is_Node -- Tells if the given building is part of our base list * + * BaseClass::Load -- loads from a saved game file * + * BaseClass::Next_Buildable -- returns ptr to the next node that needs to be built * + * BaseClass::Read_INI -- INI reading routine * + * BaseClass::Save -- saves to a saved game file * + * BaseClass::Write_INI -- INI writing routine * + * BaseNodeClass::operator != -- inequality operator * + * BaseNodeClass::operator == -- equality operator * + * BaseNodeClass::operator > -- greater-than operator * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * BaseNodeClass::operator == -- equality operator * + * * + * INPUT: * + * node node to test against * + * * + * OUTPUT: * + * true = equal, false = not equal * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +int BaseNodeClass::operator == (BaseNodeClass const & node) +{ + return(Type == node.Type && Coord == node.Coord); +} + + +/*********************************************************************************************** + * BaseNodeClass::operator != -- inequality operator * + * * + * INPUT: * + * node node to test against * + * * + * OUTPUT: * + * comparison result * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +int BaseNodeClass::operator !=(BaseNodeClass const & node) +{ + return(Type != node.Type || Coord != node.Coord); +} + + +/*********************************************************************************************** + * BaseNodeClass::operator > -- greater-than operator * + * * + * INPUT: * + * node node to test against * + * * + * OUTPUT: * + * comparison result * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +int BaseNodeClass::operator > (BaseNodeClass const & ) +{ + return(true); +} + + +/*********************************************************************************************** + * BaseClass::Read_INI -- INI reading routine * + * * + * INI entry format: * + * BLDG=COORD * + * BLDG=COORD * + * ... * + * * + * INPUT: * + * buffer pointer to loaded INI file * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * This routines assumes there is only one base defined for the scenario. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +void BaseClass::Read_INI(char *buffer) +{ + char buf[128]; + char uname[10]; + BaseNodeClass node; // node to add to list + + /* + ** First, determine the house of the human player, and set the Base's house + ** accordingly. + */ + WWGetPrivateProfileString("BASIC", "Player", "GoodGuy", buf, 20, buffer); + if (HouseTypeClass::From_Name(buf) == HOUSE_GOOD) { + House = HOUSE_BAD; + } else { + House = HOUSE_GOOD; + } + + /* + ** Read the number of buildings that will go into the base node list + */ + int count = WWGetPrivateProfileInt (INI_Name(),"Count",0,buffer); + + /* + ** Read each entry in turn, in the same order they were written out. + */ + for (int i = 0; i < count; i++) { + + /* + ** Get an INI entry + */ + sprintf(uname,"%03d",i); + WWGetPrivateProfileString(INI_Name(), uname, NULL, buf, sizeof(buf)-1, buffer); + + /* + ** Set the node's building type + */ + node.Type = BuildingTypeClass::From_Name(strtok(buf,",")); + + /* + ** Read & set the node's coordinate + */ + node.Coord = atol(strtok(NULL,",")); + + /* + ** Add this node to the Base's list + */ + Nodes.Add(node); + } +} + + +/*********************************************************************************************** + * BaseClass::Write_INI -- INI writing routine * + * * + * INI entry format: * + * BLDG=COORD * + * BLDG=COORD * + * ... * + * * + * INPUT: * + * buffer pointer to loaded INI file staging area * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * This routines assumes there is only one base defined for the scenario. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +void BaseClass::Write_INI(char *buffer) +{ + char buf[128]; + char uname[10]; + + /* + ** Clear out all existing teamtype data from the INI file. + */ + WWWritePrivateProfileString(INI_Name(), NULL, NULL, buffer); + + /* + ** Save the # of buildings in the Nodes list. This is essential because + ** they must be read in the same order they were created, so "000" must be + ** read first, etc. + */ + WWWritePrivateProfileInt (INI_Name(),"Count",Nodes.Count(),buffer); + + /* + ** Write each entry into the INI + */ + for (int i = 0; i < Nodes.Count(); i++) { + sprintf(uname,"%03d",i); + sprintf(buf,"%s,%d", + BuildingTypeClass::As_Reference(Nodes[i].Type).IniName, + Nodes[i].Coord); + + WWWritePrivateProfileString(INI_Name(), uname, buf, buffer); + } +} + + +/*********************************************************************************************** + * BaseClass::Load -- loads from a saved game file * + * * + * INPUT: * + * file open file * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +bool BaseClass::Load(FileClass &file) +{ + int num_struct; + int i; + BaseNodeClass node; + + /* + ** Read in & check the size of this class + */ + if (file.Read(&i, sizeof(i)) != sizeof(i)) { + return(false); + } + + if (i != sizeof(*this)) { + return(false); + } + + /* + ** Read in the House & the number of structures in the base + */ + if (file.Read(&House,sizeof(House)) != sizeof(House)) { + return(false); + } + + if (file.Read(&num_struct,sizeof(num_struct)) != sizeof(num_struct)) { + return(false); + } + + /* + ** Read each node entry & add it to the list + */ + for (i = 0; i < num_struct; i++) { + if (file.Read(&node,sizeof(node)) != sizeof(node)) { + return(false); + } + Nodes.Add(node); + } + + return(true); +} + + +/*********************************************************************************************** + * BaseClass::Save -- saves to a saved game file * + * * + * INPUT: * + * file open file * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +bool BaseClass::Save(FileClass &file) +{ + int num_struct; + int i; + BaseNodeClass node; + + /* + ** Write the size of this class + */ + i = sizeof(*this); + if (file.Write(&i,sizeof(i)) != sizeof(i)) { + return(false); + } + + /* + ** Write the House & the number of structures in the base + */ + if (file.Write(&House,sizeof(House)) != sizeof(House)) { + return(false); + } + + num_struct = Nodes.Count(); + if (file.Write(&num_struct,sizeof(num_struct)) != sizeof(num_struct)) { + return(false); + } + + /* + ** Write each node entry + */ + for (i = 0; i < num_struct; i++) { + node = Nodes[i]; + if (file.Write(&node,sizeof(node)) != sizeof(node)) { + return(false); + } + } + + return(true); +} + + +/*********************************************************************************************** + * BaseClass::Is_Built -- Tells if given item in the list has been built yet * + * * + * INPUT: * + * index index into base list * + * * + * OUTPUT: * + * true = yes, false = no * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +bool BaseClass::Is_Built(int index) +{ + if (Get_Building(index) != NULL) { + return(true); + } else { + return(false); + } +} + + +/*********************************************************************************************** + * BaseClass::Get_Building -- Returns ptr to the built building for the given node * + * * + * INPUT: * + * obj pointer to building to test * + * * + * OUTPUT: * + * ptr to already-built building, NULL if none * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +BuildingClass * BaseClass::Get_Building(int index) +{ + BuildingClass *bldg; + ObjectClass *obj[4]; + + /* + ** Check the location on the map where this building should be; if it's + ** there, return a pointer to it. + */ + CELL cell = Coord_Cell(Nodes[index].Coord); + + obj[0] = Map[cell].Cell_Building(); + obj[1] = Map[cell].Overlapper[0]; + obj[2] = Map[cell].Overlapper[1]; + obj[3] = Map[cell].Overlapper[2]; + + bldg = NULL; + for (int i = 0; i < 4; i++) { + if (obj[i] && + obj[i]->Coord == Nodes[index].Coord && + obj[i]->What_Am_I() == RTTI_BUILDING && + ((BuildingClass *)obj[i])->Class->Type == Nodes[index].Type) { + + bldg = (BuildingClass *)obj[i]; + break; + } + } + + return(bldg); +} + + +/*********************************************************************************************** + * BaseClass::Is_Node -- Tells if the given building is part of our base list * + * * + * INPUT: * + * obj pointer to building to test * + * * + * OUTPUT: * + * true = building is a node in the list, false = isn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +bool BaseClass::Is_Node(BuildingClass *obj) +{ + if (Get_Node(obj) != NULL) { + return(true); + } else { + return(false); + } +} + + +/*********************************************************************************************** + * BaseClass::Get_Node -- Returns ptr to the node corresponding to given object * + * * + * INPUT: * + * obj pointer to building to test * + * * + * OUTPUT: * + * ptr to node * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +BaseNodeClass * BaseClass::Get_Node(BuildingClass *obj) +{ + for (int i = 0; i < Nodes.Count(); i++) { + if (obj->Class->Type == Nodes[i].Type && obj->Coord == Nodes[i].Coord) { + return(&Nodes[i]); + } + } + return(NULL); +} + + +/*********************************************************************************************** + * BaseClass::Next_Buildable -- returns ptr to the next node that needs to be built * + * * + * If 'type' is not NONE, returns ptr to the next "hole" in the list of the given type. * + * Otherwise, returns ptr to the next hole in the list of any type. * + * * + * INPUT: * + * type type of building to check for * + * * + * OUTPUT: * + * ptr to a BaseNodeClass, NULL if none * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +BaseNodeClass * BaseClass::Next_Buildable(StructType type) +{ + /* + ** Loop through all node entries, returning a pointer to the first + ** un-built one that matches the requested type. + */ + for (int i = 0; i < Nodes.Count(); i++) { + + /* + ** For STRUCT_NONE, return the first hole found + */ + if (type == STRUCT_NONE) { + if (!Is_Built(i)) { + return(&Nodes[i]); + } + + } else { + + /* + ** For a "real" building type, return the first hold for that type + */ + if (Nodes[i].Type==type && !Is_Built(i)) { + return(&Nodes[i]); + } + } + } + + +// If no entry could be found, then create a fake one that will allow +// placement of the building. Make it static and reuse the next time this +// routine is called. + + return(NULL); +} diff --git a/BASE.H b/BASE.H new file mode 100644 index 0000000..83efb3b --- /dev/null +++ b/BASE.H @@ -0,0 +1,127 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\base.h_v 1.12 16 Oct 1995 16:46:38 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BASE.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : 03/27/95 * + * * + * Last Update : March 27, 1995 * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BASE_H +#define BASE_H + + +/**************************************************************************** +** This class defines one "node" in the pre-built base list. Each node +** contains a type of building to build, and the COORD to build it at. +*/ +class BaseNodeClass +{ + public: + BaseNodeClass(void) {}; + int operator == (BaseNodeClass const & node); + int operator != (BaseNodeClass const & node); + int operator > (BaseNodeClass const & node); + + StructType Type; + COORDINATE Coord; +}; + + +/**************************************************************************** +** This is the class that defines a pre-built base for the computer AI. +** (Despite its name, this is NOT the "base" class for C&C's class hierarchy!) +*/ +class BaseClass +{ + public: + + /********************************************************************** + ** Constructor/Destructor + */ + BaseClass(void) {}; + virtual ~BaseClass() {Nodes.Clear();} + + /********************************************************************** + ** Initialization + */ + void Init(void) {Nodes.Clear();} + + /********************************************************************** + ** The standard suite of load/save support routines + */ + void Read_INI(char *buffer); + void Write_INI(char *buffer); + static char *INI_Name(void) {return "Base";} + bool Load(FileClass & file); + bool Save(FileClass & file); + virtual void Code_Pointers(void) {}; + virtual void Decode_Pointers(void) {}; + + /********************************************************************** + ** Tells if the given node has been built or not + */ + bool Is_Built(int index); + + /********************************************************************** + ** Returns a pointer to the object for the given node + */ + BuildingClass * Get_Building(int index); + + /********************************************************************** + ** Tells if the given building ptr is a node in this base's list. + */ + bool Is_Node (BuildingClass *obj); + + /********************************************************************** + ** Returns a pointer to the requested node. + */ + BaseNodeClass * Get_Node(BuildingClass *obj); + BaseNodeClass * Get_Node(int index) { return (&Nodes[index]); } + + /********************************************************************** + ** Returns a pointer to the next "hole" in the Nodes list. + */ + BaseNodeClass * Next_Buildable(StructType type = STRUCT_NONE); + + /********************************************************************** + ** This is the list of "nodes" that define the base. Portions of this + ** list can be pre-built by simply saving those buildings in the INI + ** along with non-base buildings, so Is_Built will return true for them. + */ + DynamicVectorClass Nodes; + + /********************************************************************** + ** This is the house this base belongs to. + */ + HousesType House; +}; + + +#endif diff --git a/BBDATA.CPP b/BBDATA.CPP new file mode 100644 index 0000000..d4ca730 --- /dev/null +++ b/BBDATA.CPP @@ -0,0 +1,608 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\bbdata.cpv 2.17 16 Oct 1995 16:49:46 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BBDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 23, 1994 * + * * + * Last Update : October 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BulletTypeClass::BulletTypeClass -- Constructor for bullet type objects. * + * BulletTypeClass::Load_Shapes -- Load shape data for bullet types. * + * BulletTypeClass::One_Time -- Performs the one time processing for bullets. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/*************************************************************************** +** Detailed information about each class of bullet (projectile) in the game. +*/ +static BulletTypeClass const ClassSniper( + BULLET_BULLET, + "50cal", // NAME: Text name of this unit type. + false, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + true, // Is this projectile invisible? + false, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + false, // Can it run out of fuel? + true, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 0, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_LIGHT_SPEED, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_HOLLOW_POINT, // WARHEAD: If fires weapon, warhead type + ANIM_PIFF // Explosion to use upon impact. +); + +static BulletTypeClass const ClassBullet( + BULLET_BULLET, + "50cal", // NAME: Text name of this unit type. + false, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + true, // Is this projectile invisible? + false, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + false, // Can it run out of fuel? + true, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 0, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_LIGHT_SPEED, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_SA, // WARHEAD: If fires weapon, warhead type + ANIM_PIFF // Explosion to use upon impact. +); + +static BulletTypeClass const ClassSpreadfire( + BULLET_SPREADFIRE, + "50cal", // NAME: Text name of this unit type. + true, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + true, // Is this projectile invisible? + false, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + false, // Can it run out of fuel? + true, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 0, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_LIGHT_SPEED, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_HE, // WARHEAD: If fires weapon, warhead type + ANIM_PIFFPIFF // Explosion to use upon impact. +); + +static BulletTypeClass const ClassAPDS( + BULLET_APDS, + "120mm", // NAME: Text name of this unit type. + false, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + false, // Is this projectile invisible? + false, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + false, // Can it run out of fuel? + true, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 0, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_VERY_FAST, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_AP, // WARHEAD: If fires weapon, warhead type + ANIM_VEH_HIT3 // Explosion to use upon impact. +); + +static BulletTypeClass const Class120mm( + BULLET_HE, + "120mm", // NAME: Text name of this unit type. + true, // Flies over tall walls? + false, // Homes in on target? + true, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + false, // Is this projectile invisible? + false, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + false, // Can it run out of fuel? + true, // Is there no visual difference between projectile facings? + true, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 0, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_MEDIUM_FAST, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_HE, // WARHEAD: If fires weapon, warhead type + ANIM_ART_EXP1 // Explosion to use upon impact. +); + +static BulletTypeClass const ClassMissile( + BULLET_SSM, + "DRAGON", // NAME: Text name of this unit type. + true, // Flies over tall walls? + true, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + false, // Is this projectile invisible? + true, // Will it blow up even if it gets just NEAR to target? + true, // Does it have flickering flame animation? + true, // Can it run out of fuel? + false, // Is there no visual difference between projectile facings? + true, // Is projectile inherently inaccurate? + true, // Translucent colors are used? + true, // Good against aircraft? + 7, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_ROCKET, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + WARHEAD_HE, // WARHEAD: If fires weapon, warhead type + ANIM_FRAG1 // Explosion to use upon impact. +); + +static BulletTypeClass const ClassMissile2( + BULLET_SSM2, + "DRAGON", // NAME: Text name of this unit type. + true, // Flies over tall walls? + true, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + false, // Is this projectile invisible? + true, // Will it blow up even if it gets just NEAR to target? + true, // Does it have flickering flame animation? + true, // Can it run out of fuel? + false, // Is there no visual difference between projectile facings? + true, // Is projectile inherently inaccurate? + true, // Translucent colors are used? + true, // Good against aircraft? + 9, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_ROCKET, // SPEED: Miles per hour. + 7, // ROT: Rate of turn (degrees per tick). + WARHEAD_HE, // WARHEAD: If fires weapon, warhead type + ANIM_FRAG1 // Explosion to use upon impact. +); + +static BulletTypeClass const ClassPatriot( + BULLET_SAM, + "MISSILE", // NAME: Text name of this unit type. + true, // Flies over tall walls? + true, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + false, // Is this projectile invisible? + true, // Will it blow up even if it gets just NEAR to target? + true, // Does it have flickering flame animation? + true, // Can it run out of fuel? + false, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + true, // Good against aircraft? + 0, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_VERY_FAST, // SPEED: Miles per hour. + 10, // ROT: Rate of turn (degrees per tick). + WARHEAD_AP, // WARHEAD: If fires weapon, warhead type + ANIM_VEH_HIT1 // Explosion to use upon impact. +); + +static BulletTypeClass const ClassDragon( + BULLET_TOW, + "DRAGON", // NAME: Text name of this unit type. + true, // Flies over tall walls? + true, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + false, // Is this projectile invisible? + true, // Will it blow up even if it gets just NEAR to target? + true, // Does it have flickering flame animation? + true, // Can it run out of fuel? + false, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + true, // Translucent colors are used? + true, // Good against aircraft? + 3, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_ROCKET, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + WARHEAD_AP, // WARHEAD: If fires weapon, warhead type + ANIM_VEH_HIT2 // Explosion to use upon impact. +); + +static BulletTypeClass const ClassFlame( + BULLET_FLAME, + "FLAME", // NAME: Text name of this unit type. + false, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + true, // Is this projectile invisible? + false, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + true, // Can it run out of fuel? + false, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 12, // ARMING: Time to arm projectile after launch. + 12, // RANGE: Inherent override range factor. + MPH_FAST, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_FIRE, // WARHEAD: If fires weapon, warhead type + ANIM_NONE // Explosion to use upon impact. +); + +static BulletTypeClass const ClassChem( + BULLET_CHEMSPRAY, + "FLAME", // NAME: Text name of this unit type. + false, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + true, // Is this projectile invisible? + false, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + true, // Can it run out of fuel? + false, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 12, // ARMING: Time to arm projectile after launch. + 12, // RANGE: Inherent override range factor. + MPH_FAST, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_HE, // WARHEAD: If fires weapon, warhead type + ANIM_NONE // Explosion to use upon impact. +); + +static BulletTypeClass const ClassNapalm( + BULLET_NAPALM, + "BOMBLET", // NAME: Text name of this unit type. + true, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + true, // Is this a dropping bomb-like object? + false, // Is this projectile invisible? + false, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + false, // Can it run out of fuel? + true, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + true, // Translucent colors are used? + false, // Good against aircraft? + 24, // ARMING: Time to arm projectile after launch. + 24, // RANGE: Inherent override range factor. + MPH_MEDIUM_SLOW, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_FIRE, // WARHEAD: If fires weapon, warhead type + ANIM_NAPALM2 // Explosion to use upon impact. +); + +static BulletTypeClass const ClassGrenade( + BULLET_GRENADE, + "BOMB", // NAME: Text name of this unit type. + true, // Flies over tall walls? + false, // Homes in on target? + true, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + false, // Is this projectile invisible? + false, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + false, // Can it run out of fuel? + true, // Is there no visual difference between projectile facings? + true, // Is projectile inherently inaccurate? + true, // Translucent colors are used? + false, // Good against aircraft? + 0, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_MEDIUM_SLOW, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_HE, // WARHEAD: If fires weapon, warhead type + ANIM_VEH_HIT2 // Explosion to use upon impact. +); + +static BulletTypeClass const ClassLaser( + BULLET_LASER, + "Laser", // NAME: Text name of this unit type. + true, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + true, // Is this projectile invisible? + false, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + false, // Can it run out of fuel? + true, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 0, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_LIGHT_SPEED, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_LASER, // WARHEAD: If fires weapon, warhead type + ANIM_NONE // Explosion to use upon impact. +); + +static BulletTypeClass const ClassNukeUp( + BULLET_NUKE_UP, + "ATOMICUP", // NAME: Text name of this unit type. + true, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + false, // Is this projectile invisible? + true, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + false, // Can it run out of fuel? + true, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 0, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_VERY_FAST, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_HE, // WARHEAD: If fires weapon, warhead type + ANIM_FRAG1 // Explosion to use upon impact. +); + +static BulletTypeClass const ClassNukeDown( + BULLET_NUKE_DOWN, + "ATOMICDN", // NAME: Text name of this unit type. + true, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + false, // Is this projectile invisible? + true, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + false, // Can it run out of fuel? + true, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 0, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_VERY_FAST, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_HE, // WARHEAD: If fires weapon, warhead type + ANIM_ATOM_BLAST // Explosion to use upon impact. +); + +static BulletTypeClass const ClassHonestJohn( + BULLET_HONEST_JOHN, + "MISSILE", // NAME: Text name of this unit type. + true, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + false, // Is this projectile invisible? + true, // Will it blow up even if it gets just NEAR to target? + true, // Does it have flickering flame animation? + true, // Can it run out of fuel? + false, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 10, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_FAST, // SPEED: Miles per hour. + 10, // ROT: Rate of turn (degrees per tick). + WARHEAD_FIRE, // WARHEAD: If fires weapon, warhead type + ANIM_NAPALM3 // Explosion to use upon impact. +); + +static BulletTypeClass const ClassHeadButt( + BULLET_HEADBUTT, + "GORE", // NAME: Text name of this unit type. + false, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + true, // Is this projectile invisible? + false, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + false, // Can it run out of fuel? + true, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 0, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_LIGHT_SPEED, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_HEADBUTT, // WARHEAD: If fires weapon, warhead type + ANIM_NONE // Explosion to use upon impact. +); + +static BulletTypeClass const ClassTRexBite( + BULLET_TREXBITE, + "CHEW", // NAME: Text name of this unit type. + false, // Flies over tall walls? + false, // Homes in on target? + false, // Projectile arcs to the target? + false, // Is this a dropping bomb-like object? + true, // Is this projectile invisible? + false, // Will it blow up even if it gets just NEAR to target? + false, // Does it have flickering flame animation? + false, // Can it run out of fuel? + true, // Is there no visual difference between projectile facings? + false, // Is projectile inherently inaccurate? + false, // Translucent colors are used? + false, // Good against aircraft? + 0, // ARMING: Time to arm projectile after launch. + 0, // RANGE: Inherent override range factor. + MPH_LIGHT_SPEED, // SPEED: Miles per hour. + 0, // ROT: Rate of turn (degrees per tick). + WARHEAD_FEEDME, // WARHEAD: If fires weapon, warhead type + ANIM_NONE // Explosion to use upon impact. +); + + +/* +** This is the array of pointers to the static data associated with +** each bullet (projectile) type. +*/ +BulletTypeClass const * const BulletTypeClass::Pointers[BULLET_COUNT] = { + &ClassSniper, // BULLET_SNIPER + &ClassBullet, // BULLET_BULLET + &ClassAPDS, // BULLET_APDS + &Class120mm, // BULLET_HE + &ClassMissile, // BULLET_SSM + &ClassMissile2, // BULLET_SSM2 + &ClassPatriot, // BULLET_SAM + &ClassDragon, // BULLET_TOW + &ClassFlame, // BULLET_FLAME + &ClassChem, // BULLET_CHEMSPRAY + &ClassNapalm, // BULLET_NAPALM + &ClassGrenade, // BULLET_GRENADE + &ClassLaser, // BULLET_LASER + &ClassNukeUp, // BULLET_NUKE_UP + &ClassNukeDown, // BULLET_NUKE_DOWN + &ClassHonestJohn, // BULLET_HONEST_JOHN + &ClassSpreadfire, // BULLET_SPREADFIRE + &ClassHeadButt, // BULLET_HEADBUTT + &ClassTRexBite, // BULLET_TREXBITE +}; + + +/*********************************************************************************************** + * BulletTypeClass::BulletTypeClass -- Constructor for bullet type objects. * + * * + * This is basically a constructor for static type objects used by bullets. All bullets * + * are of a type constructed by this routine at game initialization time. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +BulletTypeClass::BulletTypeClass( + BulletType type, + char const *ininame, + bool is_high, + bool is_homing, + bool is_arcing, + bool is_dropping, + bool is_invisible, + bool is_proximity_armed, + bool is_flame_equipped, + bool is_fueled, + bool is_faceless, + bool is_inaccurate, + bool is_translucent, + bool is_antiaircraft, + int arming, int range, MPHType maxspeed, unsigned rot, + WarheadType warhead, AnimType explosion) : + ObjectTypeClass(true, false, false, true, false, false, true, true, TXT_NONE, ininame, ARMOR_NONE, 0) +{ + Explosion = explosion; + IsHigh = is_high; + IsAntiAircraft = is_antiaircraft; + IsTranslucent = is_translucent; + IsArcing = is_arcing; + IsHoming = is_homing; + IsDropping = is_dropping; + IsInvisible = is_invisible; + IsProximityArmed = is_proximity_armed; + IsFlameEquipped = is_flame_equipped; + IsFueled = is_fueled; + IsFaceless = is_faceless; + IsInaccurate = is_inaccurate; + Type = type; + Warhead = warhead; + MaxSpeed = maxspeed; + ROT = rot; + Arming = arming; + Range = range; +} + + +/*********************************************************************************************** + * BulletTypeClass::One_Time -- Performs the one time processing for bullets. * + * * + * This routine is used to perform any one time processing for the bullet type class. It * + * handles loading of the shape files. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine must be called before any rendering of bullets occurs and should * + * only be called once. * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void BulletTypeClass::One_Time(void) +{ + BulletType index; + + /* + ** Load the bullet shapes. + */ + for (index = BULLET_FIRST; index < BULLET_COUNT; index++) { + BulletTypeClass const & bullet = As_Reference(index); + char fullname[_MAX_FNAME+_MAX_EXT]; + + if (!bullet.IsInvisible) { + _makepath(fullname, NULL, NULL, bullet.IniName, ".SHP"); + + RawFileClass file(fullname); + + if (file.Is_Available()) { + ((void const *&)bullet.ImageData) = Load_Alloc_Data(file); + } else { + ((void const *&)bullet.ImageData) = MixFileClass::Retrieve(fullname); + } + } + } +} + + diff --git a/BDATA.CPP b/BDATA.CPP new file mode 100644 index 0000000..04e2b93 --- /dev/null +++ b/BDATA.CPP @@ -0,0 +1,4938 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\bdata.cpv 2.17 16 Oct 1995 16:50:08 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : July 17, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BuildingTypeClass::As_Reference -- Fetches reference to the building type specified. * + * BuildingTypeClass::Bib_And_Offset -- Determines the bib and appropriate cell offset. * + * BuildingTypeClass::BuildingTypeClass -- This is the constructor for the building types. * + * BuildingTypeClass::Create_And_Place -- Creates and places a building object onto the map. * + * BuildingTypeClass::Create_One_Of -- Creates a building of this type. * + * BuildingTypeClass::Dimensions -- Fetches the pixel dimensions of the building. * + * BuildingTypeClass::Display -- Renders a generic view of building. * + * BuildingTypeClass::Full_Name -- Fetches the full name text number. * + * BuildingTypeClass::Height -- Determins the height of the building in icons. * + * BuildingTypeClass::Init -- Performs theater specific initialization. * + * BuildingTypeClass::Init_Anim -- Initialize an animation control for a building. * + * BuildingTypeClass::Legal_Placement -- Determines if building can be legally placed at pos.* + * BuildingTypeClass::Max_Pips -- Determines the maximum pips to display. * + * BuildingTypeClass::Occupy_List -- Fetches the occupy list for the building. * + * BuildingTypeClass::One_Time -- Performs special one time action for buildings. * + * BuildingTypeClass::Overlap_List -- Fetches the overlap list for the building. * + * BuildingTypeClass::Prep_For_Add -- Prepares scenario editor for adding an object. * + * BuildingTypeClass::Repair_Cost -- Determines cost per "step" of repair. * + * BuildingTypeClass::Repair_Step -- Determines the repair step rate. * + * BuildingTypeClass::Who_Can_Build_Me -- Determines which factory can create the building. * + * BuildingTypeClass::Width -- Determines width of bulding in icons. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#define MCW MAP_CELL_W + +#define XYCELL(x,y) (y*MAP_CELL_W+x) +static short const ExitPyle[] = { + XYCELL(1,2), + XYCELL(2,2), + XYCELL(0,2), + XYCELL(-1,2), + XYCELL(-1,-1), + XYCELL(0,-1), + XYCELL(1,-1), + XYCELL(2,-1), + XYCELL(2,-1), + XYCELL(-1,0), + XYCELL(2,0), + XYCELL(2,1), + XYCELL(-1,1), + REFRESH_EOL +}; +static short const ExitHand[] = { + XYCELL(2,3), + XYCELL(1,3), + XYCELL(0,3), + XYCELL(2,2), + XYCELL(-1,3), + XYCELL(-1,2), + XYCELL(0,0), + XYCELL(1,0), + XYCELL(-1,0), + XYCELL(2,0), + XYCELL(2,1), + XYCELL(-1,1), + REFRESH_EOL +}; +static short const ExitWeap[] = { + XYCELL(-1,3), + XYCELL(0,3), + XYCELL(-1,2), + XYCELL(1,3), +// XYCELL(0,0), +// XYCELL(1,0), +// XYCELL(2,0), +// XYCELL(-1,0), +// XYCELL(3,0), + XYCELL(-1,1), + XYCELL(3,1), + XYCELL(3,2), + XYCELL(3,3), + XYCELL(2,3), + REFRESH_EOL +}; +static short const ExitAirstrip[] = { + XYCELL(-1,-1), + XYCELL(-1,0), + XYCELL(-1,1), + XYCELL(-1,2), + XYCELL(0,-1), + XYCELL(0,2), + XYCELL(1,-1), + XYCELL(1,2), + XYCELL(2,-1), + XYCELL(2,2), + XYCELL(3,-1), + XYCELL(3,2), + XYCELL(4,-1), + XYCELL(4,0), + XYCELL(4,1), + XYCELL(4,2), + REFRESH_EOL +}; + +static short const OListSAM[] = {-MCW, -(MCW-1), REFRESH_EOL}; +static short const List32[] = {0, 1, 2, MCW, MCW+1, MCW+2, REFRESH_EOL}; +static short const List22_0011[] = {MCW, MCW+1, REFRESH_EOL}; +static short const List22_1100[] = {0, 1, REFRESH_EOL}; +static short const ListFix[] = {1, MCW, MCW+1, MCW+2, MCW+MCW+1, REFRESH_EOL}; +static short const StoreList[] = {0, 1, REFRESH_EOL}; +static short const List2[] = {0, 1, MCW+1, MCW, REFRESH_EOL}; +static short const List42[] = {0, 1, 2, 3, MCW, MCW+1, MCW+2, MCW+3, REFRESH_EOL}; +static short const ListWestwood[] = {1, 2, 3, MCW+1, MCW+2, MCW+3, REFRESH_EOL}; +static short const OListWestwood[] = {0, MCW, REFRESH_EOL}; +static short const ComList[] = {0, MCW, MCW+1, REFRESH_EOL}; +static short const List21[] = {0, 1, REFRESH_EOL}; +static short const ListWeap[] = {(MCW*1), (MCW*1)+1, (MCW*1)+2, (MCW*2), (MCW*2)+1, (MCW*2)+2, REFRESH_EOL}; +static short const List12[] = {MCW, REFRESH_EOL}; +static short const ListHand[] = {MCW, MCW+1, MCW*2+1, REFRESH_EOL}; +static short const ListTmpl[] = {MCW, MCW+1, MCW+2, MCW*2, MCW*2+1, MCW*2+2, REFRESH_EOL}; +static short const List0011[] = {(MCW*1), (MCW*1)+1, REFRESH_EOL}; +static short const List1101[] = {0, 1, (MCW*1)+1, REFRESH_EOL}; +static short const List11[] = {0, 1, REFRESH_EOL}; +static short const List1[] = {0, REFRESH_EOL}; +static short const List1100[] = {0, 1, REFRESH_EOL}; +static short const List0010[] = {MCW, REFRESH_EOL}; +static short const List1000[] = {0, REFRESH_EOL}; +static short const List0100[] = {1, REFRESH_EOL}; +static short const List0111[] = {1, (MCW*1), (MCW*1)+1, REFRESH_EOL}; +//static short const List1111[] = {0, 1, (MCW*1), (MCW*1)+1, REFRESH_EOL}; +static short const List1011[] = {0, (MCW*1), (MCW*1)+1, REFRESH_EOL}; +static short const List010111000[] = {1, (MCW*1), (MCW*1)+1, (MCW*1)+2, REFRESH_EOL}; +static short const List101000111[] = {0, 2, (MCW*2), (MCW*2)+1, (MCW*2)+2, REFRESH_EOL}; + +static short const OListFix[] = {0, 2, MCW+MCW, MCW+MCW+2, REFRESH_EOL}; +static short const OListWeap[] = {0, 1, 2, REFRESH_EOL}; +static short const OComList[] = {1, REFRESH_EOL}; +static short const OList12[] = {0, REFRESH_EOL}; +static short const OListHand[] = {0, 1, MCW*2, MCW*1, REFRESH_EOL}; +static short const OListTmpl[] = {0, 1, 2, REFRESH_EOL}; + + +/*************************************************************************** +*/ +static BuildingTypeClass const ClassTemple( + STRUCT_TEMPLE, + TXT_TEMPLE, // NAME: Short name of the structure. + "TMPL", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 7, // Build level. + STRUCTF_RADAR, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 1000, // STRNTH: Full strength of building. + 4, // SIGHTRANGE: Range of sighting. + 3000, // COST: Cost to purchase. + 13, // SCENARIO: Starting availability scenario. + 0,20, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points generated. + 150, // DRAIN: Power points required. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ListTmpl, // OCCUPYLIST: List of active foundation squares. + (short const *)OListTmpl // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassEye( + STRUCT_EYE, + TXT_EYE, // NAME: Short name of the structure. + "EYE", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 7, // Build level. + STRUCTF_RADAR, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + (DirType)160, // Starting idle frame to match construction. + 500, // STRNTH: Full strength of building. + 10, // SIGHTRANGE: Range of sighting. + 2800, // COST: Cost to purchase. + 13, // SCENARIO: Starting availability scenario. + 0,100, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 200, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ComList, // OCCUPYLIST: List of active foundation squares. + (short const *)OComList // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassWeapon( + STRUCT_WEAP, + TXT_WEAPON_FACTORY, // NAME: Short name of the structure. + "WEAP", // NAME: Short name of the structure. + XYP_COORD(10+(CELL_PIXEL_W/2), ((CELL_PIXEL_H*3)-(CELL_PIXEL_H/2))-21), // Exit point for produced units. + 2, // Build level. + STRUCTF_REFINERY, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_UNITTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. +#ifdef ADVANCED + 500, // STRNTH: Full strength of building. +#else + 200, // STRNTH: Full strength of building. +#endif + 3, // SIGHTRANGE: Range of sighting. + 2000, // COST: Cost to purchase. + 5, // SCENARIO: Starting availability scenario. + 0,86, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 30, // DRAIN: Power points required. + BSIZE_33, // SIZE: Building size. + (short const *)ExitWeap, // Preferred exit cell list. + (short const *)ListWeap, // OCCUPYLIST: List of active foundation squares. + (short const *)OListWeap // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassGTower( + STRUCT_GTOWER, + TXT_GUARD_TOWER, // NAME: Short name of the structure. + "GTWR", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 2, // Build level. + STRUCTF_BARRACKS, // Building prerequisite. + true, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 3, // SIGHTRANGE: Range of sighting. + 500, // COST: Cost to purchase. + 7, // SCENARIO: Starting availability scenario. + 100,25, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_CHAIN_GUN,WEAPON_NONE, +// WEAPON_M60MG,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 00, // POWER: Power points required. + 10, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassATower( + STRUCT_ATOWER, + TXT_AGUARD_TOWER, // NAME: Short name of the structure. + "ATWR", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 4, // Build level. + STRUCTF_RADAR, // Building prerequisite. + true, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 300, // STRNTH: Full strength of building. + 4, // SIGHTRANGE: Range of sighting. + 1000, // COST: Cost to purchase. + 13, // SCENARIO: Starting availability scenario. + 100,30, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_TOW_TWO,WEAPON_NONE, +// WEAPON_TOMAHAWK,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 20, // DRAIN: Power points required. + BSIZE_12, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List12, // OCCUPYLIST: List of active foundation squares. + (short const *)OList12 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassObelisk( + STRUCT_OBELISK, + TXT_OBELISK, // NAME: Short name of the structure. + "OBLI", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 4, // Build level. + STRUCTF_RADAR, // Building prerequisite. + true, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 5, // SIGHTRANGE: Range of sighting. + 1500, // COST: Cost to purchase. + 11, // SCENARIO: Starting availability scenario. + 100,35, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_OBELISK_LASER,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 150, // DRAIN: Power points required. + BSIZE_12, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List12, // OCCUPYLIST: List of active foundation squares. + (short const *)OList12 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassTurret( + STRUCT_TURRET, + TXT_TURRET, // NAME: Short name of the structure. + "GUN", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 2, // Build level. + STRUCTF_BARRACKS, // Building prerequisite. + true, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + true, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + (DirType)208, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 5, // SIGHTRANGE: Range of sighting. +#ifdef ADVANCED + 600, // COST: Cost to purchase. +#else +#ifdef PATCH + 600, +#else + 250, // COST: Cost to purchase. +#endif +#endif + 8, // SCENARIO: Starting availability scenario. + 300,26, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_TURRET_GUN,WEAPON_NONE, + ARMOR_STEEL, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 20, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassConst( + STRUCT_CONST, + TXT_CONST_YARD, // NAME: Short name of the structure. + "FACT", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_BUILDINGTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 400, // STRNTH: Full strength of building. + 3, // SIGHTRANGE: Range of sighting. + 5000, // COST: Cost to purchase. + 1, // SCENARIO: Starting availability scenario. + 0,70, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 30, // POWER: Power points required. + 15, // DRAIN: Power points required. + BSIZE_32, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List32, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassRefinery( + STRUCT_REFINERY, + TXT_REFINERY, // NAME: Short name of the structure. + "PROC", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 1, // Build level. + STRUCTF_POWER, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 450, // STRNTH: Full strength of building. + 4, // SIGHTRANGE: Range of sighting. + 2000, // COST: Cost to purchase. + 2, // SCENARIO: Starting availability scenario. + 0,55, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 1000, // CAPACITY: Spice storage capacity. + 10, // POWER: Power points required. + 40, // DRAIN: Power points required. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List010111000, // OCCUPYLIST: List of active foundation squares. + (short const *)List101000111 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassStorage( + STRUCT_STORAGE, + TXT_STORAGE, // NAME: Short name of the structure. + "SILO", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 1, // Build level. + STRUCTF_REFINERY, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + false, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 150, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 150, // COST: Cost to purchase. +// 300, // COST: Cost to purchase. + 2, // SCENARIO: Starting availability scenario. + 0,16, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 1500, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 10, // DRAIN: Power points required. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)StoreList, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassHelipad( + STRUCT_HELIPAD, + TXT_HELIPAD, // NAME: Short name of the structure. + "HPAD", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 6, // Build level. + STRUCTF_BARRACKS, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + false, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_AIRCRAFTTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 400, // STRNTH: Full strength of building. + 3, // SIGHTRANGE: Range of sighting. + 1500, // COST: Cost to purchase. + 10, // SCENARIO: Starting availability scenario. + 0,65, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 10, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List2, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassCommand( + STRUCT_RADAR, + TXT_COMMAND, // NAME: Short name of the structure. + "HQ", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 2, // Build level. + STRUCTF_REFINERY, // Building prerequisite. + true, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + (DirType)160, // Starting idle frame to match construction. + 500, // STRNTH: Full strength of building. + 10, // SIGHTRANGE: Range of sighting. + 1000, // COST: Cost to purchase. + 3, // SCENARIO: Starting availability scenario. + 0,20, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 40, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ComList, // OCCUPYLIST: List of active foundation squares. + (short const *)OComList // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassSAM( + STRUCT_SAM, + TXT_SAM, // NAME: Short name of the structure. + "SAM", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 6, // Build level. + STRUCTF_BARRACKS, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + true, // Does it have a rotating turret? + true, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + false, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 3, // SIGHTRANGE: Range of sighting. + 750, // COST: Cost to purchase. + 5, // SCENARIO: Starting availability scenario. + 300,40, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NIKE,WEAPON_NONE, + ARMOR_STEEL, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 20, // DRAIN: Power points required. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List21, // OCCUPYLIST: List of active foundation squares. + (short const *)OListSAM // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassAirStrip( + STRUCT_AIRSTRIP, + TXT_AIRSTRIP, // NAME: Short name of the structure. + "AFLD", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 2, // Build level. + STRUCTF_REFINERY, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_UNITTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 500, // STRNTH: Full strength of building. + 5, // SIGHTRANGE: Range of sighting. + 2000, // COST: Cost to purchase. + 5, // SCENARIO: Starting availability scenario. + 300,86, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_STEEL, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 30, // DRAIN: Power points required. + BSIZE_42, // SIZE: Building size. + ExitAirstrip, // Preferred exit cell list. + (short const *)List42, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassPower( + STRUCT_POWER, + TXT_POWER, // NAME: Short name of the structure. + "NUKE", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 0, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 300, // COST: Cost to purchase. + 1, // SCENARIO: Starting availability scenario. + 0,50, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 100, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1011, // OCCUPYLIST: List of active foundation squares. + (short const *)List0100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassAdvancedPower( + STRUCT_ADVANCED_POWER, + TXT_ADVANCED_POWER, // NAME: Short name of the structure. + "NUK2", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 5, // Build level. + STRUCTF_POWER, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 300, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 700, // COST: Cost to purchase. + 13, // SCENARIO: Starting availability scenario. + 0,75, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 200, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1011, // OCCUPYLIST: List of active foundation squares. + (short const *)List0100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassHospital( + STRUCT_HOSPITAL, + TXT_HOSPITAL, // NAME: Short name of the structure. + "HOSP", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_BARRACKS, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 500, // COST: Cost to purchase. + 99, // SCENARIO: Starting availability scenario. + 0,20, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 100, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 20, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List2, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassBioLab( + STRUCT_BIO_LAB, + TXT_BIO_LAB, // NAME: Short name of the structure. + "BIO", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_HOSPITAL, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 300, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 500, // COST: Cost to purchase. + 99, // SCENARIO: Starting availability scenario. + 0,1, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 100, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 40, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List2, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassBarracks( + STRUCT_BARRACKS, + TXT_BARRACKS, // NAME: Short name of the structure. + "PYLE", // NAME: Short name of the structure. + XYP_COORD(30,33), // Exit point for produced units. + 0, // Build level. + STRUCTF_POWER, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_INFANTRYTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 400, // STRNTH: Full strength of building. + 3, // SIGHTRANGE: Range of sighting. + 300, // COST: Cost to purchase. + 1, // SCENARIO: Starting availability scenario. + 0,60, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 20, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + (short const *)ExitPyle, // Preferred exit cell list. + (short const *)List22_1100, // OCCUPYLIST: List of active foundation squares. + (short const *)List22_0011 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassHand( + STRUCT_HAND, + TXT_HAND, // NAME: Short name of the structure. + "HAND", // NAME: Short name of the structure. + XYP_COORD(36,63), // Exit point for produced units. + 0, // Build level. + STRUCTF_POWER, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_INFANTRYTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 400, // STRNTH: Full strength of building. + 3, // SIGHTRANGE: Range of sighting. + 300, // COST: Cost to purchase. + 2, // SCENARIO: Starting availability scenario. + 0,61, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 20, // DRAIN: Power points required. + BSIZE_23, // SIZE: Building size. + (short const *)ExitHand, // Preferred exit cell list. + (short const *)ListHand, // OCCUPYLIST: List of active foundation squares. + (short const *)OListHand // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassTanker( + STRUCT_TANKER, + TXT_TANKER, // NAME: Short name of the structure. + "ARCO", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_POWER, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + true, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 100, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,1, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List21, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassRepair( + STRUCT_REPAIR, + TXT_FIX_IT, // NAME: Short name of the structure. + "FIX", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 5, // Build level. + STRUCTF_POWER, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + true, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + true, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 400, // STRNTH: Full strength of building. + 3, // SIGHTRANGE: Range of sighting. + 1200, // COST: Cost to purchase. + 8, // SCENARIO: Starting availability scenario. + 0,46, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 30, // DRAIN: Power points required. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ListFix, // OCCUPYLIST: List of active foundation squares. + (short const *)OListFix // OVERLAPLIST:List of overlap cell offset. +); + +#ifdef OBSOLETE +static BuildingTypeClass const ClassRoad( + STRUCT_ROAD, + TXT_ROAD, // NAME: Short name of the structure. + "ROAD", // NAME: Short name of the structure. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + 0, // Building prerequisite. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + false, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + true, // Can it be manufactured by the player? + false, // Does it contain a crew? + true, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 1, // STRNTH: Full strength of building. + 0, // SIGHTRANGE: Range of sighting. + 50, // COST: Cost to purchase. + 99, // SCENARIO: Starting availability scenario. + 0,0, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_NONE, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +#endif + +static BuildingTypeClass const ClassV01( + STRUCT_V01, + TXT_CIV1, // NAME: Short name of the structure. + "V01", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0011, // OCCUPYLIST: List of active foundation squares. + (short const *)List1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV02( + STRUCT_V02, + TXT_CIV2, // NAME: Short name of the structure. + "V02", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0011, // OCCUPYLIST: List of active foundation squares. + (short const *)List1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV03( + STRUCT_V03, + TXT_CIV3, // NAME: Short name of the structure. + "V03", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0111, // OCCUPYLIST: List of active foundation squares. + (short const *)List1000 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV04( + STRUCT_V04, + TXT_CIV4, // NAME: Short name of the structure. + "V04", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0011, // OCCUPYLIST: List of active foundation squares. + (short const *)List1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV05( + STRUCT_V05, + TXT_CIV5, // NAME: Short name of the structure. + "V05", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV06( + STRUCT_V06, + TXT_CIV6, // NAME: Short name of the structure. + "V06", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV07( + STRUCT_V07, + TXT_CIV7, // NAME: Short name of the structure. + "V07", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV08( + STRUCT_V08, + TXT_CIV8, // NAME: Short name of the structure. + "V08", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV09( + STRUCT_V09, + TXT_CIV9, // NAME: Short name of the structure. + "V09", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV10( + STRUCT_V10, + TXT_CIV10, // NAME: Short name of the structure. + "V10", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV11( + STRUCT_V11, + TXT_CIV11, // NAME: Short name of the structure. + "V11", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV12( + STRUCT_V12, + TXT_CIV12, // NAME: Short name of the structure. + "V12", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV13( + STRUCT_V13, + TXT_CIV13, // NAME: Short name of the structure. + "V13", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV14( + STRUCT_V14, + TXT_CIV14, // NAME: Short name of the structure. + "V14", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV15( + STRUCT_V15, + TXT_CIV15, // NAME: Short name of the structure. + "V15", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV16( + STRUCT_V16, + TXT_CIV16, // NAME: Short name of the structure. + "V16", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV17( + STRUCT_V17, + TXT_CIV17, // NAME: Short name of the structure. + "V17", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV18( + STRUCT_V18, + TXT_CIV18, // NAME: Short name of the structure. + "V18", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 1, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV19( + STRUCT_PUMP, + TXT_PUMP, // NAME: Short name of the structure. + "V19", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV20( + STRUCT_V20, + TXT_CIV20, // NAME: Short name of the structure. + "V20", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0011, // OCCUPYLIST: List of active foundation squares. + (short const *)List1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV21( + STRUCT_V21, + TXT_CIV21, // NAME: Short name of the structure. + "V21", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1101, // OCCUPYLIST: List of active foundation squares. + (short const *)List0010 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV22( + STRUCT_V22, + TXT_CIV22, // NAME: Short name of the structure. + "V22", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV23( + STRUCT_V23, + TXT_CIV23, // NAME: Short name of the structure. + "V23", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV24( + STRUCT_V24, + TXT_CIV24, // NAME: Short name of the structure. + "V24", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0011, // OCCUPYLIST: List of active foundation squares. + (short const *)List1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV25( + STRUCT_V25, + TXT_CIV25, // NAME: Short name of the structure. + "V25", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0111, // OCCUPYLIST: List of active foundation squares. + (short const *)List1000 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV26( + STRUCT_V26, + TXT_CIV26, // NAME: Short name of the structure. + "V26", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV27( + STRUCT_V27, + TXT_CIV27, // NAME: Short name of the structure. + "V27", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV28( + STRUCT_V28, + TXT_CIV28, // NAME: Short name of the structure. + "V28", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV29( + STRUCT_V29, + TXT_CIV29, // NAME: Short name of the structure. + "V29", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV30( + STRUCT_V30, + TXT_CIV30, // NAME: Short name of the structure. + "V30", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV31( + STRUCT_V31, + TXT_CIV31, // NAME: Short name of the structure. + "V31", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV32( + STRUCT_V32, + TXT_CIV32, // NAME: Short name of the structure. + "V32", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV33( + STRUCT_V33, + TXT_CIV33, // NAME: Short name of the structure. + "V33", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV34( + STRUCT_V34, + TXT_CIV34, // NAME: Short name of the structure. + "V34", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV35( + STRUCT_V35, + TXT_CIV35, // NAME: Short name of the structure. + "V35", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV36( + STRUCT_V36, + TXT_CIV36, // NAME: Short name of the structure. + "V36", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +static BuildingTypeClass const ClassV37( + STRUCT_V37, + TXT_CIV37, // NAME: Short name of the structure. + "V37", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 300, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_42, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ListWestwood, // OCCUPYLIST: List of active foundation squares. + (short const *)OListWestwood // OVERLAPLIST:List of overlap cell offset. +); +static BuildingTypeClass const ClassMission( + STRUCT_MISSION, + TXT_CIVMISS, // NAME: Short name of the structure. + "MISS", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + true, // Has ability to detect adjacent cloaked objects? + true, // Animation rate is regulated for constant speed? + true, // Requires a bib dirt patch? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Is it a factory type building? + true, // Can this building be captured? + true, // Does it catch fire? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired? + false, // Can it be manufactured by the player? + true, // Does it contain a crew? + false, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 200, // STRNTH: Full strength of building. + 2, // SIGHTRANGE: Range of sighting. + 1000, // COST: Cost to purchase. + 0, // SCENARIO: Starting availability scenario. + 0,2, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_32, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List32, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +// Sandbag wall +static BuildingTypeClass const Sandbag( + STRUCT_SANDBAG_WALL, + TXT_SANDBAG_WALL, // NAME: Short name of the structure. + "SBAG", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 2, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + true, // Can it be manufactured by the player? + false, // Does it contain a crew? + true, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 1, // STRNTH: Full strength of building. + 0, // SIGHTRANGE: Range of sighting. + 50, // COST: Cost to purchase. + 5, // SCENARIO: Starting availability scenario. + 0,0, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +// Cyclone fence +static BuildingTypeClass const Cyclone( + STRUCT_CYCLONE_WALL, + TXT_CYCLONE_WALL, // NAME: Short name of the structure. + "CYCL", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 5, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + true, // Can it be manufactured by the player? + false, // Does it contain a crew? + true, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 1, // STRNTH: Full strength of building. + 0, // SIGHTRANGE: Range of sighting. + 75, // COST: Cost to purchase. + 9, // SCENARIO: Starting availability scenario. + 0,0, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +// Brick wall +static BuildingTypeClass const Brick( + STRUCT_BRICK_WALL, + TXT_BRICK_WALL, // NAME: Short name of the structure. + "BRIK", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 7, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + true, // Can it be manufactured by the player? + false, // Does it contain a crew? + true, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 1, // STRNTH: Full strength of building. + 0, // SIGHTRANGE: Range of sighting. + 100, // COST: Cost to purchase. + 13, // SCENARIO: Starting availability scenario. + 0,0, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +// Barbwire wall +static BuildingTypeClass const Barbwire( + STRUCT_BARBWIRE_WALL, + TXT_BARBWIRE_WALL, // NAME: Short name of the structure. + "BARB", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 98, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + true, // Can it be manufactured by the player? + false, // Does it contain a crew? + true, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 1, // STRNTH: Full strength of building. + 0, // SIGHTRANGE: Range of sighting. + 25, // COST: Cost to purchase. + 98, // SCENARIO: Starting availability scenario. + 0,0, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_BAD| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +// Wood wall +static BuildingTypeClass const Wood( + STRUCT_WOOD_WALL, + TXT_WOOD_WALL, // NAME: Short name of the structure. + "WOOD", // NAME: Short name of the structure. + XYP_COORD(0,0), // Exit point for produced units. + 99, // Build level. + STRUCTF_NONE, // PREREQ: Buildings that must exist first. + false, // Has ability to detect adjacent cloaked objects? + false, // Animation rate is regulated for constant speed? + false, // Requires a bib dirt patch? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Is it a factory type building? + false, // Can this building be captured? + false, // Does it catch fire? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Is it immune to normal combat damage? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired? + true, // Can it be manufactured by the player? + false, // Does it contain a crew? + true, // Does building care less if placed on concrete? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + 1, // STRNTH: Full strength of building. + 0, // SIGHTRANGE: Range of sighting. + 25, // COST: Cost to purchase. + 98, // SCENARIO: Starting availability scenario. + 0,0, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_NEUTRAL| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + 0, // CANENTER: Units that can enter building. + 0, // CAPACITY: Spice storage capacity. + 0, // POWER: Power points required. + 0, // DRAIN: Power points required. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + + +BuildingTypeClass const * const BuildingTypeClass::Pointers[STRUCT_COUNT] = { + &ClassWeapon, // STRUCT_WEAP + &ClassGTower, // STRUCT_GTOWER + &ClassATower, // STRUCT_ATOWER + &ClassObelisk, // STRUCT_OBLISK + &ClassCommand, // STRUCT_RADAR + &ClassTurret, // STRUCT_TURRET + &ClassConst, // STRUCT_CONST + &ClassRefinery, // STRUCT_REFINERY + &ClassStorage, // STRUCT_STORAGE + &ClassHelipad, // STRUCT_HELIPAD + &ClassSAM, // STRUCT_SAM + &ClassAirStrip, // STRUCT_AIRSTRIP + &ClassPower, // STRUCT_POWER + &ClassAdvancedPower, // STRUCT_POWER + &ClassHospital, // STRUCT_HOSPITAL + &ClassBarracks, // STRUCT_BARRACKS + &ClassTanker, // STRUCT_TANKER + &ClassRepair, // STRUCT_REPAIR + &ClassBioLab, // STRUCT_BIO_LAB + &ClassHand, // STRUCT_HAND + &ClassTemple, // STRUCT_TEMPLE + &ClassEye, // STRUCT_EYE + &ClassMission, // STRUCT_MISSION + + &ClassV01, // STRUCT_V1 + &ClassV02, // STRUCT_V2 + &ClassV03, // STRUCT_V3 + &ClassV04, // STRUCT_V4 + &ClassV05, // STRUCT_V5 + &ClassV06, // STRUCT_V6 + &ClassV07, // STRUCT_V7 + &ClassV08, // STRUCT_V8 + &ClassV09, // STRUCT_V9 + &ClassV10, // STRUCT_V10 + &ClassV11, // STRUCT_V11 + &ClassV12, // STRUCT_V12 + &ClassV13, // STRUCT_V13 + &ClassV14, // STRUCT_V14 + &ClassV15, // STRUCT_V15 + &ClassV16, // STRUCT_V16 + &ClassV17, // STRUCT_V17 + &ClassV18, // STRUCT_V18 + &ClassV19, // STRUCT_PUMP + &ClassV20, // STRUCT_V20 + &ClassV21, // STRUCT_V21 + &ClassV22, // STRUCT_V22 + &ClassV23, // STRUCT_V23 + &ClassV24, // STRUCT_V24 + &ClassV25, // STRUCT_V25 + &ClassV26, // STRUCT_V26 + &ClassV27, // STRUCT_V27 + &ClassV28, // STRUCT_V28 + &ClassV29, // STRUCT_V29 + &ClassV30, // STRUCT_V30 + &ClassV31, // STRUCT_V31 + &ClassV32, // STRUCT_V32 + &ClassV33, // STRUCT_V33 + &ClassV34, // STRUCT_V34 + &ClassV35, // STRUCT_V35 + &ClassV36, // STRUCT_V36 + &ClassV37, // STRUCT_V37 +#ifdef OBSOLETE + &ClassRoad, // STRUCT_ROAD +#endif + &Sandbag, // STRUCT_SANDBAG_WALL + &Cyclone, // STRUCT_CYCLONE_WALL + &Brick, // STRUCT_BRICK_WALL + &Barbwire, // STRUCT_BARBWIRE_WALL + &Wood, // STRUCT_WOOD_WALL +}; + +void const *WarFactoryOverlay; + + +/*********************************************************************************************** + * BuildingTypeClass::BuildingTypeClass -- This is the constructor for the building types. * + * * + * This is the constructor used to create the building types. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1994 JLB : Created. * + *=============================================================================================*/ +BuildingTypeClass::BuildingTypeClass( + StructType type, + int name, + char const *ininame, + COORDINATE exitpoint, + unsigned char level, + long pre, + bool is_scanner, + bool is_regulated, + bool is_bibbed, + bool is_nominal, + bool is_wall, + bool is_factory, + bool is_captureable, + bool is_flammable, + bool is_simpledamage, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_theater, + bool is_turret_equipped, + bool is_twoshooter, + bool is_repairable, + bool is_buildable, + bool is_crew, + bool is_sturdy, + RTTIType tobuild, + DirType sframe, + unsigned short strength, + int sightrange, + int cost, + int scenario, + int risk, + int reward, + int ownable, + WeaponType primary, WeaponType secondary, + ArmorType armor, + unsigned long canenter, + unsigned capacity, + int power, + int drain, + BSizeType size, + short const *exitlist, + short const *sizelist, + short const *overlap) : + TechnoTypeClass(name, + ininame, + level, + pre, + false, + is_scanner, + is_nominal, + false, + is_flammable, + false, + is_stealthy, + is_selectable, + is_legal_target, + is_insignificant, + is_immune, + is_theater, + is_twoshooter, + is_turret_equipped, + is_repairable, + is_buildable, + is_crew, + -1, + strength*2, + MPH_IMMOBILE, + sightrange, + cost, + scenario, + risk, + reward, + ownable, + primary, + secondary, + armor) +{ + CanEnter = canenter; + Capacity = capacity; + Drain = drain; + ExitList = exitlist; + ExitPoint = exitpoint; + IsBibbed = is_bibbed; + IsCaptureable = is_captureable; + IsFactory = is_factory; + IsRegulated = is_regulated; + IsSimpleDamage = is_simpledamage; + IsSturdy = is_sturdy; + IsWall = is_wall; + OccupyList = sizelist; + OverlapList = overlap; + Power = power; + Size = size; + StartFace = sframe; + ToBuild = tobuild; + Type = type; + + Anims[BSTATE_CONSTRUCTION].Start = 0; + Anims[BSTATE_CONSTRUCTION].Count = 1; + Anims[BSTATE_CONSTRUCTION].Rate = 0; + + Anims[BSTATE_IDLE].Start = 0; + Anims[BSTATE_IDLE].Count = 1; + Anims[BSTATE_IDLE].Rate = 0; + + Anims[BSTATE_ACTIVE].Start = 0; + Anims[BSTATE_ACTIVE].Count = 1; + Anims[BSTATE_ACTIVE].Rate = 0; + + Anims[BSTATE_AUX1].Start = 0; + Anims[BSTATE_AUX1].Count = 1; + Anims[BSTATE_AUX1].Rate = 0; + + Anims[BSTATE_AUX2].Start = 0; + Anims[BSTATE_AUX2].Count = 1; + Anims[BSTATE_AUX2].Rate = 0; +} + + +/*********************************************************************************************** + * BuildingTypeClass::One_Time -- Performs special one time action for buildings. * + * * + * This routine is used to do the one time action necessary to handle building type class * + * objects. This entails loading of the building shapes and the brain file used by * + * buildings. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine should only be called ONCE. * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + * 06/11/1994 JLB : Updated construction time and frame count logic. * + *=============================================================================================*/ +void BuildingTypeClass::One_Time(void) +{ + static const struct { + StructType Class; // Building class number. + BStateType Stage; // Animation sequence to assign animation range to. + int Start; // Starting frame number. + int Length; // Number of frames (-1 means use all frames). + int Rate; // Rate of animation. + } _anims[] = { + {STRUCT_OBELISK, BSTATE_ACTIVE, 0, 4, OBELISK_ANIMATION_RATE}, + {STRUCT_ADVANCED_POWER, BSTATE_IDLE, 0, 4, 15}, + {STRUCT_AIRSTRIP, BSTATE_IDLE, 0, 16,3}, + {STRUCT_BARRACKS, BSTATE_ACTIVE, 0, 10,3}, + {STRUCT_BARRACKS, BSTATE_IDLE, 0, 10,3}, + {STRUCT_CONST, BSTATE_ACTIVE, 4, 20,3}, + {STRUCT_CONST, BSTATE_IDLE, 0, 4, 3}, + {STRUCT_EYE, BSTATE_IDLE, 0, 16,4}, + {STRUCT_HELIPAD, BSTATE_ACTIVE, 0, 7, 4}, + {STRUCT_HELIPAD, BSTATE_IDLE, 0, 0, 0}, + {STRUCT_HOSPITAL, BSTATE_IDLE, 0, 4, 3}, + {STRUCT_POWER, BSTATE_IDLE, 0, 4, 15}, + {STRUCT_PUMP, BSTATE_IDLE, 0, 14,4}, + {STRUCT_RADAR, BSTATE_IDLE, 0, 16,4}, + {STRUCT_REFINERY, BSTATE_ACTIVE, 12,7, 4}, // Docking phase. + {STRUCT_REFINERY, BSTATE_AUX1, 19,5, 4}, // Siphoning phase. + {STRUCT_REFINERY, BSTATE_AUX2, 24,6, 4}, // Undocking phase. + {STRUCT_REFINERY, BSTATE_IDLE, 0, 6, 4}, // Idle phase. + {STRUCT_REFINERY, BSTATE_FULL, 6, 6, 4}, // Flashing lights + {STRUCT_REPAIR, BSTATE_ACTIVE, 0, 7, 2}, + {STRUCT_REPAIR, BSTATE_IDLE, 0, 1, 0}, + {STRUCT_TEMPLE, BSTATE_IDLE, 0, 1, 0}, + {STRUCT_V20, BSTATE_IDLE, 0, 3, 3}, + {STRUCT_V21, BSTATE_IDLE, 0, 3, 3}, + {STRUCT_V22, BSTATE_IDLE, 0, 3, 3}, + {STRUCT_V23, BSTATE_IDLE, 0, 3, 3}, + {STRUCT_WEAP, BSTATE_ACTIVE, 0, 1, 0}, + {STRUCT_WEAP, BSTATE_IDLE, 0, 1, 0}, + {STRUCT_TEMPLE, BSTATE_ACTIVE, 0, 5, 1}, + }; + + for (StructType sindex = STRUCT_FIRST; sindex < STRUCT_COUNT; sindex++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + char buffer[_MAX_FNAME]; + BuildingTypeClass const & building = As_Reference(sindex); + + /* + ** Fetch the sidebar cameo image for this building. + */ + if (building.IsBuildable) { + if ( Get_Resolution_Factor() ) { + sprintf(buffer, "%sICNH", building.IniName); + } else { + sprintf(buffer, "%sICON", building.IniName); + } + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + ((void const *&)building.CameoData) = MixFileClass::Retrieve(fullname); + } + + /* + ** Fetch the construction animation for this building. + */ + sprintf(buffer, "%sMAKE", building.IniName); + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + void const * dataptr = MixFileClass::Retrieve(fullname); + ((void const *&)building.BuildupData) = dataptr; + if (dataptr) { + int timedelay = 1; + int count = Get_Build_Frame_Count(dataptr); + if (count) { + timedelay = (5 * TICKS_PER_SECOND) / count; + } + building.Init_Anim(BSTATE_CONSTRUCTION, 0, count, timedelay); + } + + /* + ** Fetch the normal game shape for this building. + */ + _makepath(fullname, NULL, NULL, building.IniName, ".SHP"); + ((void const *&)building.ImageData) = MixFileClass::Retrieve(fullname); + } + + // Try to load weap2.shp + char fullname[_MAX_FNAME+_MAX_EXT]; + _makepath(fullname, NULL, NULL, (char const *)"WEAP2",".SHP"); + WarFactoryOverlay = MixFileClass::Retrieve(fullname); + + /* + ** Install all the special animation sequences for the different building types. + */ + for (unsigned index = 0; index < (sizeof(_anims) / sizeof(_anims[0])); index++) { + BuildingTypeClass const *b = &As_Reference(_anims[index].Class); + if (b) { + b->Init_Anim(_anims[index].Stage, _anims[index].Start, _anims[index].Length, _anims[index].Rate); + } + } +} + + + + +/*********************************************************************************************** + * Struct_From_Name -- Find BData structure from its name. * + * * + * This routine will convert an ASCII name for a building class into * + * the actual building class it represents. * + * * + * INPUT: name -- ASCII representation of a building class. * + * * + * OUTPUT: Returns with the actual building class number that the string * + * represents. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 05/02/1994 JLB : Converted to member function. * + *=============================================================================================*/ +StructType BuildingTypeClass::From_Name(char const *name) +{ + if (name) { + for (StructType classid = STRUCT_FIRST; classid < STRUCT_COUNT; classid++) { + if (stricmp(As_Reference(classid).IniName, name) == 0) { + return(classid); + } + } + } + return(STRUCT_NONE); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * BuildingTypeClass::Display -- Renders a generic view of building. * + * * + * This routine is used to display a generic representation of the * + * building. Typical use of this occurs with the scenario editor. * + * * + * INPUT: x,y -- Coordinate to display the building (centered). * + * * + * window -- The window the building should be rendered * + * relative to. * + * * + * house -- The house color to use for the building. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::Display(int x, int y, WindowNumberType window, HousesType house) const +{ + void const * ptr = Get_Cameo_Data(); + if (!ptr) { + ptr = Get_Image_Data(); + } + CC_Draw_Shape(ptr, 0, x, y, window, SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, HouseTypeClass::As_Reference(house).RemapTable); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Prep_For_Add -- Prepares scenario editor for adding a * + * * + * This routine is used to prepare the scenario editor for the addition * + * of a building object to the game. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + * 06/04/1994 JLB : Uses map editing interface routines. * + *=============================================================================================*/ +void BuildingTypeClass::Prep_For_Add(void) +{ + for (StructType index = STRUCT_FIRST; index < STRUCT_COUNT; index++) { + if (As_Reference(index).Get_Image_Data()) { + Map.Add_To_List(&As_Reference(index)); + } + } +} +#endif + + +/*********************************************************************************************** + * BuildingTypeClass::Create_And_Place -- Creates and places a building object onto the map. * + * * + * This routine is used by the scenario editor to create and place buildings on the map. * + * * + * INPUT: cell -- The cell that the building is to be placed upon. * + * * + * house -- The owner of the building. * + * * + * OUTPUT: bool; Was the building successfully created and placed on the map? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +bool BuildingTypeClass::Create_And_Place(CELL cell, HousesType house) const +{ + BuildingClass * ptr; + + ptr = new BuildingClass(Type, house); + if (ptr) { + return(ptr->Unlimbo(Cell_Coord(cell), DIR_N)); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Create_One_Of -- Creates a building of this type. * + * * + * This routine will create a building object of this type. The building object is in a * + * limbo state. It is presumed that the building object will be unlimboed at the correct * + * place and time. Typical use is when the building is created in a factory situation * + * and will be placed on the map when construction completes. * + * * + * INPUT: house -- Pointer to the house that is to be the owner of the building. * + * * + * OUTPUT: Returns with a pointer to the building. If the building could not be created * + * then a NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/07/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * BuildingTypeClass::Create_One_Of(HouseClass * house) const +{ + HousesType htype = HOUSE_NEUTRAL; + if (house) { + htype = house->Class->House; + } + return(new BuildingClass(Type, htype)); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Init_Anim -- Initialize an animation control for a building. * + * * + * This routine will initialize one animation control element for a * + * specified building. This modifies a "const" class and thus must * + * perform some strategic casting to get away with this. * + * * + * INPUT: state -- The animation state to apply these data values to. * + * * + * start -- Starting frame for the building's animation. * + * * + * count -- The number of frames in this animation. * + * * + * rate -- The countdown timer between animation frames. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/18/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::Init_Anim(BStateType state, int start, int count, int rate) const +{ + ((int &)Anims[state].Start) = start; + ((int &)Anims[state].Count) = count; + ((int &)Anims[state].Rate) = rate; +} + + +/*********************************************************************************************** + * BuildingTypeClass::Legal_Placement -- Determines if building can be legally placed at pos. * + * * + * This routine is used to determine if a building can be legally * + * placed at the specified position. Buildings can only be placed on * + * unoccupied rock terrain. * + * * + * INPUT: pos -- Position that the building would be placed (up-left) * + * * + * OUTPUT: 0=illegal, 1=on concrete, -1..-8=part on concrete. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/15/1991 JLB : Created. * + * 04/26/1992 JLB : Concrete and scenario init adjustment. * + * 05/06/1992 JLB : Good, Bad, and Adjacent checking added. * + * 08/09/1992 JLB : Determines full or partial concrete foundation. * + * 06/07/1994 JLB : Handles concrete special check. * + * 06/21/1994 JLB : Converted to building type class member function. * + *=============================================================================================*/ +int BuildingTypeClass::Legal_Placement(CELL pos) const +{ + short const *offset; // Pointer to cell offset list. + + if (pos == -1) return(false); + +#ifdef NEVER + /* + ** Concrete has special checking performed to determine legal placement. Concrete + ** can legally be placed if there is any cell that would be affected by the concrete + ** placement. Unlike other buildings, only one cell needs to be effective in order + ** to flag legal placement for the entire "structure". + */ + if (Type == STRUCT_CONCRETE_NOD || Type == STRUCT_CONCRETE_GDI) { + offset = Occupy_List(); + while (*offset != REFRESH_EOL) { + if (!Map.Cell_Template(pos + (CELL)*offset++)) { + return(true); + } + } + + /* + ** No squares would be affected by concrete placement so consider legal + ** placement query to be false. + */ + return(false); + } +#endif + + /* + ** Normal buildings must check to see that every foundation square is free of + ** obstacles. If this check passes for all foundation squares, only then does the + ** routine return that it is legal to place. + */ + offset = Occupy_List(true); + while (*offset != REFRESH_EOL) { + CELL cell = pos + *offset++; + if (!Map.In_Radar(cell)) return(false); + if (!Map[cell].Is_Generally_Clear()) { + return(false); + } + } + return(true); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Who_Can_Build_Me -- Determines which factory can create the building. * + * * + * Use this routine to determine which building is available to build the building. If * + * there are no suitable buildings, then NULL is returned. Typical use of this function is * + * to maintain the construction list sidebar. * + * * + * INPUT: intheory -- If true, then it merely checks for a building of the proper ownership * + * when determining if construction is allowed. It doesn't consider the * + * possibility that the construction building is currently busy or not. * + * * + * legal -- Should building prerequisite legality checks be performed as well? * + * For building placements, this is usually false. For sidebar button * + * adding, this is usually true. * + * * + * house -- The owner of the building to be built. Only construction buildings of * + * the same ownership are allowed to build. * + * * + * OUTPUT: Returns with a pointer to the construction object (building) that can build * + * the building type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/29/1994 JLB : Created. * + * 01/02/1995 JLB : Scans in reverse order so that later buildings are biased. * + *=============================================================================================*/ +BuildingClass * BuildingTypeClass::Who_Can_Build_Me(bool intheory, bool legal, HousesType house) const +{ + for (int index = Buildings.Count()-1; index >= 0; index--) { + BuildingClass * building = Buildings.Ptr(index); + + if (building && !building->IsInLimbo && + building->House->Class->House == house && + building->Class->ToBuild == RTTI_BUILDINGTYPE && + building->Mission != MISSION_DECONSTRUCTION && + ((1L << building->ActLike) & Ownable) && + (!legal || building->House->Can_Build(Type, building->ActLike)) && + (intheory || !building->In_Radio_Contact())) { + return(building); + } + } + return(0); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Init -- Performs theater specific initialization. * + * * + * This routine is used to perform any initialization that is custom per theater. * + * Typically, this is fetching the building shape data for those building types that have * + * theater specific art. * + * * + * INPUT: theater -- The theater to base this initialization on. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::Init(TheaterType theater) +{ + if (theater != LastTheater){ + char fullname[_MAX_FNAME+_MAX_EXT]; + + for (StructType sindex = STRUCT_FIRST; sindex < STRUCT_COUNT; sindex++) { + BuildingTypeClass const *classptr = &As_Reference(sindex); + + if (classptr->IsTheater) { + _makepath(fullname, NULL, NULL, classptr->IniName, Theaters[theater].Suffix); + ((void const *&)classptr->ImageData) = MixFileClass::Retrieve(fullname); + } + + if ( Get_Resolution_Factor() ) { + char buffer[_MAX_FNAME]; + char fullname[_MAX_FNAME+_MAX_EXT]; + void const * cameo_ptr; + + ((void const *&)classptr->CameoData) = NULL; + + sprintf(buffer, "%.4sICNH", classptr->IniName); + _makepath (fullname, NULL, NULL, buffer, Theaters[theater].Suffix); + cameo_ptr = MixFileClass::Retrieve(fullname); + if (cameo_ptr){ + ((void const *&)classptr->CameoData) = cameo_ptr; + } + } + } + } +} + + +/*********************************************************************************************** + * BuildingTypeClass::Dimensions -- Fetches the pixel dimensions of the building. * + * * + * This routine will fetch the dimensions of the building (in pixels). These dimensions are * + * used to render the selection rectangle and the health bar. * + * * + * INPUT: width -- Reference to the pixel width (to be filled in). * + * * + * height -- Reference to the pixel height (to be filled in). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::Dimensions(int &width, int &height) const +{ + static struct { + int Width; + int Height; + } _dimensions[BSIZE_COUNT] = { + {1,1}, + {2,1}, + {1,2}, + {2,2}, + {2,3}, + {3,2}, + {3,3}, + {4,2}, + {5,5} + }; + + width = _dimensions[Size].Width * ICON_PIXEL_W; + width -= (width/5); + height = _dimensions[Size].Height * ICON_PIXEL_H; + height -= (height/5); +} + + +/*********************************************************************************************** + * BuildingTypeClass::As_Reference -- Fetches reference to the building type specified. * + * * + * This routine will fetch a reference to the BuildingTypeClass as indicated by the * + * building type number specified. * + * * + * INPUT: type -- The building type number to convert into a BuildingTypeClass reference. * + * * + * OUTPUT: Returns with a reference to the building type class as indicated by the * + * parameter. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +BuildingTypeClass const & BuildingTypeClass::As_Reference(StructType type) +{ + return(* Pointers[type]); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Occupy_List -- Fetches the occupy list for the building. * + * * + * Use this routine to fetch the occupy list pointer for the building. The occupy list is * + * used to determine what cells the building occupies and thus precludes other buildings * + * or objects from using. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a pointer to a cell offset list to be used to determine what cells * + * this building occupies. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +short const * BuildingTypeClass::Occupy_List(bool placement) const +{ + SmudgeType bib = SMUDGE_NONE; + CELL cell=0; + + if ((placement && Bib_And_Offset(bib, cell)) || (Special.IsRoad && (*this == STRUCT_BARRACKS || (placement && *this == STRUCT_REFINERY)))) { + + /* + ** The barracks is always considered to have a bib under it for placement reasons even + ** if the bib logic is turned off. + */ + if (Special.IsRoad && *this == STRUCT_BARRACKS) { + bib = SMUDGE_BIB3; + cell = 0; + } + + /* + ** If bibs are disabled, then always ensure that the refinery bib is marked + ** as occupied. + */ + if (Special.IsRoad && *this == STRUCT_REFINERY) { + bib = SMUDGE_BIB2; + cell = MAP_CELL_W; + } + + SmudgeTypeClass const & smudge = SmudgeTypeClass::As_Reference(bib); + static short _list[25]; + short * dest = &_list[0]; + + /* + ** Copy the bib overlap list into the working buffer. + */ + short const * src = smudge.Occupy_List(); + while (*src != REFRESH_EOL) { + *dest++ = (*src++) + cell; + } + + /* + ** Append the building occupy list to this working buffer. + */ + src = OccupyList; + while (src && *src != REFRESH_EOL) { + *dest++ = *src++; + } + *dest = REFRESH_EOL; + + return(&_list[0]); + } + + if (OccupyList) { + return(OccupyList); + } + + static short const _templap[] = {REFRESH_EOL}; + return(&_templap[0]); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Overlap_List -- Fetches the overlap list for the building. * + * * + * This routine will fetch the overlap list for the building. The overlap list is used * + * to determine what cells the building's graphics cover, but is not considered to occupy * + * for movement purposes. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the cell offset list that is used to determine the * + * cells that this building overlaps. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +short const * BuildingTypeClass::Overlap_List(void) const +{ + if (OverlapList) { + return(OverlapList); + } + + static short const _templap[] = {REFRESH_EOL}; + return(&_templap[0]); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Width -- Determines width of bulding in icons. * + * * + * Use this routine to determine the width of the building type in icons. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the building width in icons. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/23/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Width(void) const +{ + static int width[BSIZE_COUNT] = { + 1, + 2, + 1, + 2, + 2, + 3, + 3, + 4, + 5 + }; + return(width[Size]); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Height -- Determins the height of the building in icons. * + * * + * Use this routine to find the height of the building in icons. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the building height in icons. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/23/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Height(void) const +{ + static int height[BSIZE_COUNT] = { + 1, + 1, + 2, + 2, + 3, + 2, + 3, + 2, + 5 + }; + return(height[Size]); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Repair_Cost -- Determines cost per "step" of repair. * + * * + * Use this routine to determine how much it will cost to repair the building one * + * step. A step is defined as the number of hit points returned from the Repair_Step() * + * function. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the credit cost to repair this building one step. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/23/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Repair_Cost(void) const +{ + int cost = (Raw_Cost()*REPAIR_STEP) / MaxStrength; + cost /= 2; + cost = MAX(cost, 1); + cost = Fixed_To_Cardinal(cost, REPAIR_PERCENT); + return(MAX(cost, 1)); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Repair_Step -- Determines the repair step rate. * + * * + * This routine will determine how many strength points get healed for each "step". The * + * cost to repair one step is determine from the Repair_Cost() function. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of health points repaired for each "step". * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/23/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Repair_Step(void) const +{ + return(REPAIR_STEP); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Bib_And_Offset -- Determines the bib and appropriate cell offset. * + * * + * This routine is used to determine what (if any) bib should be used for this building * + * and also the cell offset for the upper left corner of the bib smudge type. * + * * + * INPUT: bib -- Reference to the bib that should be used for this building. * + * * + * cell -- The cell offset for the upper left corner of the bib. This offset is * + * relative to the upper left corner of the building. * + * * + * OUTPUT: Is a bib required for this building? If the result is true, then the correct * + * bib and cell offset will be filled in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool BuildingTypeClass::Bib_And_Offset(SmudgeType & bib, CELL & cell) const +{ + bib = SMUDGE_NONE; + + if (IsBibbed && !Special.IsRoad) { + switch (Width()) { + case 2: + bib = SMUDGE_BIB3; + break; + + case 3: + bib = SMUDGE_BIB2; + break; + + case 4: + bib = SMUDGE_BIB1; + break; + } + + /* + ** Adjust the bib position for special buildings that have the bib as part + ** of the building art itself. + */ + if (bib != SMUDGE_NONE) { + cell += ((Height()-1)*MAP_CELL_W); + } + } + return(bib != SMUDGE_NONE); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Max_Pips -- Determines the maximum pips to display. * + * * + * Use this routine to determine the maximum number of pips to display on this building * + * when it is rendered. Typically, this is the tiberium capacity divided by 100. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of pips to display on this building when selected. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/29/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Max_Pips(void) const +{ + return(Bound(Capacity/100, 0, 10)); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Full_Name -- Fetches the full name text number. * + * * + * This routine will fetch the full name of this building (expressed as a text number). * + * If special civilian names are enabled, then the civilian buildings will show their true * + * name rather than "civilian building". * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the full name of this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/29/1995 JLB : Created. * + * 07/17/1995 JLB : Village wells will always have their name displayed. * + *=============================================================================================*/ +int BuildingTypeClass::Full_Name(void) const +{ + if (::Scenario == 3 && Type == STRUCT_MISSION) { + return(TXT_PRISON); + } + if (!IsNominal || Special.IsNamed || IsWall || Debug_Map || Type == STRUCT_V23 || Type == STRUCT_V30 || Type == STRUCT_MISSION || Type == STRUCT_BIO_LAB) { + return(TechnoTypeClass::Full_Name()); + } + return(TXT_CIVILIAN_BUILDING); +} + + +int BuildingTypeClass::Raw_Cost(void) const +{ +#ifdef PATCH + /* + ** Forces the turret cost down to original 250 for old + ** version games. + */ + if (IsV107 && Type == STRUCT_TURRET && GameToPlay != GAME_NORMAL) { + return(250); + } +#endif + + int cost = TechnoTypeClass::Raw_Cost(); + + if (Type == STRUCT_HELIPAD) { + cost -= AircraftTypeClass::As_Reference(AIRCRAFT_ORCA).Cost; + } + if (Type == STRUCT_REFINERY) { + cost -= UnitTypeClass::As_Reference(UNIT_HARVESTER).Cost; + } + return(cost); +} + + +int BuildingTypeClass::Cost_Of(void) const +{ + if (Special.IsSeparate && Type == STRUCT_HELIPAD) { + return(Raw_Cost()); + } + +#ifdef PATCH + /* + ** Forces the turret cost down to original 250 for old + ** version games. + */ + if (IsV107 && Type == STRUCT_TURRET && GameToPlay != GAME_NORMAL) { + return(250); + } +#endif + + return(TechnoTypeClass::Cost_Of()); +} diff --git a/BFILE.MAK b/BFILE.MAK new file mode 100644 index 0000000..72f1434 --- /dev/null +++ b/BFILE.MAK @@ -0,0 +1,3406 @@ +# +# Command & Conquer(tm) +# Copyright 2025 Electronic Arts Inc. +# +# 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 3 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# $Header: F:\projects\c&c\vcs\code\bfile.mav 1.14 02 Aug 1995 17:04:30 JOE_BOSTIC $ +#*************************************************************************** +#** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +#*************************************************************************** +#* * +#* Project Name : Command & Conquer * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Joe L. Bostic * +#* * +#* Start Date : March 25, 1993 * +#* * +#* Last Update : March 25, 1993 [JLB] * +#* * +#*-------------------------------------------------------------------------* + + +# Comment out the following line to disable "include file autodependency". +.AUTODEPEND +#.SWAP + +!include "rules.mak" + +# +# Name of the executable (without extension). Presumes .EXE +# +EXE = conquer + + +########################################################################## + + +# Village of the Unfortunate +# Deja View +SC001 = \ + EXPAND.DAT \ + ..\maps\expand\SCG20EA.INI \ + ..\maps\expand\SCG20EA.BIN \ + ..\maps\expand\SCG21EA.INI \ + ..\maps\expand\SCG21EA.BIN \ + SCM75EA.INI \ + SCM75EA.BIN \ + SCM76EA.INI \ + SCM76EA.BIN \ + +# Desert Shores +# Crete +SC002 = \ + EXPAND.DAT \ + SCM20EA.INI \ + SCM20EA.BIN \ + SCM81EA.INI \ + SCM81EA.BIN \ + +# The Great Race +# A Long Way From Home +SC003 = \ + EXPAND.DAT \ + SCM82EA.INI \ + SCM82EA.BIN \ + SCM90EA.INI \ + SCM90EA.BIN \ + + + +MAPFILES = \ + MLTIPLYR.WSA \ + GREYERTH.WSA \ + AFRICA.WSA \ + E-BWTOCL.WSA \ +# EARTH_A.WSA \ +# S_AFRICA.WSA \ + CLICK_SA.CPS \ + CLICK_A.CPS \ +# BOSNIA.WSA \ + CLICK_E.CPS \ + CLICK_EB.CPS \ + DARK_E.PAL \ +# EARTH_E.WSA \ + EUROPE.WSA \ + S-GDIIN2.WSA \ + SCRSCN1.WSA \ + DARK_B.PAL \ + DARK_SA.PAL \ + SATSEL.PAL \ + SATSEL.CPS \ +# CHOSEGDI.WSA \ +# CHOSENOD.WSA \ + +CACHEMAP = \ + COUNTRYA.SHP \ + COUNTRYE.SHP \ + TIME.SHP \ + HISCORE1.SHP \ + HISCORE2.SHP \ + BAR3RED.SHP \ + BAR3YLW.SHP \ + LOGOS.SHP \ + CREDS.SHP \ + +LOCALFILES = \ + CONQUER.ENG \ + SCOREFNT.FNT \ + GRAD6FNT.FNT \ + 3POINT.FNT \ + 6POINT.FNT \ + 8POINT.FNT \ + 8FAT.FNT \ + FONT12.FNT \ + FONT6.FNT \ + LED.FNT \ + VCR.FNT \ + TEMPERAT.PAL \ + MOUSE.SHP \ + +UPDATECFILES = \ + HPIPS.SHP \ + HPIPS_G.SHP \ + HPIPS_F.SHP \ + HBTN-DN.SHP \ + HBTN-UP.SHP \ + HBTN-DN2.SHP \ + HBTN-UP2.SHP \ + HSIDE1.SHP \ + HSIDE2.SHP \ + HPWRBAR.SHP \ + HREPAIR.SHP \ + HSELL.SHP \ + HMAP.SHP \ + HREPAIRG.SHP \ + HSELLG.SHP \ + HMAPG.SHP \ + HREPAIRF.SHP \ + HSELLF.SHP \ + HMAPF.SHP \ + BTN-STH.SHP \ + BTN-PLH.SHP \ + HRADAR.GDI \ + HRADAR.NOD \ + HRADAR.JP \ + HCLOCK.SHP \ + HPOWER.SHP \ + HSTRIP.SHP \ + HSTRIPDN.SHP \ + HSTRIPUP.SHP \ + HTABS.SHP \ + BTEXTURE.SHP \ + GRAD12FN.FNT \ +# EYEICNH.SHP \ +# BOMBICNH.SHP \ +# ATOMICNH.SHP \ +# IONICNH.SHP \ +# A10ICNH.SHP \ +# AFLDICNH.SHP \ +# APCICNH.SHP \ +# ARCOICNH.SHP \ +# ARTYICNH.SHP \ +# ATWRICNH.SHP \ +# BARBICNH.SHP \ +# BGGYICNH.SHP \ +# BIKEICNH.SHP \ +# BIOICNH.SHP \ +# BOATICNH.SHP \ +# BRIKICNH.SHP \ +# C17ICNH.SHP \ +# CYCLICNH.SHP \ +# E1ICNH.SHP \ +# E2ICNH.SHP \ +# E3ICNH.SHP \ +# E4ICNH.SHP \ +# E5ICNH.SHP \ +# E6ICNH.SHP \ +# FACTICNH.SHP \ +# FIXICNH.SHP \ +# FTNKICNH.SHP \ +# GTWRICNH.SHP \ +# GUNICNH.SHP \ +# HANDICNH.SHP \ +# HARVICNH.SHP \ +# HELIICNH.SHP \ +# HOSPICNH.SHP \ +# HPADICNH.SHP \ +# HQICNH.SHP \ +# HTNKICNH.SHP \ +# JEEPICNH.SHP \ +# LSTICNH.SHP \ +# LTNKICNH.SHP \ +# MCVICNH.SHP \ +# MHQICNH.SHP \ +# MLRSICNH.SHP \ +# MTNKICNH.SHP \ +# NUKEICNH.SHP \ +# NUK2ICNH.SHP \ +# ORCAICNH.SHP \ +# OBLIICNH.SHP \ +# PROCICNH.SHP \ +# PUMPICNH.SHP \ +# PYLEICNH.SHP \ +# RMBOICNH.SHP \ +# ROADICNH.SHP \ +# SAMICNH.SHP \ +# SBAGICNH.SHP \ +# SILOICNH.SHP \ +# STNKICNH.SHP \ +# TMPLICNH.SHP \ +# TRANICNH.SHP \ +# WEAPICNH.SHP \ +# WOODICNH.SHP \ +# MSAMICNH.SHP \ + + +UPDATEFILES = \ +# DESERT.PAL \ +# LASTSCEN.PAL \ + ATTRACT2.CPS \ + LASTSCNG.PAL \ + LASTSCNB.PAL \ + MAP1.PAL \ + MAP_GRY2.PAL \ + MAP_LOC2.PAL \ + MAP_LOC3.PAL \ + MAP_LOCL.PAL \ + MAP_PROG.PAL \ + MULTSCOR.PAL \ + SATSELIN.PAL \ + SCORPAL1.PAL \ + SIDES.PAL \ + SNODPAL1.PAL \ + AIRSTRK.VQP \ + AKIRA.VQP \ + BANNER.VQP \ + BCANYON.VQP \ + BKGROUND.VQP \ + BOMBAWAY.VQP \ + BOMBFLEE.VQP \ + BURDET1.VQP \ + BURDET2.VQP \ + CC2TEASE.VQP \ + OLDCC2T.VQP \ + CCLOGO.VQP \ + CONSYARD.VQP \ + DESFLEES.VQP \ + DESKILL.VQP \ + DESOLAT.VQP \ + DESSWEEP.VQP \ + DINO.VQP \ + FLAG.VQP \ + FLYY.VQP \ + FORESTKL.VQP \ + GAMEOVER.VQP \ + GDI1.VQP \ + GDI10.VQP \ + GDI11.VQP \ + GDI12.VQP \ + GDI13.VQP \ + GDI14.VQP \ + GDI15.VQP \ + GDI2.VQP \ + GDI3.VQP \ + GDI3LOSE.VQP \ + GDI4A.VQP \ + GDI4B.VQP \ + GDI5.VQP \ + GDI6.VQP \ + GDI7.VQP \ + GDI8A.VQP \ + GDI8B.VQP \ + GDI9.VQP \ + GDIEND1.VQP \ + GDIEND2.VQP \ + GDIFINA.VQP \ + GDIFINB.VQP \ + GDILOSE.VQP \ + GENERIC.VQP \ + GUNBOAT.VQP \ + HELLVALY.VQP \ + INSITES.VQP \ + INTRO2.VQP \ + KANEPRE.VQP \ + LANDING.VQP \ + LOGO.VQP \ + NAPALM.VQP \ + NEWLOGO.VQP \ + NITEJUMP.VQP \ + NOD1.VQP \ + NOD10A.VQP \ + NOD10B.VQP \ + NOD11.VQP \ + NOD12.VQP \ + NOD13.VQP \ + NOD1PRE.VQP \ + NOD2.VQP \ + NOD3.VQP \ + NOD4A.VQP \ + NOD4B.VQP \ + NOD5.VQP \ + NOD6.VQP \ + NOD7A.VQP \ + NOD7B.VQP \ + NOD8.VQP \ + NOD9.VQP \ + NODEND1.VQP \ + NODEND2.VQP \ + NODEND3.VQP \ + NODEND4.VQP \ + NODFINAL.VQP \ + NODFLEES.VQP \ + NODLOSE.VQP \ + NODSWEEP.VQP \ + NUKE.VQP \ + OBEL.VQP \ + PARATROP.VQP \ + PINTLE.VQP \ + PLANECRA.VQP \ + PODIUM.VQP \ + REFINT.VQP \ + RETRO.VQP \ + RETROGER.VQP \ + SABOTAGE.VQP \ + SAMDIE.VQP \ + SAMSITE.VQP \ + SEIGE.VQP \ + SETHPRE.VQP \ + SPYCRASH.VQP \ + STEALTH.VQP \ + SUNDIAL.VQP \ + TANKGO.VQP \ + TANKKILL.VQP \ + TBRINFO1.VQP \ + TBRINFO2.VQP \ + TBRINFO3.VQP \ + TIBERFX.VQP \ + TRAILER.VQP \ + TRTKIL_D.VQP \ + TURTKILL.VQP \ + VISOR.VQP \ + WWLOGO.VQP \ + BANR_NOD.VQP \ + BLACKOUT.VQP \ + BODYBAGS.VQP \ + INFERNO.VQP \ + IONTEST.VQP \ + REFINERY.VQP \ + 12GREEN.FNT \ + 12GRNGRD.FNT \ + HTITLE.PCX \ + HEARTH_A.WSA \ + HEARTH_E.WSA \ + HBOSNIA.WSA \ + HSAFRICA.WSA \ + + + + + + + +CONQUERFILES = \ + 120MM.SHP \ + 50CAL.SHP \ + A10.SHP \ +# A10ICON.SHP \ + AFLD.SHP \ +# AFLDICON.SHP \ + AFLDMAKE.SHP \ + APC.SHP \ +# APCICON.SHP \ + ARCO.SHP \ +# ARCOICON.SHP \ + ART-EXP1.SHP \ + ARTY.SHP \ +# ARTYICON.SHP \ + ATOMDOOR.SHP \ + ATOMICDN.SHP \ +# ATOMICON.SHP \ + ATOMICUP.SHP \ + ATOMSFX.SHP \ + ATWR.SHP \ +# ATWRICON.SHP \ + ATWRMAKE.SHP \ + BARB.SHP \ +# BARBICON.SHP \ + BGGY.SHP \ +# BGGYICON.SHP \ + BIKE.SHP \ +# BIKEICON.SHP \ + BIO.SHP \ +# BIOICON.SHP \ + BIOMAKE.SHP \ + BOAT.SHP \ +# BOATICON.SHP \ + BOMB.SHP \ +# BOMBICON.SHP \ + BOMBLET.SHP \ + BRIK.SHP \ +# BRIKICON.SHP \ + BTN-DN.SHP \ + BTN-PL.SHP \ + BTN-ST.SHP \ + BTN-UP.SHP \ + BURN-L.SHP \ + BURN-M.SHP \ + BURN-S.SHP \ + C1.SHP \ + C10.SHP \ + C17.SHP \ +# C17ICON.SHP \ + C2.SHP \ + C3.SHP \ + C4.SHP \ + C5.SHP \ + C6.SHP \ + C7.SHP \ + C8.SHP \ + C9.SHP \ + CHAN.SHP \ + CHEM-E.SHP \ + CHEM-N.SHP \ + CHEM-NE.SHP \ + CHEM-NW.SHP \ + CHEM-S.SHP \ + CHEM-SE.SHP \ + CHEM-SW.SHP \ + CHEM-W.SHP \ + CHEMBALL.SHP \ +# CLOCK.SHP \ + CONC.SHP \ + CYCL.SHP \ +# CYCLICON.SHP \ + DELPHI.SHP \ + DEVIATOR.SHP \ + DOLLAR.SHP \ + DRAGON.SHP \ + E1.SHP \ +# E1ICON.SHP \ + E1ROT.SHP \ + E2.SHP \ +# E2ICON.SHP \ + E2ROT.SHP \ + E3.SHP \ +# E3ICON.SHP \ + E3ROT.SHP \ + E4.SHP \ +# E4ICON.SHP \ + E4ROT.SHP \ + E5.SHP \ +# E5ICON.SHP \ + E6.SHP \ +# E6ICON.SHP \ + EARTH.SHP \ + EMPULSE.SHP \ + EYE.SHP \ +# EYEICON.SHP \ + EYEMAKE.SHP \ + FACT.SHP \ +# FACTICON.SHP \ + FACTMAKE.SHP \ + FBALL1.SHP \ + FIRE1.SHP \ + FIRE2.SHP \ + FIRE3.SHP \ + FIRE4.SHP \ + FIX.SHP \ +# FIXICON.SHP \ + FIXMAKE.SHP \ + FLAGFLY.SHP \ + FLAME-E.SHP \ + FLAME-N.SHP \ + FLAME-NE.SHP \ + FLAME-NW.SHP \ + FLAME-S.SHP \ + FLAME-SE.SHP \ + FLAME-SW.SHP \ + FLAME-W.SHP \ + FLMSPT.SHP \ + FPLS.SHP \ + FRAG1.SHP \ + FRAG3.SHP \ + FTNK.SHP \ +# FTNKICON.SHP \ + GTWR.SHP \ +# GTWRICON.SHP \ + GTWRMAKE.SHP \ + GUN.SHP \ + GUNFIRE.SHP \ +# GUNICON.SHP \ + GUNMAKE.SHP \ + HAND.SHP \ +# HANDICON.SHP \ + HANDMAKE.SHP \ + HARV.SHP \ +# HARVICON.SHP \ + HELI.SHP \ +# HELIICON.SHP \ + HOSP.SHP \ +# HOSPICON.SHP \ + HOSPMAKE.SHP \ + HPAD.SHP \ +# HPADICON.SHP \ + HPADMAKE.SHP \ + HQ.SHP \ +# HQICON.SHP \ + HQMAKE.SHP \ + HTNK.SHP \ +# HTNKICON.SHP \ + INVUN.SHP \ +# IONICON.SHP \ + IONSFX.SHP \ + JEEP.SHP \ +# JEEPICON.SHP \ + LROTOR.SHP \ + LST.SHP \ +# LSTICON.SHP \ + LTNK.SHP \ +# LTNKICON.SHP \ + MCV.SHP \ +# MCVICON.SHP \ + MHQ.SHP \ +# MHQICON.SHP \ + MINE.SHP \ + MINIGUN.SHP \ + MISS.SHP \ + MISSILE.SHP \ + MISSILE2.SHP \ + MLRS.SHP \ +# MLRSICON.SHP \ + MOEBIUS.SHP \ + MOVEFLSH.SHP \ + MSAM.SHP \ +# MSAMICON.SHP \ + MTNK.SHP \ +# MTNKICON.SHP \ + NAPALM1.SHP \ + NAPALM2.SHP \ + NAPALM3.SHP \ + NUK2.SHP \ +# NUK2ICON.SHP \ + NUK2MAKE.SHP \ + NUKE.SHP \ +# NUKEICON.SHP \ + NUKEMAKE.SHP \ + OBLI.SHP \ +# OBLIICON.SHP \ + OBLIMAKE.SHP \ + OPTIONS.SHP \ + ORCA.SHP \ +# ORCAICON.SHP \ + PATRIOT.SHP \ + PIFF.SHP \ + PIFFPIFF.SHP \ +## PIPS.SHP \ + POWER.SHP \ + PROC.SHP \ +# PROCICON.SHP \ + PROCMAKE.SHP \ +# PUMPICON.SHP \ + PUMPMAKE.SHP \ + PYLE.SHP \ +# PYLEICON.SHP \ + PYLEMAKE.SHP \ +## RADAR.GDI \ +## RADAR.JP \ +## RADAR.NOD \ + RAPID.SHP \ + RAPT.SHP \ + RMBO.SHP \ +# RMBOICON.SHP \ + ROAD.SHP \ +# ROADICON.SHP \ + RROTOR.SHP \ + SAM.SHP \ + SAMFIRE.SHP \ +# SAMICON.SHP \ + SAMMAKE.SHP \ + SBAG.SHP \ +# SBAGICON.SHP \ + SCRATE.SHP \ + SELECT.SHP \ + SHADOW.SHP \ + SILO.SHP \ +# SILOICON.SHP \ + SILOMAKE.SHP \ + SMOKEY.SHP \ + SMOKE_M.SHP \ + SMOKLAND.SHP \ + SQUISH.SHP \ + STEALTH2.SHP \ + STEG.SHP \ + STNK.SHP \ +# STNKICON.SHP \ +## STRIP.SHP \ +## STRIPDN.SHP \ +## STRIPUP.SHP \ +## TABS.SHP \ + TMPL.SHP \ +# TMPLICON.SHP \ + TMPLMAKE.SHP \ + TRAN.SHP \ +# TRANICON.SHP \ + TRANS.ICN \ + TREX.SHP \ + TRIC.SHP \ + V19.SHP \ + VEH-HIT1.SHP \ + VEH-HIT2.SHP \ + VEH-HIT3.SHP \ + VICE.SHP \ + WAKE.SHP \ + WCRATE.SHP \ + WEAP.SHP \ + WEAP2.SHP \ +# WEAPICON.SHP \ + WEAPMAKE.SHP \ + WOOD.SHP \ +# WOODICON.SHP \ + +# MESSAGE.ENG \ +# FAME.CPS \ +# BTN-CL.SHP \ +# BTN-MINS.SHP \ +# BTN-OP.SHP \ +# BTN-PLUS.SHP \ + +GDIMAPFILES = \ + SCB01EA.BIN \ + SCB01EA.INI \ + SCG01EA.BIN \ + SCG01EA.INI \ + SCG02EA.BIN \ + SCG02EA.INI \ + SCG03EA.BIN \ + SCG03EA.INI \ + SCG04EA.BIN \ + SCG04EA.INI \ + SCG04WA.BIN \ + SCG04WA.INI \ + SCG04WB.BIN \ + SCG04WB.INI \ + SCG05EA.BIN \ + SCG05EA.INI \ + SCG05EB.BIN \ + SCG05EB.INI \ + SCG05WA.BIN \ + SCG05WA.INI \ + SCG05WB.BIN \ + SCG05WB.INI \ + SCG06EA.BIN \ + SCG06EA.INI \ + SCG07EA.BIN \ + SCG07EA.INI \ + SCG08EA.BIN \ + SCG08EA.INI \ + SCG08EB.BIN \ + SCG08EB.INI \ + SCG09EA.BIN \ + SCG09EA.INI \ + SCG10EA.BIN \ + SCG10EA.INI \ + SCG10EB.BIN \ + SCG10EB.INI \ + SCG11EA.BIN \ + SCG11EA.INI \ + SCG12EA.BIN \ + SCG12EA.INI \ + SCG12EB.BIN \ + SCG12EB.INI \ + SCG13EA.BIN \ + SCG13EA.INI \ + SCG13EB.BIN \ + SCG13EB.INI \ + SCG14EA.BIN \ + SCG14EA.INI \ + SCG15EA.BIN \ + SCG15EA.INI \ + SCG15EB.BIN \ + SCG15EB.INI \ + SCG15EC.BIN \ + SCG15EC.INI \ + SCG60EA.BIN \ + SCG60EA.INI \ + SCG61EA.BIN \ + SCG61EA.INI \ + SCG62EA.BIN \ + SCG62EA.INI \ + SCB60EA.BIN \ + SCB60EA.INI \ + SCB61EA.BIN \ + SCB61EA.INI \ + +# SCG03EB.BIN \ +# SCG03EB.INI \ +# SCG03EL.BIN \ +# SCG03EL.INI \ + +NODMAPFILES = \ + SCG01EA.BIN \ + SCG01EA.INI \ + SCB01EA.BIN \ + SCB01EA.INI \ + SCB02EA.BIN \ + SCB02EA.INI \ + SCB02EB.BIN \ + SCB02EB.INI \ + SCB03EA.BIN \ + SCB03EA.INI \ + SCB03EB.BIN \ + SCB03EB.INI \ + SCB04EA.BIN \ + SCB04EA.INI \ + SCB04EB.BIN \ + SCB04EB.INI \ + SCB05EA.BIN \ + SCB05EA.INI \ + SCB06EA.BIN \ + SCB06EA.INI \ + SCB06EB.BIN \ + SCB06EB.INI \ + SCB06EC.BIN \ + SCB06EC.INI \ + SCB07EA.BIN \ + SCB07EA.INI \ + SCB07EB.BIN \ + SCB07EB.INI \ + SCB07EC.BIN \ + SCB07EC.INI \ + SCB08EA.BIN \ + SCB08EA.INI \ + SCB08EB.BIN \ + SCB08EB.INI \ + SCB09EA.BIN \ + SCB09EA.INI \ + SCB10EA.BIN \ + SCB10EA.INI \ + SCB10EB.BIN \ + SCB10EB.INI \ + SCB11EA.BIN \ + SCB11EA.INI \ + SCB11EB.BIN \ + SCB11EB.INI \ + SCB12EA.BIN \ + SCB12EA.INI \ + SCB13EA.BIN \ + SCB13EA.INI \ + SCB13EB.BIN \ + SCB13EB.INI \ + SCB13EC.BIN \ + SCB13EC.INI \ + SCG60EA.BIN \ + SCG60EA.INI \ + SCG61EA.BIN \ + SCG61EA.INI \ + SCG62EA.BIN \ + SCG62EA.INI \ + SCB60EA.BIN \ + SCB60EA.INI \ + SCB61EA.BIN \ + SCB61EA.INI \ + +NETMAPFILES = \ + SCJ01EA.BIN \ + SCJ01EA.INI \ + SCJ02EA.BIN \ + SCJ02EA.INI \ + SCJ03EA.BIN \ + SCJ03EA.INI \ + SCJ04EA.BIN \ + SCJ04EA.INI \ + SCJ05EA.BIN \ + SCJ05EA.INI \ + SCM01EA.BIN \ + SCM01EA.INI \ + SCM02EA.BIN \ + SCM02EA.INI \ + SCM03EA.BIN \ + SCM03EA.INI \ + SCM04EA.BIN \ + SCM04EA.INI \ + SCM05EA.BIN \ + SCM05EA.INI \ + SCM06EA.BIN \ + SCM06EA.INI \ + SCM07EA.BIN \ + SCM07EA.INI \ + SCM08EA.BIN \ + SCM08EA.INI \ + SCM09EA.BIN \ + SCM09EA.INI \ + SCM70EA.BIN \ + SCM70EA.INI \ + SCM71EA.BIN \ + SCM71EA.INI \ + SCM72EA.BIN \ + SCM72EA.INI \ + SCM73EA.BIN \ + SCM73EA.INI \ + SCM74EA.BIN \ + SCM74EA.INI \ + SCM77EA.BIN \ + SCM77EA.INI \ + SCM96EA.BIN \ + SCM96EA.INI \ + + + +JAPANFILES = \ + SCB01EA.CPS \ + SCB01EB.CPS \ + SCB02EA.CPS \ + SCB02EB.CPS \ + SCB03EA.CPS \ + SCB03EB.CPS \ + SCB04EA.CPS \ + SCB04EB.CPS \ + SCB05EA.CPS \ + SCB06EA.CPS \ + SCB06EB.CPS \ + SCB06EC.CPS \ + SCB07EA.CPS \ + SCB07EB.CPS \ + SCB07EC.CPS \ + SCB08EA.CPS \ + SCB08EB.CPS \ + SCB09EA.CPS \ + SCB09EB.CPS \ + SCB10EA.CPS \ + SCB10EB.CPS \ + SCB11EA.CPS \ + SCB11EB.CPS \ + SCB12EA.CPS \ + SCB12EB.CPS \ + SCB13EA.CPS \ + SCB13EB.CPS \ + SCB13EC.CPS \ + SCG01EA.CPS \ + SCG02EA.CPS \ + SCG03EA.CPS \ + SCG03EB.CPS \ + SCG04EA.CPS \ + SCG04WA.CPS \ + SCG04WB.CPS \ + SCG05EA.CPS \ + SCG05EB.CPS \ + SCG05WA.CPS \ + SCG05WB.CPS \ + SCG06EA.CPS \ + SCG07EA.CPS \ + SCG08EA.CPS \ + SCG08EB.CPS \ + SCG09EA.CPS \ + SCG10EA.CPS \ + SCG10EB.CPS \ + SCG11EA.CPS \ + SCG12EA.CPS \ + SCG12EB.CPS \ + SCG13EA.CPS \ + SCG13EB.CPS \ + SCG14EA.CPS \ + SCG15EA.CPS \ + SCG15EB.CPS \ + SCG15EC.CPS \ + + + + + +# Files for hard drive to allow quick access, but never cached. +GENERALFILES = \ + MISSION.INI \ + TITLE.CPS \ + +TEMPERATEFILES = \ + SPLIT2.TEM \ + SPLIT3.TEM \ + BIB1.TEM \ + BIB2.TEM \ + BIB3.TEM \ + B1.TEM \ + B2.TEM \ + B3.TEM \ + BRIDGE1.TEM \ + BRIDGE1D.TEM \ + BRIDGE2.TEM \ + BRIDGE2D.TEM \ + CLEAR1.TEM \ + CR1.TEM \ + CR2.TEM \ + CR3.TEM \ + CR4.TEM \ + CR5.TEM \ + CR6.TEM \ + D01.TEM \ + D02.TEM \ + D03.TEM \ + D04.TEM \ + D05.TEM \ + D06.TEM \ + D07.TEM \ + D08.TEM \ + D09.TEM \ + D10.TEM \ + D11.TEM \ + D12.TEM \ + D13.TEM \ + D14.TEM \ + D15.TEM \ + D16.TEM \ + D17.TEM \ + D18.TEM \ + D19.TEM \ + D20.TEM \ + D21.TEM \ + D22.TEM \ + D23.TEM \ + D24.TEM \ + D25.TEM \ + D26.TEM \ + D27.TEM \ + D28.TEM \ + D29.TEM \ + D30.TEM \ + D31.TEM \ + D32.TEM \ + D33.TEM \ + D34.TEM \ + D35.TEM \ + D36.TEM \ + D37.TEM \ + D38.TEM \ + D39.TEM \ + D40.TEM \ + D41.TEM \ + D42.TEM \ + D43.TEM \ + FALLS1.TEM \ + FALLS2.TEM \ + FORD1.TEM \ + FORD2.TEM \ + P01.TEM \ + P02.TEM \ + P03.TEM \ + P04.TEM \ + P07.TEM \ + P08.TEM \ + P13.TEM \ + P14.TEM \ + RV01.TEM \ + RV02.TEM \ + RV03.TEM \ + RV04.TEM \ + RV05.TEM \ + RV06.TEM \ + RV07.TEM \ + RV08.TEM \ + RV09.TEM \ + RV10.TEM \ + RV11.TEM \ + RV12.TEM \ + RV13.TEM \ + S01.TEM \ + S02.TEM \ + S03.TEM \ + S04.TEM \ + S05.TEM \ + S06.TEM \ + S07.TEM \ + S08.TEM \ + S09.TEM \ + S10.TEM \ + S11.TEM \ + S12.TEM \ + S13.TEM \ + S14.TEM \ + S15.TEM \ + S16.TEM \ + S17.TEM \ + S18.TEM \ + S19.TEM \ + S20.TEM \ + S21.TEM \ + S22.TEM \ + S23.TEM \ + S24.TEM \ + S25.TEM \ + S26.TEM \ + S27.TEM \ + S28.TEM \ + S29.TEM \ + S30.TEM \ + S31.TEM \ + S32.TEM \ + S33.TEM \ + S34.TEM \ + S35.TEM \ + S36.TEM \ + S37.TEM \ + S38.TEM \ + SC1.TEM \ + SC2.TEM \ + SC3.TEM \ + SC4.TEM \ + SC5.TEM \ + SC6.TEM \ + SH1.TEM \ + SH10.TEM \ + SH11.TEM \ + SH12.TEM \ + SH13.TEM \ + SH14.TEM \ + SH15.TEM \ + SH16.TEM \ + SH17.TEM \ + SH18.TEM \ + SH32.TEM \ + SH33.TEM \ + SH34.TEM \ + SH35.TEM \ + SH2.TEM \ + SH3.TEM \ + SH4.TEM \ + SH5.TEM \ + SH6.TEM \ + SH7.TEM \ + SH8.TEM \ + SH9.TEM \ + SR1.TEM \ + SR2.TEM \ + T01.TEM \ + T02.TEM \ + T03.TEM \ + T05.TEM \ + T06.TEM \ + T07.TEM \ + T08.TEM \ + T10.TEM \ + T11.TEM \ + T12.TEM \ + T13.TEM \ + T14.TEM \ + T15.TEM \ + T16.TEM \ + T17.TEM \ + TC01.TEM \ + TC02.TEM \ + TC03.TEM \ + TC04.TEM \ + TC05.TEM \ + TCLOCK.MRF \ + TWHITE.MRF \ + TEMPERAT.PAL \ + TGREEN.MRF \ + TI1.TEM \ + TI10.TEM \ + TI11.TEM \ + TI12.TEM \ + TI2.TEM \ + TI3.TEM \ + TI4.TEM \ + TI5.TEM \ + TI6.TEM \ + TI7.TEM \ + TI8.TEM \ + TI9.TEM \ + TLIGHT.MRF \ + TMOUSE.MRF \ + TRED.MRF \ + TSHADE.MRF \ + TSHADOW.MRF \ + TTRANS.MRF \ + TUNITS.MRF \ + TYELLOW.MRF \ + V01.TEM \ + V02.TEM \ + V03.TEM \ + V04.TEM \ + V05.TEM \ + V06.TEM \ + V07.TEM \ + V08.TEM \ + V09.TEM \ + V10.TEM \ + V11.TEM \ + V12.TEM \ + V13.TEM \ + V14.TEM \ + V15.TEM \ + V16.TEM \ + V17.TEM \ + V18.TEM \ + W1.TEM \ + W2.TEM \ + + +TEMPERATEICONFILES = \ + A10ICNH.TEM \ + AFLDICNH.TEM \ + APCICNH.TEM \ + ARTYICNH.TEM \ + ATWRICNH.TEM \ + BARBICNH.TEM \ + BGGYICNH.TEM \ + BIKEICNH.TEM \ + BOATICNH.TEM \ + BRIKICNH.TEM \ + E1ICNH.TEM \ + E2ICNH.TEM \ + E3ICNH.TEM \ + E4ICNH.TEM \ + E5ICNH.TEM \ + E6ICNH.TEM \ + EYEICNH.TEM \ + FIXICNH.TEM \ + GTWRICNH.TEM \ + GUNICNH.TEM \ + HANDICNH.TEM \ + HARVICNH.TEM \ + HELIICNH.TEM \ + HPADICNH.TEM \ + HQICNH.TEM \ + HTNKICNH.TEM \ + JEEPICNH.TEM \ + LSTICNH.TEM \ + LTNKICNH.TEM \ + MCVICNH.TEM \ + MSAMICNH.TEM \ + MTNKICNH.TEM \ + NUK2ICNH.TEM \ + NUKEICNH.TEM \ + OBLIICNH.TEM \ + ORCAICNH.TEM \ + PROCICNH.TEM \ + PYLEICNH.TEM \ + SAMICNH.TEM \ + SBAGICNH.TEM \ + SILOICNH.TEM \ + STNKICNH.TEM \ + TMPLICNH.TEM \ + TRANICNH.TEM \ + WEAPICNH.TEM \ + CYCLICNH.TEM \ + MLRSICNH.TEM \ + FTNKICNH.TEM \ + IONICNH.TEM \ + ATOMICNH.TEM \ + BOMBICNH.TEM \ + RMBOICNH.TEM \ + WOODICNH.TEM \ + + + +WINTERFILES = \ + SPLIT2.WIN \ + SPLIT3.WIN \ + BIB1.WIN \ + BIB2.WIN \ + BIB3.WIN \ + B1.WIN \ + B2.WIN \ + B3.WIN \ + BRIDGE1.WIN \ + BRIDGE1D.WIN \ + BRIDGE2.WIN \ + BRIDGE2D.WIN \ + RV01.WIN \ + RV02.WIN \ + RV03.WIN \ + RV04.WIN \ + RV05.WIN \ + RV06.WIN \ + RV07.WIN \ + RV08.WIN \ + RV09.WIN \ + RV10.WIN \ + RV11.WIN \ + RV12.WIN \ + RV13.WIN \ + CLEAR1.WIN \ + CR1.WIN \ + CR2.WIN \ + CR3.WIN \ + CR4.WIN \ + CR5.WIN \ + CR6.WIN \ + D01.WIN \ + D02.WIN \ + D03.WIN \ + D04.WIN \ + D05.WIN \ + D06.WIN \ + D07.WIN \ + D08.WIN \ + D09.WIN \ + D10.WIN \ + D11.WIN \ + D12.WIN \ + D13.WIN \ + D14.WIN \ + D15.WIN \ + D16.WIN \ + D17.WIN \ + D18.WIN \ + D19.WIN \ + D20.WIN \ + D21.WIN \ + D22.WIN \ + D23.WIN \ + D24.WIN \ + D25.WIN \ + D26.WIN \ + D27.WIN \ + D28.WIN \ + D29.WIN \ + D30.WIN \ + D31.WIN \ + D32.WIN \ + D33.WIN \ + D34.WIN \ + D35.WIN \ + D36.WIN \ + D37.WIN \ + D38.WIN \ + D39.WIN \ + D40.WIN \ + D41.WIN \ + D42.WIN \ + D43.WIN \ + FALLS1.WIN \ + FALLS2.WIN \ + FORD1.WIN \ + FORD2.WIN \ + P07.WIN \ + P08.WIN \ + P13.WIN \ + P14.WIN \ + P15.WIN \ + P16.WIN \ + P17.WIN \ + P18.WIN \ + P19.WIN \ + P20.WIN \ + S01.WIN \ + S02.WIN \ + S03.WIN \ + S04.WIN \ + S05.WIN \ + S06.WIN \ + S07.WIN \ + S08.WIN \ + S09.WIN \ + S10.WIN \ + S11.WIN \ + S12.WIN \ + S13.WIN \ + S14.WIN \ + S15.WIN \ + S16.WIN \ + S17.WIN \ + S18.WIN \ + S19.WIN \ + S20.WIN \ + S21.WIN \ + S22.WIN \ + S23.WIN \ + S24.WIN \ + S25.WIN \ + S26.WIN \ + S27.WIN \ + S28.WIN \ + S29.WIN \ + S30.WIN \ + S31.WIN \ + S32.WIN \ + S33.WIN \ + S34.WIN \ + S35.WIN \ + S36.WIN \ + S37.WIN \ + S38.WIN \ + SC1.WIN \ + SC2.WIN \ + SC3.WIN \ + SC4.WIN \ + SC5.WIN \ + SC6.WIN \ + SH1.WIN \ + SH10.WIN \ + SH11.WIN \ + SH12.WIN \ + SH13.WIN \ + SH14.WIN \ + SH15.WIN \ + SH16.WIN \ + SH17.WIN \ + SH18.WIN \ + SH32.WIN \ + SH33.WIN \ + SH34.WIN \ + SH35.WIN \ + SH2.WIN \ + SH3.WIN \ + SH4.WIN \ + SH5.WIN \ + SH6.WIN \ + SH7.WIN \ + SH8.WIN \ + SH9.WIN \ + SR1.WIN \ + SR1.WIN \ + SR2.WIN \ + SR2.WIN \ + T01.WIN \ + T02.WIN \ + T03.WIN \ + T05.WIN \ + T06.WIN \ + T07.WIN \ + T08.WIN \ + T10.WIN \ + T11.WIN \ + T12.WIN \ + T13.WIN \ + T14.WIN \ + T15.WIN \ + T16.WIN \ + T17.WIN \ + TC01.WIN \ + TC02.WIN \ + TC03.WIN \ + TC04.WIN \ + TC05.WIN \ + TI1.WIN \ + TI10.WIN \ + TI11.WIN \ + TI12.WIN \ + TI2.WIN \ + TI3.WIN \ + TI4.WIN \ + TI5.WIN \ + TI6.WIN \ + TI7.WIN \ + TI8.WIN \ + TI9.WIN \ + W1.WIN \ + W2.WIN \ + WWHITE.MRF \ + WCLOCK.MRF \ + WGREEN.MRF \ + WINTER.PAL \ + WLIGHT.MRF \ + WMOUSE.MRF \ + WRED.MRF \ + WSHADE.MRF \ + WSHADOW.MRF \ + WTRANS.MRF \ + WUNITS.MRF \ + WYELLOW.MRF \ + V01.WIN \ + V02.WIN \ + V03.WIN \ + V04.WIN \ + V05.WIN \ + V06.WIN \ + V07.WIN \ + V08.WIN \ + V09.WIN \ + V10.WIN \ + V11.WIN \ + V12.WIN \ + V13.WIN \ + V14.WIN \ + V15.WIN \ + V16.WIN \ + V17.WIN \ + V18.WIN \ + + +WINTERICONFILES = \ + A10ICNH.WIN \ + AFLDICNH.WIN \ + APCICNH.WIN \ + ARTYICNH.WIN \ + ATWRICNH.WIN \ + BARBICNH.WIN \ + BGGYICNH.WIN \ + BIKEICNH.WIN \ + BOATICNH.WIN \ + BRIKICNH.WIN \ + E1ICNH.WIN \ + E2ICNH.WIN \ + E3ICNH.WIN \ + E4ICNH.WIN \ + E5ICNH.WIN \ + E6ICNH.WIN \ + EYEICNH.WIN \ + FIXICNH.WIN \ + GTWRICNH.WIN \ + GUNICNH.WIN \ + HANDICNH.WIN \ + HARVICNH.WIN \ + HELIICNH.WIN \ + HPADICNH.WIN \ + HQICNH.WIN \ + HTNKICNH.WIN \ + JEEPICNH.WIN \ + LSTICNH.WIN \ + LTNKICNH.WIN \ + MCVICNH.WIN \ + MSAMICNH.WIN \ + MTNKICNH.WIN \ + NUK2ICNH.WIN \ + NUKEICNH.WIN \ + OBLIICNH.WIN \ + ORCAICNH.WIN \ + PROCICNH.WIN \ + PYLEICNH.WIN \ + SAMICNH.WIN \ + SBAGICNH.WIN \ + SILOICNH.WIN \ + STNKICNH.WIN \ + TMPLICNH.WIN \ + TRANICNH.WIN \ + WEAPICNH.WIN \ + CYCLICNH.WIN \ + MLRSICNH.WIN \ + FTNKICNH.WIN \ + IONICNH.WIN \ + ATOMICNH.WIN \ + BOMBICNH.WIN \ + RMBOICNH.WIN \ + WOODICNH.WIN \ + + +# BB1.WIN \ +# BB2.WIN \ +# BB3.WIN \ + + +DESERTFILES = \ + SPLIT3.DES \ + BIB1.DES \ + BIB2.DES \ + BIB3.DES \ + DESERT.PAL \ + B1.DES \ + B2.DES \ + B4.DES \ + B5.DES \ + B6.DES \ + BR1.DES \ + BR10.DES \ + BR2.DES \ + BR3.DES \ + BR4.DES \ + BR5.DES \ + BR6.DES \ + BR7.DES \ + BR8.DES \ + BR9.DES \ + BRIDGE3.DES \ + BRIDGE3D.DES \ + BRIDGE4.DES \ + BRIDGE4D.DES \ + CLEAR1.DES \ + CR1.DES \ + CR2.DES \ + CR3.DES \ + CR4.DES \ + CR5.DES \ + CR6.DES \ + D01.DES \ + D02.DES \ + D03.DES \ + D04.DES \ + D05.DES \ + D06.DES \ + D07.DES \ + D08.DES \ + D09.DES \ + D10.DES \ + D11.DES \ + D12.DES \ + D13.DES \ + D14.DES \ + D15.DES \ + D16.DES \ + D17.DES \ + D18.DES \ + D19.DES \ + D20.DES \ + D21.DES \ + D22.DES \ + D23.DES \ + D24.DES \ + D25.DES \ + D26.DES \ + D27.DES \ + D28.DES \ + D29.DES \ + D30.DES \ + D31.DES \ + D32.DES \ + D33.DES \ + D34.DES \ + D35.DES \ + D36.DES \ + D37.DES \ + D38.DES \ + D39.DES \ + D40.DES \ + D41.DES \ + D42.DES \ + D43.DES \ + DWHITE.MRF \ + DCLOCK.MRF \ + DGREEN.MRF \ + DLIGHT.MRF \ + DMOUSE.MRF \ + DRED.MRF \ + DSHADE.MRF \ + DSHADOW.MRF \ + DTRANS.MRF \ + DUNITS.MRF \ + DYELLOW.MRF \ + FALLS1.DES \ + FALLS2.DES \ + FORD1.DES \ + FORD2.DES \ + P01.DES \ + P02.DES \ + P03.DES \ + P04.DES \ + P05.DES \ + P06.DES \ + P07.DES \ + ROCK1.DES \ + ROCK2.DES \ + ROCK3.DES \ + ROCK4.DES \ + ROCK5.DES \ + ROCK6.DES \ + ROCK7.DES \ + RV14.DES \ + RV15.DES \ + RV16.DES \ + RV17.DES \ + RV18.DES \ + RV19.DES \ + RV20.DES \ + RV21.DES \ + RV22.DES \ + RV23.DES \ + RV24.DES \ + RV25.DES \ + S01.DES \ + S02.DES \ + S03.DES \ + S04.DES \ + S05.DES \ + S06.DES \ + S07.DES \ + S08.DES \ + S09.DES \ + S10.DES \ + S11.DES \ + S12.DES \ + S13.DES \ + S14.DES \ + S15.DES \ + S16.DES \ + S17.DES \ + S18.DES \ + S19.DES \ + S20.DES \ + S21.DES \ + S22.DES \ + S23.DES \ + S24.DES \ + S25.DES \ + S26.DES \ + S27.DES \ + S28.DES \ + S29.DES \ + S30.DES \ + S31.DES \ + S32.DES \ + S33.DES \ + S34.DES \ + S35.DES \ + S36.DES \ + S37.DES \ + S38.DES \ + SC1.DES \ + SC2.DES \ + SC3.DES \ + SC4.DES \ + SC5.DES \ + SC6.DES \ + SH17.DES \ + SH18.DES \ + SH19.DES \ + SH20.DES \ + SH21.DES \ + SH22.DES \ + SH23.DES \ + SH24.DES \ + SH25.DES \ + SH26.DES \ + SH27.DES \ + SH28.DES \ + SH29.DES \ + SH30.DES \ + SH31.DES \ + SH36.DES \ + SH37.DES \ + SH38.DES \ + SH39.DES \ + SH40.DES \ + SH41.DES \ + SH42.DES \ + SH43.DES \ + SH44.DES \ + SH45.DES \ + SH46.DES \ + SH47.DES \ + SH48.DES \ + SH49.DES \ + SH50.DES \ + SH51.DES \ + SH52.DES \ + SH53.DES \ + SH54.DES \ + SH55.DES \ + SH56.DES \ + SH57.DES \ + SH58.DES \ + SH59.DES \ + SH60.DES \ + SH61.DES \ + SH62.DES \ + SH63.DES \ + T04.DES \ + T08.DES \ + T09.DES \ + T18.DES \ + TI1.DES \ + TI10.DES \ + TI11.DES \ + TI12.DES \ + TI2.DES \ + TI3.DES \ + TI4.DES \ + TI5.DES \ + TI6.DES \ + TI7.DES \ + TI8.DES \ + TI9.DES \ + W1.DES \ + V20.DES \ + V21.DES \ + V22.DES \ + V23.DES \ + V24.DES \ + V25.DES \ + V26.DES \ + V27.DES \ + V28.DES \ + V29.DES \ + V30.DES \ + V31.DES \ + V32.DES \ + V33.DES \ + V34.DES \ + V35.DES \ + V36.DES \ + V37.DES \ + + +DESERTICONFILES = \ + A10ICNH.DES \ + AFLDICNH.DES \ + APCICNH.DES \ + ARTYICNH.DES \ + ATWRICNH.DES \ + BARBICNH.DES \ + BGGYICNH.DES \ + BIKEICNH.DES \ + BOATICNH.DES \ + BRIKICNH.DES \ + E1ICNH.DES \ + E2ICNH.DES \ + E3ICNH.DES \ + E4ICNH.DES \ + E5ICNH.DES \ + E6ICNH.DES \ + EYEICNH.DES \ + FIXICNH.DES \ + GTWRICNH.DES \ + GUNICNH.DES \ + HANDICNH.DES \ + HARVICNH.DES \ + HELIICNH.DES \ + HPADICNH.DES \ + HQICNH.DES \ + HTNKICNH.DES \ + JEEPICNH.DES \ + LSTICNH.DES \ + LTNKICNH.DES \ + MCVICNH.DES \ + MSAMICNH.DES \ + MTNKICNH.DES \ + NUK2ICNH.DES \ + NUKEICNH.DES \ + OBLIICNH.DES \ + ORCAICNH.DES \ + PROCICNH.DES \ + PYLEICNH.DES \ + SAMICNH.DES \ + SBAGICNH.DES \ + SILOICNH.DES \ + STNKICNH.DES \ + TMPLICNH.DES \ + TRANICNH.DES \ + WEAPICNH.DES \ + CYCLICNH.DES \ + MLRSICNH.DES \ + FTNKICNH.DES \ + IONICNH.DES \ + ATOMICNH.DES \ + BOMBICNH.DES \ + RMBOICNH.DES \ + WOODICNH.DES \ + + + + + +# Sound effects (Juvenile or Adult) +SFX = \ + CRATE4.AUD \ + HELIDOWN.AUD \ + HELIUP1.AUD \ + BAZOOK1.AUD \ + BLEEP2.AUD \ + BOMB1.AUD \ + BUTTON.AUD \ + COMCNTR1.AUD \ + CONSTRU2.AUD \ + CRUMBLE.AUD \ + FLAMER2.AUD \ + GUN18.AUD \ + GUN19.AUD \ + GUN20.AUD \ + GUN5.AUD \ + GUN8.AUD \ + GUNCLIP1.AUD \ + HVYDOOR1.AUD \ + HVYGUN10.AUD \ + ION1.AUD \ + MGUN11.AUD \ + MGUN2.AUD \ + NUKEMISL.AUD \ + NUKEXPLO.AUD \ + OBELRAY1.AUD \ + POWRDN1.AUD \ + RAMGUN2.AUD \ + ROCKET1.AUD \ + ROCKET2.AUD \ + SAMMOTR2.AUD \ + SCOLD2.AUD \ + SIDBAR1C.AUD \ + SIDBAR2C.AUD \ + SQUISH2.AUD \ + TNKFIRE2.AUD \ + TNKFIRE3.AUD \ + TNKFIRE4.AUD \ + TNKFIRE6.AUD \ + TONE15.AUD \ + TONE16.AUD \ + TONE2.AUD \ + TONE5.AUD \ + TOSS1.AUD \ + TRANS1.AUD \ + TREEBRN1.AUD \ + TURRFIR5.AUD \ + XPLOBIG4.AUD \ + XPLOBIG6.AUD \ + XPLOBIG7.AUD \ + XPLODE.AUD \ + XPLOS.AUD \ + XPLOSML2.AUD \ + +# Generic wave files (never changes). +WAVFILES = \ + GUYOKAY1.AUD \ + GUYYEAH1.AUD \ + GIRLOKAY.AUD \ + GIRLYEAH.AUD \ + OBELPOWR.AUD \ + YELL1.AUD \ + NUYELL1.AUD \ + NUYELL3.AUD \ + NUYELL4.AUD \ + NUYELL5.AUD \ + NUYELL6.AUD \ + NUYELL7.AUD \ + NUYELL10.AUD \ + NUYELL11.AUD \ + NUYELL12.AUD \ + NEWTARG1.AUD \ + APPEAR1.AUD \ + BEEPY2.AUD \ + BEEPY3.AUD \ + BEEPY6.AUD \ + CASHTURN.AUD \ + CLOCK1.AUD \ + COUNTER1.AUD \ + COUNTRY1.AUD \ + COUNTRY4.AUD \ + KEYSTROK.AUD \ + SFX4.AUD \ + SCOLD1.AUD \ + TARGET1.AUD \ + TARGET2.AUD \ + TARGET3.AUD \ + TEXT2.AUD \ + WORLD2.AUD \ + MYES1.AUD \ + MCOMND1.AUD \ + MHELLO1.AUD \ + MHMMM1.AUD \ + MPLAN3.AUD \ + MCOURSE1.AUD \ + MYESYES1.AUD \ + MTIBER1.AUD \ + MTHANKS1.AUD \ +# MMG1.AUD \ +# MACTION1.AUD \ +# MREMARK1.AUD \ +# MPLAN1.AUD \ +# MPLAN2.AUD \ +# MHASTE1.AUD \ +# MONCE1.AUD \ +# MIMMD1.AUD \ + +RESPONSE1 = \ + ACKNO.AUD \ + AFFIRM1.AUD \ + AWAIT1.AUD \ + MOVOUT1.AUD \ + REPORT1.AUD \ + UNIT1.AUD \ + VEHIC1.AUD \ + YESSIR1.AUD \ +# 2DANGR1.AUD \ +# NEGATV1.AUD \ + +RESPONSE2 = \ + ACKNO.AUD \ + AFFIRM1.AUD \ + AWAIT1.AUD \ + MOVOUT1.AUD \ + NOPROB.AUD \ + OVEROUT.AUD \ + READY.AUD \ + REPORT1.AUD \ + RITAWAY.AUD \ + ROGER.AUD \ + UGOTIT.AUD \ + YESSIR1.AUD \ + +RAMBO = \ + BOMBIT1.AUD \ + CMON1.AUD \ + GOTIT1.AUD \ + KEEPEM1.AUD \ + LAUGH1.AUD \ + LEFTY1.AUD \ + NOPRBLM1.AUD \ + ONIT1.AUD \ + RAMYELL1.AUD \ + ROKROLL1.AUD \ + TUFFGUY1.AUD \ + YEAH1.AUD \ + YES1.AUD \ + YO1.AUD \ +# OHSH1.AUD \ + +TSCOREFILES = \ + cps\record.bin \ + CHOOSE.WSA \ + NOD1PRE.VQA \ + STRUGGLE.AUD \ + GDI_SLCT.AUD \ + NOD_SLCT.AUD \ + KANEFINL.AUD \ + LOOPIE6M.AUD \ + WIN1.AUD \ + MAP1.AUD \ + +VARFILES = \ + TROUBLE.AUD \ + AOI.AUD \ + WIN1.AUD \ + BEFEARED.AUD \ + HEART.AUD \ + ROUT.AUD \ + +SCOREFILES = \ + NOD_WIN1.AUD \ + NOD_MAP1.AUD \ + AIRSTRIK.AUD \ + AOI.AUD \ + BEFEARED.AUD \ + CCTHANG.AUD \ + DIE.AUD \ + FWP.AUD \ + HEAVYG.AUD \ + I_AM.AUD \ + IND.AUD \ + IND2.AUD \ + J1.AUD \ + JDI_V2.AUD \ + JUSTDOIT.AUD \ + LINEFIRE.AUD \ + MARCH.AUD \ + NOMERCY.AUD \ + OTP.AUD \ + PRP.AUD \ + RADIO.AUD \ + RAIN.AUD \ + STOPTHEM.AUD \ + TARGET.AUD \ + TROUBLE.AUD \ + VALKYRIE.AUD \ + WARFARE.AUD \ + +# BFEARED.AUD \ +# CCTHANG.AUD \ +# CHOOSE1.AUD \ +# DIE!!.AUD \ +# FWP.AUD \ +# IAM.AUD \ +# IND.AUD \ +# JUSTDOIT.AUD \ +# LINEFIRE.AUD \ +# MARCH.AUD \ +# MECHMAN.AUD \ +# NOMERCY.AUD \ +# OTP.AUD \ +# PRP.AUD \ +# ROUT.AUD \ +# STOPTHEM.AUD \ +# TROUBLE.AUD \ +# WARFARE.AUD \ +# VALK.AUD \ + +SPEECHFILES = \ + UNITLOST.AUD \ + NEEDHARV.AUD \ + STRCLOST.AUD \ + ENMYUNIT.AUD \ + ACCOM1.AUD \ + FAIL1.AUD \ + BLDG1.AUD \ + CONSTRU1.AUD \ + NEWOPT1.AUD \ + DEPLOY1.AUD \ + GDIDEAD1.AUD \ + NODDEAD1.AUD \ + CIVDEAD1.AUD \ + NOCASH1.AUD \ + BATLCON1.AUD \ + REINFOR1.AUD \ + CANCEL1.AUD \ + BLDGING1.AUD \ + LOPOWER1.AUD \ + NOPOWER1.AUD \ + MOCASH1.AUD \ + BASEATK1.AUD \ + INCOME1.AUD \ + ENEMYA.AUD \ + NUKE1.AUD \ + NOBUILD1.AUD \ + PRIBLDG1.AUD \ + NODCAPT1.AUD \ + GDICAPT1.AUD \ + IONCHRG1.AUD \ + IONREDY1.AUD \ + UNITREDY.AUD \ + NUKAVAIL.AUD \ + NUKLNCH1.AUD \ + SELECT1.AUD \ + AIRREDY1.AUD \ + NOREDY1.AUD \ + ENMYAPP1.AUD \ + SILOS1.AUD \ + ONHOLD1.AUD \ + REPAIR1.AUD \ + ESTRUCX.AUD \ + GSTRUC1.AUD \ + NSTRUC1.AUD \ + +# TRANSSEE.AUD \ +# TRANLOAD.AUD \ +# BLUEP.AUD \ +# BLUES.AUD \ +# BLUEU.AUD \ +# CIVS.AUD \ +# CIVU.AUD \ +# DEPART.AUD \ +# DESTROY.AUD \ +# GDIS.AUD \ +# GDIU.AUD \ +# GOLDP.AUD \ +# GOLDS.AUD \ +# GOLDU.AUD \ +# GREENP.AUD \ +# GREENS.AUD \ +# GREENU.AUD \ +# GREYP.AUD \ +# GREYS.AUD \ +# GREYU.AUD \ +# NODS.AUD \ +# NODU.AUD \ +# ORANGEP.AUD \ +# ORANGES.AUD \ +# ORANGEU.AUD \ +# REDP.AUD \ +# REDU.AUD \ +# VICTORY.AUD \ + +# GUKILL1.AUD \ +# GSTRUD1.AUD \ +# GONLINE1.AUD \ +# GLEFT1.AUD \ +# GOLDKILT.AUD \ +# GOLDWIN.AUD \ +# RUKILL1.AUD \ +# RSTRUD1.AUD \ +# RONLINE1.AUD \ +# RLEFT1.AUD \ +# REDKILT.AUD \ +# REDWIN.AUD \ +# GYUKILL1.AUD \ +# GYSTRUD1.AUD \ +# GYONLINE.AUD \ +# GYLEFT1.AUD \ +# GREYKILT.AUD \ +# GREYWIN.AUD \ +# OUKILL1.AUD \ +# OSTRUD1.AUD \ +# OONLINE1.AUD \ +# OLEFT1.AUD \ +# ORANKILT.AUD \ +# ORANWIN.AUD \ +# GNUKILL1.AUD \ +# GNSTRUD1.AUD \ +# GNONLINE.AUD \ +# GNLEFT1.AUD \ +# GRENKILT.AUD \ +# GRENWIN.AUD \ +# BUKILL1.AUD \ +# BSTRUD1.AUD \ +# BONLINE1.AUD \ +# BLEFT1.AUD \ +# BLUEKILT.AUD \ +# BLUEWIN.AUD \ +# REPDONE1.AUD \ +# RADOK1.AUD \ +# RADFATL1.AUD \ +# UPUNIT1.AUD \ +# UPSTRUC1.AUD \ +# EVAYES1.AUD \ +# EVANO1.AUD \ +# SOLD1.AUD \ +# ACHIEV1.AUD \ +# ADJUST1.AUD \ +# AIRRAID1.AUD \ +# BRIGHT1.AUD \ +# COLOR1.AUD \ +# CONQUER1.AUD \ +# CONTRST1.AUD \ +# DEFEAT1.AUD \ +# DISCOV1.AUD \ +# ENEMYE.AUD \ +# ENEMYN.AUD \ +# ENEMYS.AUD \ +# ENEMYW.AUD \ +# EVABOOT1.AUD \ +# EXIT1.AUD \ +# FACTORY1.AUD \ +# FACTUSE1.AUD \ +# FULFILL1.AUD \ +# LOADGAM1.AUD \ +# LOST1.AUD \ +# MUSIC1.AUD \ +# OPTION1.AUD \ +# PLANEA.AUD \ +# PLANEE.AUD \ +# PLANEN.AUD \ +# PLANES.AUD \ +# PLANEW.AUD \ +# PREPARE1.AUD \ +# PROGRES1.AUD \ +# RADHAZ1.AUD \ +# RESTAB1.AUD \ +# SAVED1.AUD \ +# SAVEGAM1.AUD \ +# SOUND1.AUD \ +# SPEED1.AUD \ +# SUCCED1.AUD \ +# THANKS1.AUD \ +# TINT1.AUD \ +# UNAVAIL1.AUD \ + +GDIMOVIES = \ + BANNER.VQA \ + BCANYON.VQA \ + BKGROUND.VQA \ + BOMBAWAY.VQA \ + BOMBFLEE.VQA \ + BURDET1.VQA \ + BURDET2.VQA \ + CC2TEASE.VQA \ + CONSYARD.VQA \ + DESOLAT.VQA \ + DINO.VQA \ + FLAG.VQA \ + FLYY.VQA \ + FORESTKL.VQA \ + GAMEOVER.VQA \ + GDI1.VQA \ + GDI10.VQA \ + GDI11.VQA \ + GDI12.VQA \ + GDI13.VQA \ + GDI14.VQA \ + GDI15.VQA \ + GDI2.VQA \ + GDI3.VQA \ + GDI3LOSE.VQA \ + GDI4A.VQA \ + GDI4B.VQA \ + GDI5.VQA \ + GDI6.VQA \ + GDI7.VQA \ + GDI8A.VQA \ + GDI8B.VQA \ + GDI9.VQA \ + GDIEND1.VQA \ + GDIEND2.VQA \ + GDIFINA.VQA \ + GDIFINB.VQA \ + GDILOSE.VQA \ + GENERIC.VQA \ + GUNBOAT.VQA \ + HELLVALY.VQA \ + INTRO2.VQA \ + LANDING.VQA \ + LOGO.VQA \ + NAPALM.VQA \ + NITEJUMP.VQA \ + NOD1.VQA \ + NODFLEES.VQA \ + NODLOSE.VQA \ + NODLOSE.VQA \ + NODSWEEP.VQA \ + PARATROP.VQA \ + PINTLE.VQA \ + PLANECRA.VQA \ + PODIUM.VQA \ + RETRO.VQA \ + SABOTAGE.VQA \ + SAMDIE.VQA \ + SAMSITE.VQA \ + SEIGE.VQA \ + TBRINFO1.VQA \ + TBRINFO2.VQA \ + TBRINFO3.VQA \ +# TRAILER.VQA \ + TURTKILL.VQA \ + VISOR.VQA \ + +NODMOVIES = \ + AIRSTRK.VQA \ + AKIRA.VQA \ + BANNER.VQA \ + BCANYON.VQA \ + BOMBAWAY.VQA \ + BOMBFLEE.VQA \ + CC2TEASE.VQA \ + CONSYARD.VQA \ + DESFLEES.VQA \ + DESKILL.VQA \ + DESSWEEP.VQA \ + DINO.VQA \ + FLAG.VQA \ + FORESTKL.VQA \ + GAMEOVER.VQA \ + GDI1.VQA \ + GENERIC.VQA \ + INSITES.VQA \ + INTRO2.VQA \ + KANEPRE.VQA \ + LANDING.VQA \ + LOGO.VQA \ + NOD1.VQA \ + NOD10A.VQA \ + NOD10B.VQA \ + NOD11.VQA \ + NOD12.VQA \ + NOD13.VQA \ + NOD1PRE.VQA \ + NOD2.VQA \ + NOD3.VQA \ + NOD4A.VQA \ + NOD4B.VQA \ + NOD5.VQA \ + NOD6.VQA \ + NOD7A.VQA \ + NOD7B.VQA \ + NOD8.VQA \ + NOD9.VQA \ + NODEND1.VQA \ + NODEND2.VQA \ + NODEND3.VQA \ + NODEND4.VQA \ + NODFINAL.VQA \ + NODLOSE.VQA \ + NUKE.VQA \ + OBEL.VQA \ + REFINT.VQA \ + RETRO.VQA \ + SAMSITE.VQA \ + SEIGE.VQA \ + SETHPRE.VQA \ + SPYCRASH.VQA \ + STEALTH.VQA \ + SUNDIAL.VQA \ + TANKGO.VQA \ + TANKKILL.VQA \ + TIBERFX.VQA \ +# TRAILER.VQA \ + TRTKIL_D.VQA \ + VISOR.VQA \ + + +DEMOMFILES= \ + GDI1.VQA \ + GDI10.VQA \ + GDI6.VQA \ + LOGO.VQA \ + n:\code\cps\AOI.A6 \ + n:\code\cps\HEAVYG.A6 \ + +DEMO2MFILES= \ + INTRO2.VQA \ + AOI.AUD \ + HEAVYG.AUD \ + RADIO.AUD \ + LINEFIRE.AUD \ + CCTHANG.AUD \ + MARCH.AUD \ + BCANYON.VQA \ + CONSYARD.VQA \ + DESFLEES.VQA \ + DESKILL.VQA \ + DESSWEEP.VQA \ + FLAG.VQA \ + GAMEOVER.VQA \ + GDI1.VQA \ + GDI10.VQA \ + GDI6.VQA \ + GDILOSE.VQA \ + INSITES.VQA \ + LANDING.VQA \ + LOGO.VQA \ + NITEJUMP.VQA \ + NOD1.VQA \ + NOD5.VQA \ + NOD8.VQA \ + NODFLEES.VQA \ + NODLOSE.VQA \ + SABOTAGE.VQA \ + SAMSITE.VQA \ + SETHPRE.VQA \ + STEALTH.VQA \ + TBRINFO2.VQA \ + TIBERFX.VQA \ + TRAILER.VQA \ + CC2TEASE.VQA \ + +DEMOLFILES= \ + CONQUER.ENG \ + SCOREFNT.FNT \ + GRAD6FNT.FNT \ + 3POINT.FNT \ + 6POINT.FNT \ + 8POINT.FNT \ + 8FAT.FNT \ + FONT12.FNT \ + FONT6.FNT \ + LED.FNT \ + VCR.FNT \ + TEMPERAT.PAL \ + MOUSE.SHP \ + NOD1PRE.VQA \ + NOD1PRE.VQP \ + STRUGGLE.AUD \ + GDI_SLCT.AUD \ + NOD_SLCT.AUD \ + CHOOSE.WSA \ + S-GDIIN2.WSA \ + SCRSCN1.WSA \ + DEMOPIC.PCX \ + PREPICK.PCX \ + MISSION.INI \ + ATTRACT2.CPS \ + 12GREEN.FNT \ + 12GRNGRD.FNT \ + HTITLE.PCX \ + GDI1.VQP \ + GDI10.VQP \ + GDI6.VQP \ + LOGO.VQP \ + HPIPS.SHP \ + HBTN-DN.SHP \ + HBTN-UP.SHP \ + HBTN-DN2.SHP \ + HBTN-UP2.SHP \ + HSIDE1.SHP \ + HSIDE2.SHP \ + HPWRBAR.SHP \ + HREPAIR.SHP \ + HSELL.SHP \ + HMAP.SHP \ + BTN-STH.SHP \ + BTN-PLH.SHP \ + HRADAR.GDI \ + HRADAR.NOD \ + HCLOCK.SHP \ + HPOWER.SHP \ + HSTRIP.SHP \ + HSTRIPDN.SHP \ + HSTRIPUP.SHP \ + HTABS.SHP \ + BTEXTURE.SHP \ + GRAD12FN.FNT \ + SCORPAL1.PAL \ + SIDES.PAL \ + SNODPAL1.PAL \ + ..\maps\demo\SCG10EA.BIN \ + ..\maps\demo\SCG10EA.INI \ + ..\maps\demo\SCG01EA.BIN \ + ..\maps\demo\SCG01EA.INI \ + ..\maps\demo\SCG06EA.BIN \ + ..\maps\demo\SCG06EA.INI \ + + +DEMOCFILES= \ + CONQUER.ENG \ + SCOREFNT.FNT \ + GRAD6FNT.FNT \ + 6POINT.FNT \ + 8POINT.FNT \ + LED.FNT \ + VCR.FNT \ + TEMPERAT.PAL \ + MOUSE.SHP \ + +DEMO2LFILES= \ + NOD1PRE.VQA \ + STRUGGLE.AUD \ + GDI_SLCT.AUD \ + NOD_SLCT.AUD \ + CHOOSE.WSA \ + S-GDIIN2.WSA \ + SCRSCN1.WSA \ + DEMOPIC.CPS \ + PREPICK.CPS \ + PREPICK2.CPS \ + MISSION.INI \ + ATTRACT2.CPS \ + TITLE.CPS \ + ..\maps\demo2\SCG10EA.BIN \ + ..\maps\demo2\SCG10EA.INI \ + ..\maps\demo2\SCG01EA.BIN \ + ..\maps\demo2\SCG01EA.INI \ + ..\maps\demo2\SCG06EA.BIN \ + ..\maps\demo2\SCG06EA.INI \ + ..\maps\demo2\SCB01EA.BIN \ + ..\maps\demo2\SCB01EA.INI \ + ..\maps\demo2\SCB05EA.BIN \ + ..\maps\demo2\SCB05EA.INI \ + ..\maps\demo2\SCB08EA.BIN \ + ..\maps\demo2\SCB08EA.INI \ + + +DEMOFILES= \ + TIME.SHP \ + HISCORE1.SHP \ + HISCORE2.SHP \ + BAR3RED.SHP \ + BAR3YLW.SHP \ + LOGOS.SHP \ + CREDS.SHP \ + VICE.SHP \ + TABS.SHP \ + FLMSPT.SHP \ + PIPS.SHP \ + RROTOR.SHP \ + LROTOR.SHP \ + MOVEFLSH.SHP \ + ATOMDOOR.SHP \ + ATOMICDN.SHP \ + ATOMICUP.SHP \ + BTN-DN.SHP \ + BTN-ST.SHP \ + BTN-UP.SHP \ + BTN-PL.SHP \ + OPTIONS.SHP \ + SCRATE.SHP \ + WCRATE.SHP \ + EYE.SHP \ + EYEICON.SHP \ + EYEMAKE.SHP \ + SMOKLAND.SHP \ + BOMBICON.SHP \ + IONICON.SHP \ + ATOMSFX.SHP \ + ATOMICON.SHP \ + 120MM.SHP \ + 50CAL.SHP \ + A10.SHP \ + A10ICON.SHP \ + AFLD.SHP \ + AFLDICON.SHP \ + AFLDMAKE.SHP \ + APC.SHP \ + APCICON.SHP \ + ARCO.SHP \ + ARCOICON.SHP \ + ART-EXP1.SHP \ + ARTY.SHP \ + ARTYICON.SHP \ + ATWR.SHP \ + ATWRICON.SHP \ + ATWRMAKE.SHP \ + BARB.SHP \ + BARBICON.SHP \ + BGGY.SHP \ + BGGYICON.SHP \ + BIKE.SHP \ + BIKEICON.SHP \ + BIO.SHP \ + BIOICON.SHP \ + BIOMAKE.SHP \ + BOAT.SHP \ + BOATICON.SHP \ + BOMB.SHP \ + BOMBLET.SHP \ + BRIK.SHP \ + BRIKICON.SHP \ + BURN-L.SHP \ + BURN-M.SHP \ + BURN-S.SHP \ + C1.SHP \ + CHAN.SHP \ + C17.SHP \ + C17ICON.SHP \ + C2.SHP \ + C3.SHP \ + C4.SHP \ + C5.SHP \ + C6.SHP \ + C7.SHP \ + DELPHI.SHP \ + C8.SHP \ + C9.SHP \ + C10.SHP \ + CLOCK.SHP \ + CONC.SHP \ + CYCL.SHP \ + CYCLICON.SHP \ + DRAGON.SHP \ + E1.SHP \ + E1ICON.SHP \ + E1ROT.SHP \ + E2.SHP \ + E2ICON.SHP \ + E2ROT.SHP \ + E3.SHP \ + E3ICON.SHP \ + E3ROT.SHP \ + E4.SHP \ + E4ICON.SHP \ + E4ROT.SHP \ + E5.SHP \ + E5ICON.SHP \ + E6.SHP \ + E6ICON.SHP \ + FACT.SHP \ + FACTICON.SHP \ + FACTMAKE.SHP \ + FBALL1.SHP \ + FIRE1.SHP \ + FIRE2.SHP \ + FIRE3.SHP \ + FIRE4.SHP \ + FIX.SHP \ + FIXICON.SHP \ + FIXMAKE.SHP \ + FLAME-N.SHP \ + FLAME-NW.SHP \ + FLAME-S.SHP \ + FLAME-SW.SHP \ + FLAME-W.SHP \ + FLAME-E.SHP \ + FLAME-NE.SHP \ + FLAME-SE.SHP \ + FRAG1.SHP \ + FRAG3.SHP \ + FTNK.SHP \ + FTNKICON.SHP \ + GTWR.SHP \ + GTWRICON.SHP \ + GTWRMAKE.SHP \ + GUN.SHP \ + GUNFIRE.SHP \ + GUNICON.SHP \ + GUNMAKE.SHP \ + HAND.SHP \ + HANDICON.SHP \ + HANDMAKE.SHP \ + HARV.SHP \ + HARVICON.SHP \ + HELI.SHP \ + HELIICON.SHP \ + HOSP.SHP \ + HOSPICON.SHP \ + HOSPMAKE.SHP \ + HPAD.SHP \ + HPADICON.SHP \ + HPADMAKE.SHP \ + HQ.SHP \ + HQICON.SHP \ + HQMAKE.SHP \ + HTNK.SHP \ + HTNKICON.SHP \ + IONSFX.SHP \ + JEEP.SHP \ + JEEPICON.SHP \ + LST.SHP \ + LSTICON.SHP \ + LTNK.SHP \ + LTNKICON.SHP \ + MCV.SHP \ + MCVICON.SHP \ + MHQ.SHP \ + MHQICON.SHP \ + MINIGUN.SHP \ + MISS.SHP \ + MISSILE.SHP \ + MLRS.SHP \ + MLRSICON.SHP \ + MOEBIUS.SHP \ + MTNK.SHP \ + MTNKICON.SHP \ + NAPALM1.SHP \ + NAPALM2.SHP \ + NAPALM3.SHP \ + NUKE.SHP \ + NUKEICON.SHP \ + NUKEMAKE.SHP \ + NUK2.SHP \ + NUK2ICON.SHP \ + NUK2MAKE.SHP \ + ORCA.SHP \ + ORCAICON.SHP \ + OBLI.SHP \ + OBLIICON.SHP \ + OBLIMAKE.SHP \ + PATRIOT.SHP \ + PIFF.SHP \ + PIFFPIFF.SHP \ + POWER.SHP \ + PROC.SHP \ + PROCICON.SHP \ + PROCMAKE.SHP \ + PUMPICON.SHP \ + PUMPMAKE.SHP \ + PYLE.SHP \ + PYLEICON.SHP \ + PYLEMAKE.SHP \ + RADAR.GDI \ + RADAR.NOD \ + RADAR.JP \ + RMBO.SHP \ + RMBOICON.SHP \ + ROAD.SHP \ + ROADICON.SHP \ + SAM.SHP \ + SAMFIRE.SHP \ + SAMICON.SHP \ + SAMMAKE.SHP \ + SBAG.SHP \ + SBAGICON.SHP \ + SELECT.SHP \ + SHADOW.SHP \ + SILO.SHP \ + SILOICON.SHP \ + SILOMAKE.SHP \ + SMOKEY.SHP \ + SMOKE_M.SHP \ + SQUISH.SHP \ + STNK.SHP \ + STNKICON.SHP \ + STRIP.SHP \ + STRIPDN.SHP \ + STRIPUP.SHP \ + TRAN.SHP \ + TRANICON.SHP \ + TRANS.ICN \ + VEH-HIT1.SHP \ + VEH-HIT2.SHP \ + VEH-HIT3.SHP \ + WAKE.SHP \ + WEAP.SHP \ + WEAP2.SHP \ + WEAPICON.SHP \ + WEAPMAKE.SHP \ + WOOD.SHP \ + WOODICON.SHP \ + V19.SHP \ + MSAM.SHP \ + MSAMICON.SHP \ +# COUNTRYA.SHP \ +# CHEMBALL.SHP \ +# COUNTRYE.SHP \ +# 3POINT.FNT \ +# DEVIATOR.SHP \ +# DOLLAR.SHP \ +# EARTH.SHP \ +# EMPULSE.SHP \ +# INVUN.SHP \ +# MINE.SHP \ +# RAPID.SHP \ +# STEALTH2.SHP \ +# MISSILE2.SHP \ +# FLAGFLY.SHP \ +# FPLS.SHP \ +# TRIC.SHP \ +# STEG.SHP \ +# RAPT.SHP \ +# TREX.SHP \ +# TMPL.SHP \ +# TMPLICON.SHP \ +# TMPLMAKE.SHP \ +# CHEM-N.SHP \ +# CHEM-NW.SHP \ +# CHEM-S.SHP \ +# CHEM-SW.SHP \ +# CHEM-W.SHP \ +# CHEM-E.SHP \ +# CHEM-NE.SHP \ +# CHEM-SE.SHP \ +# SPLIT2.TEM \ +# SPLIT3.TEM \ +# BIB1.TEM \ +# BIB2.TEM \ +# BIB3.TEM \ +# B1.TEM \ +# B2.TEM \ +# B3.TEM \ +# BRIDGE1.TEM \ +# BRIDGE1D.TEM \ +# BRIDGE2.TEM \ +# BRIDGE2D.TEM \ +# CLEAR1.TEM \ +# CR1.TEM \ +# CR2.TEM \ +# CR3.TEM \ +# CR4.TEM \ +# CR5.TEM \ +# CR6.TEM \ +# D01.TEM \ +# D02.TEM \ +# D03.TEM \ +# D04.TEM \ +# D05.TEM \ +# D06.TEM \ +# D07.TEM \ +# D08.TEM \ +# D09.TEM \ +# D10.TEM \ +# D11.TEM \ +# D12.TEM \ +# D13.TEM \ +# D14.TEM \ +# D15.TEM \ +# D16.TEM \ +# D17.TEM \ +# D18.TEM \ +# D19.TEM \ +# D20.TEM \ +# D21.TEM \ +# D22.TEM \ +# D23.TEM \ +# D24.TEM \ +# D25.TEM \ +# D26.TEM \ +# D27.TEM \ +# D28.TEM \ +# D29.TEM \ +# D30.TEM \ +# D31.TEM \ +# D32.TEM \ +# D33.TEM \ +# D34.TEM \ +# D35.TEM \ +# D36.TEM \ +# D37.TEM \ +# D38.TEM \ +# D39.TEM \ +# D40.TEM \ +# D41.TEM \ +# D42.TEM \ +# D43.TEM \ +# FALLS1.TEM \ +# FALLS2.TEM \ +# FORD1.TEM \ +# FORD2.TEM \ +# P01.TEM \ +# P02.TEM \ +# P03.TEM \ +# P04.TEM \ +# P07.TEM \ +# P08.TEM \ +# P13.TEM \ +# P14.TEM \ +# RV01.TEM \ +# RV02.TEM \ +# RV03.TEM \ +# RV04.TEM \ +# RV05.TEM \ +# RV06.TEM \ +# RV07.TEM \ +# RV08.TEM \ +# RV09.TEM \ +# RV10.TEM \ +# RV11.TEM \ +# RV12.TEM \ +# RV13.TEM \ +# S01.TEM \ +# S02.TEM \ +# S03.TEM \ +# S04.TEM \ +# S05.TEM \ +# S06.TEM \ +# S07.TEM \ +# S08.TEM \ +# S09.TEM \ +# S10.TEM \ +# S11.TEM \ +# S12.TEM \ +# S13.TEM \ +# S14.TEM \ +# S15.TEM \ +# S16.TEM \ +# S17.TEM \ +# S18.TEM \ +# S19.TEM \ +# S20.TEM \ +# S21.TEM \ +# S22.TEM \ +# S23.TEM \ +# S24.TEM \ +# S25.TEM \ +# S26.TEM \ +# S27.TEM \ +# S28.TEM \ +# S29.TEM \ +# S30.TEM \ +# S31.TEM \ +# S32.TEM \ +# S33.TEM \ +# S34.TEM \ +# S35.TEM \ +# S36.TEM \ +# S37.TEM \ +# S38.TEM \ +# SC1.TEM \ +# SC2.TEM \ +# SC3.TEM \ +# SC4.TEM \ +# SC5.TEM \ +# SC6.TEM \ +# SH1.TEM \ +# SH10.TEM \ +# SH11.TEM \ +# SH12.TEM \ +# SH13.TEM \ +# SH14.TEM \ +# SH15.TEM \ +# SH16.TEM \ +# SH17.TEM \ +# SH18.TEM \ +# SH32.TEM \ +# SH33.TEM \ +# SH34.TEM \ +# SH35.TEM \ +# SH2.TEM \ +# SH3.TEM \ +# SH4.TEM \ +# SH5.TEM \ +# SH6.TEM \ +# SH7.TEM \ +# SH8.TEM \ +# SH9.TEM \ +# SR1.TEM \ +# SR2.TEM \ +# T01.TEM \ +# T02.TEM \ +# T03.TEM \ +# T05.TEM \ +# T06.TEM \ +# T07.TEM \ +# T08.TEM \ +# T10.TEM \ +# T11.TEM \ +# T12.TEM \ +# T13.TEM \ +# T14.TEM \ +# T15.TEM \ +# T16.TEM \ +# T17.TEM \ +# TC01.TEM \ +# TC02.TEM \ +# TC03.TEM \ +# TC04.TEM \ +# TC05.TEM \ +# TCLOCK.MRF \ +# TWHITE.MRF \ +# TEMPERAT.PAL \ +# TGREEN.MRF \ +# TI1.TEM \ +# TI10.TEM \ +# TI11.TEM \ +# TI12.TEM \ +# TI2.TEM \ +# TI3.TEM \ +# TI4.TEM \ +# TI5.TEM \ +# TI6.TEM \ +# TI7.TEM \ +# TI8.TEM \ +# TI9.TEM \ +# TLIGHT.MRF \ +# TMOUSE.MRF \ +# TRED.MRF \ +# TSHADE.MRF \ +# TSHADOW.MRF \ +# TTRANS.MRF \ +# TUNITS.MRF \ +# TYELLOW.MRF \ +# V01.TEM \ +# V02.TEM \ +# V03.TEM \ +# V04.TEM \ +# V05.TEM \ +# V06.TEM \ +# V07.TEM \ +# V08.TEM \ +# V09.TEM \ +# V10.TEM \ +# V11.TEM \ +# V12.TEM \ +# V13.TEM \ +# V14.TEM \ +# V15.TEM \ +# V16.TEM \ +# V17.TEM \ +# V18.TEM \ +# W1.TEM \ +# W2.TEM \ + +DEMO2FILES= \ + TIME.SHP \ + HISCORE1.SHP \ + HISCORE2.SHP \ + BAR3RED.SHP \ + BAR3YLW.SHP \ + LOGOS.SHP \ + CREDS.SHP \ + VICE.SHP \ + TABS.SHP \ + FLMSPT.SHP \ + PIPS.SHP \ + RROTOR.SHP \ + LROTOR.SHP \ + MOVEFLSH.SHP \ + ATOMDOOR.SHP \ + ATOMICDN.SHP \ + ATOMICUP.SHP \ + BTN-DN.SHP \ + BTN-ST.SHP \ + BTN-UP.SHP \ + BTN-PL.SHP \ + OPTIONS.SHP \ + SCRATE.SHP \ + WCRATE.SHP \ + EYE.SHP \ + EYEICON.SHP \ + EYEMAKE.SHP \ + SMOKLAND.SHP \ + BOMBICON.SHP \ + IONICON.SHP \ + ATOMSFX.SHP \ + ATOMICON.SHP \ + 120MM.SHP \ + 50CAL.SHP \ + A10.SHP \ + A10ICON.SHP \ + AFLD.SHP \ + AFLDICON.SHP \ + AFLDMAKE.SHP \ + APC.SHP \ + APCICON.SHP \ + ARCO.SHP \ + ARCOICON.SHP \ + ART-EXP1.SHP \ + ARTY.SHP \ + ARTYICON.SHP \ + ATWR.SHP \ + ATWRICON.SHP \ + ATWRMAKE.SHP \ + BARB.SHP \ + BARBICON.SHP \ + BGGY.SHP \ + BGGYICON.SHP \ + BIKE.SHP \ + BIKEICON.SHP \ + BIO.SHP \ + BIOICON.SHP \ + BIOMAKE.SHP \ + BOAT.SHP \ + BOATICON.SHP \ + BOMB.SHP \ + BOMBLET.SHP \ + BRIK.SHP \ + BRIKICON.SHP \ + BURN-L.SHP \ + BURN-M.SHP \ + BURN-S.SHP \ + C1.SHP \ + CHAN.SHP \ + C17.SHP \ + C17ICON.SHP \ + C2.SHP \ + C3.SHP \ + C4.SHP \ + C5.SHP \ + C6.SHP \ + C7.SHP \ + DELPHI.SHP \ + C8.SHP \ + C9.SHP \ + C10.SHP \ + CLOCK.SHP \ + CONC.SHP \ + CYCL.SHP \ + CYCLICON.SHP \ + DRAGON.SHP \ + E1.SHP \ + E1ICON.SHP \ + E1ROT.SHP \ + E2.SHP \ + E2ICON.SHP \ + E2ROT.SHP \ + E3.SHP \ + E3ICON.SHP \ + E3ROT.SHP \ + E4.SHP \ + E4ICON.SHP \ + E4ROT.SHP \ + E5.SHP \ + E5ICON.SHP \ + E6.SHP \ + E6ICON.SHP \ + FACT.SHP \ + FACTICON.SHP \ + FACTMAKE.SHP \ + FBALL1.SHP \ + FIRE1.SHP \ + FIRE2.SHP \ + FIRE3.SHP \ + FIRE4.SHP \ + FIX.SHP \ + FIXICON.SHP \ + FIXMAKE.SHP \ + FLAME-N.SHP \ + FLAME-NW.SHP \ + FLAME-S.SHP \ + FLAME-SW.SHP \ + FLAME-W.SHP \ + FLAME-E.SHP \ + FLAME-NE.SHP \ + FLAME-SE.SHP \ + FRAG1.SHP \ + FRAG3.SHP \ + FTNK.SHP \ + FTNKICON.SHP \ + GTWR.SHP \ + GTWRICON.SHP \ + GTWRMAKE.SHP \ + GUN.SHP \ + GUNFIRE.SHP \ + GUNICON.SHP \ + GUNMAKE.SHP \ + HAND.SHP \ + HANDICON.SHP \ + HANDMAKE.SHP \ + HARV.SHP \ + HARVICON.SHP \ + HELI.SHP \ + HELIICON.SHP \ + HOSP.SHP \ + HOSPICON.SHP \ + HOSPMAKE.SHP \ + HPAD.SHP \ + HPADICON.SHP \ + HPADMAKE.SHP \ + HQ.SHP \ + HQICON.SHP \ + HQMAKE.SHP \ + HTNK.SHP \ + HTNKICON.SHP \ + IONSFX.SHP \ + JEEP.SHP \ + JEEPICON.SHP \ + LST.SHP \ + LSTICON.SHP \ + LTNK.SHP \ + LTNKICON.SHP \ + MCV.SHP \ + MCVICON.SHP \ + MHQ.SHP \ + MHQICON.SHP \ + MINIGUN.SHP \ + MISS.SHP \ + MISSILE.SHP \ + MLRS.SHP \ + MLRSICON.SHP \ + MOEBIUS.SHP \ + MTNK.SHP \ + MTNKICON.SHP \ + NAPALM1.SHP \ + NAPALM2.SHP \ + NAPALM3.SHP \ + NUKE.SHP \ + NUKEICON.SHP \ + NUKEMAKE.SHP \ + NUK2.SHP \ + NUK2ICON.SHP \ + NUK2MAKE.SHP \ + ORCA.SHP \ + ORCAICON.SHP \ + OBLI.SHP \ + OBLIICON.SHP \ + OBLIMAKE.SHP \ + PATRIOT.SHP \ + PIFF.SHP \ + PIFFPIFF.SHP \ + POWER.SHP \ + PROC.SHP \ + PROCICON.SHP \ + PROCMAKE.SHP \ + PUMPICON.SHP \ + PUMPMAKE.SHP \ + PYLE.SHP \ + PYLEICON.SHP \ + PYLEMAKE.SHP \ + RADAR.GDI \ + RADAR.NOD \ + RADAR.JP \ + RMBO.SHP \ + RMBOICON.SHP \ + ROAD.SHP \ + ROADICON.SHP \ + SAM.SHP \ + SAMFIRE.SHP \ + SAMICON.SHP \ + SAMMAKE.SHP \ + SBAG.SHP \ + SBAGICON.SHP \ + SELECT.SHP \ + SHADOW.SHP \ + SILO.SHP \ + SILOICON.SHP \ + SILOMAKE.SHP \ + SMOKEY.SHP \ + SMOKE_M.SHP \ + SQUISH.SHP \ + STNK.SHP \ + STNKICON.SHP \ + STRIP.SHP \ + STRIPDN.SHP \ + STRIPUP.SHP \ + TRAN.SHP \ + TRANICON.SHP \ + TRANS.ICN \ + VEH-HIT1.SHP \ + VEH-HIT2.SHP \ + VEH-HIT3.SHP \ + WAKE.SHP \ + WEAP.SHP \ + WEAP2.SHP \ + WEAPICON.SHP \ + WEAPMAKE.SHP \ + WOOD.SHP \ + WOODICON.SHP \ + V19.SHP \ + MSAM.SHP \ + MSAMICON.SHP \ + + +LINTOBJECTS1 = $(OBJECTS:,=) +LINTOBJECTS = $(LINTOBJECTS1:.OBJ=.LOB) + +PACKFILES= \ + CONQUER.MIX \ + DESERT.MIX \ + $(.path.cd1)GENERAL.MIX \ + $(.path.cd2)GENERAL.MIX \ + TEMPERAT.MIX \ + WINTER.MIX \ + $(.path.cd1)install\CCLOCAL.MIX \ + $(.path.cd1)install\UPDATE.MIX \ + $(.path.cd1)install\UPDATEC.MIX \ + $(.path.cd1)install\TRANSIT.MIX \ + $(.path.cd1)install\DESEICNH.MIX \ + $(.path.cd1)install\WINTICNH.MIX \ + $(.path.cd1)install\TEMPICNH.MIX \ +# $(.path.cd1)aud1\SPEECH.MIX \ +# SOUNDS.MIX \ +# SCORES.MIX \ +# $(.path.cd1)MOVIES.MIX \ +# $(.path.cd2)MOVIES.MIX \ + +# Limited mixfiles that can be built on home system. +HOMEFILES= \ + CONQUER.MIX \ + DESERT.MIX \ + TEMPERAT.MIX \ + GENERAL.MIX \ + + +############################################################# +# Rebuilds all pack files. +packfiles: $(PACKFILES) + + +############################################################# +# Debug text file creation. +#debug.eng: debug.txt +# utils\textmake -b1000 $(.path.txt)$&.txt $(.path.eng)$&.eng $&.h + +############################################################# +# Demo #2 (Point of Sale version) +demo2: $(.path.dm2)install\DEMO.MIX $(.path.dm2)install\DEMOL.MIX $(.path.dm2)DEMOM.MIX $(.path.dm2)install\DEMOC.MIX + +############################################################# +# Help screen for this complex makefile. +#help: +# type &&! +# This makefile controls many C&C operations. Pass the +# following parameter to MAKE for the desired function. +# --------------------------------------------------------- +# _______________Rebuilds executable. +# PACKFILES______Rebuilds all mixfiles. +#! + +#################################################################### +# Creates the data file that is loaded at the beginning and then not +# freed. +CONQUER.MIX: $(CONQUERFILES) $(CACHEMAP) + UTILS\MIXFILE -I$(.path.cps) &&! + $** +! $(.path.cd1)$&.mix + copy $(.path.cd1)$&.mix $(.path.cd2)$&.mix + +DESERT.MIX: $(DESERTFILES) + UTILS\MIXFILE -I$(.path.cps) &&! + $** +! $(.path.cd1)$&.mix + copy $(.path.cd1)$&.mix $(.path.cd2)$&.mix + +WINTER.MIX: $(WINTERFILES) + UTILS\MIXFILE -I$(.path.cps) &&! + $** +! $(.path.cd1)$&.mix + copy $(.path.cd1)$&.mix $(.path.cd2)$&.mix + +TEMPERAT.MIX: $(TEMPERATEFILES) + UTILS\MIXFILE -I$(.path.cps) &&! + $** +! $(.path.cd1)$&.mix + copy $(.path.cd1)$&.mix $(.path.cd2)$&.mix + +DESEICNH.MIX: $(DESERTICONFILES) + UTILS\MIXFILE -I$(.path.cps) &&! + $** +! $(.path.cd1)install\$&.mix + copy $(.path.cd1)install\$&.mix $(.path.cd2)install\$&.mix + +WINTICNH.MIX: $(WINTERICONFILES) + UTILS\MIXFILE -I$(.path.cps) &&! + $** +! $(.path.cd1)install\$&.mix + copy $(.path.cd1)install\$&.mix $(.path.cd2)install\$&.mix + +TEMPICNH.MIX: $(TEMPERATEICONFILES) + UTILS\MIXFILE -I$(.path.cps) &&! + $** +! $(.path.cd1)install\$&.mix + copy $(.path.cd1)install\$&.mix $(.path.cd2)install\$&.mix + +$(.path.cd1)GENERAL.MIX: $(GENERALFILES) $(GDIMAPFILES) $(NETMAPFILES) $(MAPFILES) + UTILS\MIXFILE -I$(.path.cps) -I$(.path.ini) &&! + $** +! $(.path.cd1)$&.mix + +$(.path.cd2)GENERAL.MIX: $(GENERALFILES) $(NODMAPFILES) $(NETMAPFILES) $(MAPFILES) + UTILS\MIXFILE -I$(.path.cps) -I$(.path.ini) &&! + $** +! $(.path.cd2)$&.mix + +#SCORES8.MIX: $(SCOREFILES:.AUD=.A8) +# UTILS\MIXFILE -E.A8=.AUD -E.V16=.VAR -I$(.path.cps) -I$(.path.ini) &&! +# $** +#! $(.path.mix)$&.mix + +SCORES.MIX: $(SCOREFILES:.AUD=.A6) $(VARFILES:.AUD=.V16) + UTILS\MIXFILE -E.A6=.AUD -E.V16=.VAR -I$(.path.cps) -I$(.path.ini) &&! + $** +! $(.path.cd1)$&.mix + copy $(.path.cd1)$&.mix $(.path.cd2)$&.mix + +#$(.path.mix)8_st\SOUNDS.MIX: $(WAVFILES:.AUD=.A8) $(SFX:.AUD=.A8A) $(SFX:.AUD=.A8J) +# UTILS\MIXFILE -e.A8=.AUD -e.A8A=.AUD -e.A8J=.JUV -I$(.path.cps) &&! +# $** +#! $(.path.mix)8_st\$&.mix + +#$(.path.mix)install\SOUNDS.MIX: $(WAVFILES:.AUD=.A6) $(SFX:.AUD=.A6A) $(SFX:.AUD=.A6J) +# UTILS\MIXFILE -e.A6=.AUD -e.A6A=.AUD -e.A6J=.JUV -I$(.path.cps) &&! +# $** +#! $(.path.mix)install\$&.mix + +# UTILS\MIXFILE -e.A6=.AUD -e.A6A=.AUD -e.A6J=.JUV -E.A60=.V00 -E.A61=.V01 -E.A62=.V02 -E.A63=.V03 -E.A64=.V04 -I$(.path.aud);$(.path.cps) &&! +# UTILS\MIXFILE -eA6=AUD -eA6A=AUD -eA6J=JUV -EA60=V00 -EA61=V01 -EA62=V02 -EA63=V03 -EA64=V04 -I$(.path.aud) &&! +#SOUNDS.MIX: $(WAVFILES:.AUD=.A6) $(SFX:.AUD=.A6A) $(SFX:.AUD=.A6J) $(RESPONSE1:.AUD=.A60) $(RESPONSE2:.AUD=.A61) $(RESPONSE1:.AUD=.A62) $(RESPONSE2:.AUD=.A63) $(RAMBO:.AUD=.A6) $(TSCOREFILES:.AUD=.A6) +SOUNDS.MIX: $(WAVFILES:.AUD=.A6) $(SFX:.AUD=.A6A) $(RESPONSE1:.AUD=.A60) $(RESPONSE2:.AUD=.A61) $(RESPONSE1:.AUD=.A62) $(RESPONSE2:.AUD=.A63) $(RAMBO:.AUD=.A6) + UTILS\MIXFILE -eA6=AUD -eA6A=AUD -EA60=V00 -EA61=V01 -EA62=V02 -EA63=V03 -EA64=V04 -I$(.path.aud) &&! + $** +! $(.path.cd1)$&.mix + copy $(.path.cd1)$&.mix $(.path.cd2)$&.mix + +ZOUNDS.MIX: $(SFX:.AUD=.JUV) + UTILS\MIXFILE -eA6J=JUV -I$(.path.aud) &&! + $** +! $(.path.mix)$&.mix + +#$(.path.mix)8_st\INFANTRY.MIX: $(RESPONSE:.AUD=.A80) $(RESPONSE:.AUD=.A81) $(RESPONSE:.AUD=.A82) $(RESPONSE:.AUD=.A83) $(RAMBO:.AUD=.A8) +# UTILS\MIXFILE -E.A80=.V00 -E.A81=.V01 -E.A82=.V02 -E.A83=.V03 -E.A84=.V04 -E.A8=.AUD -I$(.path.aud) &&! +# $** +#! $(.path.mix)8_st\$&.mix + +#$(.path.mix)install\INFANTRY.MIX: $(RESPONSE1:.AUD=.A60) $(RESPONSE2:.AUD=.A61) $(RESPONSE1:.AUD=.A62) $(RESPONSE2:.AUD=.A63) $(RAMBO:.AUD=.A6) $(TSCOREFILES:.AUD=.A6) +# UTILS\MIXFILE -E.A60=.V00 -E.A61=.V01 -E.A62=.V02 -E.A63=.V03 -E.A64=.V04 -E.A6=.AUD -I$(.path.aud) &&! +# $** +#! $(.path.mix)install\$&.mix + +$(.path.cd1)install\CCLOCAL.MIX: $(LOCALFILES) + UTILS\MIXFILE -E.A6=.AUD -I$(.path.cps) &&! + $** +! $(.path.cd1)install\$&.mix + copy $(.path.cd1)install\$&.mix $(.path.cd2)install\$&.mix + +UPDATE.MIX: $(UPDATEFILES) + UTILS\MIXFILE -E.A6=.AUD -I$(.path.cps) &&! + $** +! $(.path.cd1)install\$&.mix + copy $(.path.cd1)\$&.mix $(.path.cd2)\$&.mix + +UPDATEC.MIX: $(UPDATECFILES) + UTILS\MIXFILE -E.A6=.AUD -I$(.path.cps) &&! + $** +! $(.path.cd1)install\$&.mix + copy $(.path.cd1)install\$&.mix $(.path.cd2)install\$&.mix + +LANGUAGE.MIX: $(JAPANFILES) + UTILS\MIXFILE -E.A6=.AUD -I$(.path.cps) &&! + $** +! $(.path.cd1)install\$&.mix + copy $(.path.cd1)\$&.mix $(.path.cd2)\$&.mix + +$(.path.cd1)install\TRANSIT.MIX: $(TSCOREFILES:.AUD=.A6) + UTILS\MIXFILE -E.A6=.AUD -I$(.path.cps) &&! + $** +! $(.path.cd1)install\$&.mix + copy $(.path.cd1)install\$&.mix $(.path.cd2)install\$&.mix + + +#$(.path.mix)8_st\SPEECH.MIX: $(SPEECHFILES:.AUD=.A8) +# UTILS\MIXFILE -E.A8=.AUD -I$(.path.aud) &&! +# $** +#! $(.path.mix)8_st\$&.mix + +SPEECH.MIX: $(SPEECHFILES:.AUD=.A6) + UTILS\MIXFILE -E.A6=.AUD -I$(.path.aud) &&! + $** +! $(.path.cd1)install\$&.mix + copy $(.path.cd1)aud1\$&.mix $(.path.cd2)aud1\$&.mix + +#$(.path.mix)8_st\TRANSIT.MIX: $(TSCOREFILES:.AUD=.A8) +# UTILS\MIXFILE -E.A8=.AUD -I$(.path.aud) &&! +# $** +#! $(.path.mix)8_st\$&.mix + +#$(.path.mix)16_st\TRANSIT.MIX: $(TSCOREFILES:.AUD=.A6) +# UTILS\MIXFILE -E.A6=.AUD -I$(.path.aud) &&! +# $** +#! $(.path.mix)16_st\$&.mix + +$(.path.cd1)MOVIES.MIX: $(GDIMOVIES) + UTILS\MIXFILE -I$(.path.vqa) &&! + $** +! $(.path.cd1)$&.mix + +$(.path.cd2)MOVIES.MIX: $(NODMOVIES) + UTILS\MIXFILE -I$(.path.vqa) &&! + $** +! $(.path.cd2)$&.mix + + +$(.path.dmo)DEMO.MIX: $(DEMOFILES:.AUD=.A6) + UTILS\MIXFILE -I$(.path.cps) &&! + $** +! $(.path.dmo)$&.mix + +$(.path.dmo)DEMOL.MIX: $(DEMOLFILES:.AUD=.A6) + UTILS\MIXFILE -E.A6=.AUD -I$(.path.cps) &&! + $** +! $(.path.dmo)$&.mix + +$(.path.dmo)DEMOM.MIX: $(DEMOMFILES) + UTILS\MIXFILE -E.A6=.AUD -I$(.path.cps) &&! + $** +! $(.path.dmo)$&.mix + + +$(.path.dm2)install\DEMO.MIX: $(DEMO2FILES:.AUD=.A6) + UTILS\MIXFILE -I$(.path.cps) &&! + $** +! $(.path.dm2)install\$&.mix + +$(.path.dm2)install\DEMOL.MIX: $(DEMO2LFILES:.AUD=.A6) + UTILS\MIXFILE -E.A6=.AUD -I$(.path.cps) &&! + $** +! $(.path.dm2)install\$&.mix + +$(.path.dm2)install\DEMOC.MIX: $(DEMOCFILES:.AUD=.A6) + UTILS\MIXFILE -E.A6=.AUD -I$(.path.cps) &&! + $** +! $(.path.dm2)install\$&.mix + +$(.path.dm2)DEMOM.MIX: $(DEMO2MFILES) + UTILS\MIXFILE -E.A6=.AUD -I$(.path.cps) &&! + $** +! $(.path.dm2)$&.mix + +###################################################################### +# Special rule to create the mouse shape (which must be a shape file) +mouse.shp: $(.path.anm)mouse.anm + utils\makeshps $(.path.lbm)title.lbm &&! + &$(.path.anm)mouse.anm; + end; +! $(.path.shp)$&.shp $(SHAPEBUFFSIZE) diff --git a/BUILDING.CPP b/BUILDING.CPP new file mode 100644 index 0000000..a6f5108 --- /dev/null +++ b/BUILDING.CPP @@ -0,0 +1,4960 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\building.cpv 2.13 02 Aug 1995 17:00:14 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BUILDING.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : August 20, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BuildingClass::AI -- Handles non-graphic AI processing for buildings. * + * BuildingClass::Active_Click_With -- Handles cell selection for buildings. * + * BuildingClass::As_Target -- Convert the building into a target value. * + * BuildingClass::Assign_Target -- Assigns a target to the building. * + * BuildingClass::Begin_Mode -- Begins an animation mode for the building. * + * BuildingClass::BuildingClass -- Constructor for buildings. * + * BuildingClass::Can_Demolish -- Can the player demolish (sell back) the building? * + * BuildingClass::Can_Enter_Cell -- Determines if building can be placed down. * + * BuildingClass::Can_Fire -- Determines if this building can fire. * + * BuildingClass::Captured -- Captures the building. * + * BuildingClass::Center_Coord -- Fetches the center coordinate for the building. * + * BuildingClass::Click_With -- Handles clicking on the map while the building is selected. * + * BuildingClass::Crew_Type -- This determines the crew that this object generates. * + * BuildingClass::Death_Announcement -- Announce the death of this building. * + * BuildingClass::Debug_Dump -- Displays building status to the monochrome screen. * + * BuildingClass::Detach -- Handles target removal from the game system. * + * BuildingClass::Detach_All -- Possibly abandons production according to factory type. * + * BuildingClass::Draw_It -- Displays the building at the location specified. * + * BuildingClass::Drop_Debris -- Drops rubble when building is destroyed. * + * BuildingClass::Enter_Idle_Mode -- The building will enter its idle mode. * + * BuildingClass::Exit_Object -- Initiates an object to leave the building. * + * BuildingClass::Fire_At -- Fires weapon at specified target. * + * BuildingClass::Fire_Coord -- Calculates the coordinate that projectile would appear. * + * BuildingClass::Fire_Direction -- Fetches the direction of firing. * + * BuildingClass::Fire_Out -- Handles when attached animation expires. * + * BuildingClass::Flush_For_Placement -- Handles clearing a zone for object placement. * + * BuildingClass::Grand_Opening -- Handles construction completed special operations. * + * BuildingClass::Greatest_Threat -- Searches for target that building can fire upon. * + * BuildingClass::Init -- Initialize the building system to an empty null state. * + * BuildingClass::Limbo -- Handles power adjustment as building goes into limbo. * + * BuildingClass::Look -- Reveal map around building. * + * BuildingClass::Mark -- Building interface to map rendering system. * + * BuildingClass::Mission_Attack -- Handles attack mission for building. * + * BuildingClass::Mission_Construction -- Handles mission construction. * + * BuildingClass::Mission_Deconstruction -- Handles building deconstruction. * + * BuildingClass::Mission_Guard -- Handles guard mission for combat buildings. * + * BuildingClass::Mission_Harvest -- Handles refinery unloading harvesters. * + * BuildingClass::Mission_Missile -- State machine for nuclear missile launch. * + * BuildingClass::Mission_Repair -- Handles the repair (active) state for building. * + * BuildingClass::Mission_Unload -- Handles the unload mission for a building. * + * BuildingClass::Pip_Count -- Determines "full" pips to display for building. * + * BuildingClass::Power_Output -- Fetches the current power output from this building. * + * BuildingClass::Read_INI -- Reads buildings from INI file. * + * BuildingClass::Receive_Message -- Handle an incoming message to the building. * + * BuildingClass::Refund_Amount -- Fetches the refund amount if building is sold. * + * BuildingClass::Remap_Table -- Fetches the remap table to use for this building. * + * BuildingClass::Repair -- Initiates or terminates the repair process. * + * BuildingClass::Revealed -- Reveals the building to the specified house. * + * BuildingClass::Sell_Back -- Controls the sell back (demolish) operation. * + * BuildingClass::Sort_Y -- Returns the building coordinate used for sorting. * + * BuildingClass::Take_Damage -- Inflicts damage points upon a building. * + * BuildingClass::Toggle_Primary -- Toggles the primary factory state. * + * BuildingClass::Unlimbo -- Removes a building from limbo state. * + * BuildingClass::Update_Buildables -- Informs sidebar of additional construction options. * + * BuildingClass::Update_Specials -- removes computer specials for lost bld * + * BuildingClass::What_Action -- Determines action to perform if click on specified object. * + * BuildingClass::What_Action -- Determines what action will occur. * + * BuildingClass::Write_INI -- Writes all building data to an INI file. * + * BuildingClass::delete -- Deallocates building object. * + * BuildingClass::new -- Allocates a building object from building pool. * + * BuildingClass::~BuildingClass -- Destructor for building type objects. * + * BuildingClass::Validate -- validates building pointer * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +enum SAMState { + SAM_NONE=-1, // Used for non SAM site buildings. + SAM_UNDERGROUND, // Launcher is underground and awaiting orders. + SAM_RISING, // Doors open and launcher rises to normal locked down position. + SAM_READY, // Launcher can be facing any direction tracking targets. + SAM_FIRING, // Stationary while missile is being fired. + SAM_READY2, // Launcher can be facing any direction tracking targets. + SAM_FIRING2, // Stationary while missile is being fired. + SAM_LOCKING, // Rotating to locked position in preparation for lowering. + SAM_LOWERING, // Launcher is lowering into the ground. +}; + + +/*************************************************************************** +** Center of building offset table. +*/ +COORDINATE const BuildingClass::CenterOffset[BSIZE_COUNT] = { + 0x00800080L, + 0x008000FFL, + 0x00FF0080L, + 0x00FF00FFL, + 0x018000FFL, + 0x00FF0180L, + 0x01800180L, + + 0x00FF0200L, + + 0x02800280L, +}; + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * BuildingClass::VTable; + + +/*********************************************************************************************** + * BuildingClass::Validate -- validates building pointer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int BuildingClass::Validate(void) const +{ + int num; + + num = Buildings.ID(this); + if (num < 0 || num >= BUILDING_MAX) { + Validate_Error("BUILDING"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * BuildingClass::Receive_Message -- Handle an incoming message to the building. * + * * + * This routine handles an incoming message to the building. Messages regulate the * + * various cooperative ventures between buildings and units. This might include such * + * actions as coordinating the construction yard animation with the actual building's * + * construction animation. * + * * + * INPUT: from -- The originator of the message received. * + * * + * message -- The radio message received. * + * * + * param -- Reference to an optional parameter that might be used to return * + * extra information to the message originator. * + * * + * OUTPUT: Returns with the response to the message (typically, this is just RADIO_OK). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/09/1994 JLB : Created. * + * 06/26/1995 JLB : Forces refinery load anim to start immediately. * + * 08/13/1995 JLB : Uses ScenarioInit for special loose "CAN_LOAD" check. * + *=============================================================================================*/ +RadioMessageType BuildingClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + Validate(); + switch (message) { + + /* + ** This message is received as a request to attach/load/dock with this building. + ** Verify that this is allowed and return the appropriate response. + */ + case RADIO_CAN_LOAD: + TechnoClass::Receive_Message(from, message, param); + if (BState == BSTATE_CONSTRUCTION || (!ScenarioInit && In_Radio_Contact())) return(RADIO_NEGATIVE); + switch (Class->Type) { + case STRUCT_AIRSTRIP: + if (from->What_Am_I() == RTTI_AIRCRAFT && *((AircraftClass const *)from) == AIRCRAFT_CARGO) { + return(RADIO_ROGER); + } + break; + + case STRUCT_HELIPAD: + if (from->What_Am_I() == RTTI_AIRCRAFT && !((AircraftClass const *)from)->Class->IsFixedWing) { + return(RADIO_ROGER); + } + break; + + case STRUCT_REPAIR: + if (/*from->Health_Ratio() < 0x0100 &&*/ from->What_Am_I() == RTTI_UNIT || from->What_Am_I() == RTTI_AIRCRAFT) { + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + + case STRUCT_REFINERY: + if (from->What_Am_I() == RTTI_UNIT && + *((UnitClass *)from) == UNIT_HARVESTER && + (ScenarioInit || !Is_Something_Attached())) { + + return(RADIO_ROGER); + } + break; + + default: + break; + } + return(RADIO_NEGATIVE); + + /* + ** This message is received when the object has attached itself to this + ** building. + */ + case RADIO_IM_IN: + if (Mission == MISSION_DECONSTRUCTION) { + return(RADIO_NEGATIVE); + } + switch (Class->Type) { + case STRUCT_REPAIR: + IsReadyToCommence = true; + Assign_Mission(MISSION_REPAIR); + from->Assign_Mission(MISSION_SLEEP); + return(RADIO_ROGER); + + case STRUCT_HELIPAD: + Assign_Mission(MISSION_REPAIR); + from->Assign_Mission(MISSION_SLEEP); + return(RADIO_ROGER); + + case STRUCT_REFINERY: + ScenarioInit++; + Begin_Mode(BSTATE_ACTIVE); + ScenarioInit--; + Mark(MARK_CHANGE); + Assign_Mission(MISSION_HARVEST); + return(RADIO_ATTACH); + } + break; + + /* + ** Docking maneuver maintenance message. See if new order should be given to the + ** unit trying to dock. + */ + case RADIO_DOCKING: + TechnoClass::Receive_Message(from, message, param); + + /* + ** When in radio contact for loading, the refinery starts + ** flashing the lights. + */ + if (*this == STRUCT_REFINERY && BState != BSTATE_FULL) { + Begin_Mode(BSTATE_FULL); + } + + if (Transmit_Message(RADIO_NEED_TO_MOVE, from) == RADIO_ROGER) { + if (*this == STRUCT_HELIPAD) { + param = As_Target(); + } else { + if (*this == STRUCT_REPAIR) { + Transmit_Message(RADIO_TETHER); + param = ::As_Target(Coord_Cell(Center_Coord())); + } else { + param = ::As_Target(Coord_Cell(Adjacent_Cell(Center_Coord(), DIR_SW))); + } + } + + /* + ** Tell the harvester to move to the docking pad of the refinery. + */ + if (Transmit_Message(RADIO_MOVE_HERE, param, from) == RADIO_YEA_NOW_WHAT) { + + /* + ** Since the harvester is already there, tell it to begin the backup + ** procedure now. If it can't, then tell it to get outta here. + */ + Transmit_Message(RADIO_TETHER); + if (*this == STRUCT_REFINERY && Transmit_Message(RADIO_BACKUP_NOW, from) != RADIO_ROGER) { + from->Scatter(NULL, true); + } + } + } + return(RADIO_ROGER); + + /* + ** If a transport or harvester is requesting permission to head toward, dock + ** and load/unload, check to make sure that this is allowed given the current + ** state of the building. + */ + case RADIO_ARE_REFINERY: + if (Is_Something_Attached() || In_Radio_Contact() || IsInLimbo || House->Class->House != from->Owner() || (*this != STRUCT_REFINERY/* && *this != STRUCT_REPAIR*/)) { + return(RADIO_NEGATIVE); + } + return(RADIO_ROGER); + + /* + ** Someone is telling us that it is starting construction. This should only + ** occur if this is a construction yard and a building was just placed on + ** the map. + */ + case RADIO_BUILDING: + Assign_Mission(MISSION_REPAIR); + TechnoClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + /* + ** Someone is telling us that they have finished construction. This should + ** only occur if this is a construction yard and the building that was being + ** constructed has finished. In this case, stop the construction yard + ** animation. + */ + case RADIO_COMPLETE: + if (Mission != MISSION_DECONSTRUCTION) { + Assign_Mission(MISSION_GUARD); + } + TechnoClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + /* + ** This message may occur unexpectedly if the unit in contact with this + ** building is suddenly destroyed. Handle any cleanup necessary. For example, + ** a construction yard should stop its construction animation in this case. + */ + case RADIO_OVER_OUT: + Begin_Mode(BSTATE_IDLE); + TechnoClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + /* + ** This message is received when an object has completely left + ** building. Sometimes special cleanup action is required when + ** this event occurs. + */ + case RADIO_UNLOADED: + if (*this == STRUCT_REPAIR) { + if (Distance(from) < 0x0180) { + return(RADIO_ROGER); + } + } + TechnoClass::Receive_Message(from, message, param); + if (*this == STRUCT_WEAP || *this == STRUCT_AIRSTRIP || *this == STRUCT_REPAIR) return(RADIO_RUN_AWAY); + return(RADIO_ROGER); + } + + /* + ** Pass along the message to the default message handler in the radio itself. + */ + return(TechnoClass::Receive_Message(from, message, param)); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * BuildingClass::Debug_Dump -- Displays building status to the monochrome screen. * + * * + * This utility function will output the current status of the building class to the * + * monochrome screen. It is through this data that bugs may be fixed or detected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Debug_Dump(MonoClass *mono) const +{ + Validate(); + mono->Set_Cursor(0, 0); + mono->Print( + "ÚName:ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂMission:ÄÄÄÂTarCom:ÂÄÄÄÄÄÄÄÂRadio:ÂCoord:ÄÄÂÄÄÄÄÄÄÄÄÂSt:Ä¿\n" + "³ ³ ³ ³ ³ ³ ³ ³ ³\n" + "ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂNÂYÂHealth:ÄÂÄÄÄÁÄÂTurret:ÂÄÄÄÄÄÁÂÄBuilding:ÄÄÂCargo:ÄÄÄÄÁÄÄÄÄ´\n" + "³Active........³ ³ ³ ³ ³ ³ ³ ³ ³\n" + "³Limbo.........³ ³ ÃÄÄÄÄÄÄÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n" + "³Owned.........³ ³ ³Last Message: ³\n" + "³Discovered....³ ³ ÃTimer:ÂArm:ÂÄÄÄÄÄÄÂTiberium:ÂFlash:ÂStage:ÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ\n" + "³Selected......³ ³ ³ ³ ³ ³ ³ ³ ³ \n" + "³Teathered.....³ ³ ÃÄÄÄÄÄÄÁÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÙ \n" + "³Locked on Map.³ ³ ³ \n" + "³Is A Loaner...³ ³ ³ \n" + "³ ³ ³ ³ \n" + "³ ³ ³ ³ \n" + "³ ³ ³ ³ \n" + "³Repairing.....³ ³ ³ \n" + "³ ³ ³ ³ \n" + "³ ³ ³ ³ \n" + "³Recoiling.....³ ³ ³ \n" + "³To Display....³ ³ ³ \n" + "ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÁÄÙ \n"); + mono->Set_Cursor(1, 1);mono->Printf("%s:%s", House->Class->IniName, Class->IniName); + mono->Set_Cursor(35, 3);mono->Printf("%02X:%02X", PrimaryFacing.Current(), PrimaryFacing.Desired()); + mono->Set_Cursor(50, 3); + if (Factory) { + mono->Printf(Factory->Get_Object()->Class_Of().IniName); + mono->Printf(" "); + mono->Printf("%d%%", Factory->Completion()); + } else { + mono->Printf("(empty)"); + } + + mono->Text_Print("X", 16 + (IsRepairing?2:0), 14); +// mono->Set_Cursor(44, 3);mono->Printf("%d", SAM); + mono->Set_Cursor(34, 1);mono->Printf("%04X", TarCom); + mono->Set_Cursor(28, 7);mono->Printf("%2d", Arm); + + TechnoClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * BuildingClass::Draw_It -- Displays the building at the location specified. * + * * + * This is the low level graphic routine that displays the building at the location * + * specified. * + * * + * INPUT: x,y -- The coordinate to draw the building at. * + * * + * window -- The clipping window to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + * 06/27/1994 JLB : Takes a clipping window parameter. * + * 07/06/1995 JLB : Handles damaged silos correctly. * + *=============================================================================================*/ +void BuildingClass::Draw_It(int x, int y, WindowNumberType window) +{ + Validate(); + void const * shapefile; // Pointer to loaded shape file. + int shapenum; + + shapenum = Fetch_Stage(); + + /* + ** The shape file to use for rendering depends on whether the building + ** is undergoing construction or not. + */ + if (BState == BSTATE_CONSTRUCTION) { + shapefile = Class->Get_Buildup_Data(); + + /* + ** If the building is deconstructing, then the display frame progresses + ** from the end to the beginning. Reverse the shape number accordingly. + */ + if (Mission == MISSION_DECONSTRUCTION) { + shapenum = (Class->Anims[BState].Start+Class->Anims[BState].Count-1)-shapenum; + } + + } else { + + shapefile = Class->Get_Image_Data(); + + /* + ** The obelisk has a stage value than can be overridden by + ** its current state. + */ + if (*this == STRUCT_OBELISK) { + if (IsCharged) { + shapenum = 3; + } else { + if (IsCharging) { + shapenum = Fetch_Stage(); + } else { + shapenum = 0; + } + } + } + + /* + ** Buildings that contain a turret handle their shape determination + ** differently than normal buildings. They need to take into consideration + ** the direction the turret is facing. + */ + if (Class->IsTurretEquipped) { + shapenum = UnitClass::BodyShape[Facing_To_32(PrimaryFacing.Current())]; + + if (*this == STRUCT_SAM) { + + /* + ** SAM sites that are free to rotate fetch their animation frame + ** from the building's turret facing. All other animation stages + ** fetch their frame from the embedded animation sequencer. + */ + if (Status == SAM_READY || Status == SAM_FIRING || Status == SAM_LOCKING) { + shapenum += 16; + } else { + shapenum = Fetch_Stage(); + } + } else { + if (IsInRecoilState) { + shapenum += 32; + } + } + if (Health_Ratio() < 0x0080) { + shapenum += 64; + } + } else { + + /* + ** If it has only one point of strength left, it is shown in the + ** worst state possible. + */ + if (Strength <= 1) { + shapenum = Get_Build_Frame_Count(shapefile)-1; + } else { + + if (*this == STRUCT_WEAP) { + shapenum = 0; + if (Health_Ratio() < 0x0080) { + shapenum = 1; + } + + } else { + + /* + ** Special render stage for silos. The stage is dependant on the current + ** Tiberium collected as it relates to Tiberium capacity. + */ + if (*this == STRUCT_STORAGE) { + int level = 0; + if (House->Capacity) { + level = (House->Tiberium * 5) / House->Capacity; + } +// int level = Fixed_To_Cardinal(4, Cardinal_To_Fixed(House->Capacity, House->Tiberium)); + + shapenum += Bound(level, 0, 4); + if (Health_Ratio() < 0x0080) { + shapenum += 5; + } + + } else { + + if (Health_Ratio() < 0x0080) { + + /* + ** Special damage stage for pump. + */ + if (!Class->IsSimpleDamage) { + int last1 = Class->Anims[BSTATE_IDLE].Start + Class->Anims[BSTATE_IDLE].Count; + int last2 = Class->Anims[BSTATE_ACTIVE].Start + Class->Anims[BSTATE_ACTIVE].Count; + int largest = MAX(last1, last2); + last2 = Class->Anims[BSTATE_AUX1].Start + Class->Anims[BSTATE_AUX1].Count; + largest = MAX(largest, last2); + last2 = Class->Anims[BSTATE_AUX2].Start + Class->Anims[BSTATE_AUX2].Count; + largest = MAX(largest, last2); + + shapenum += largest; + } else { + + /* + ** Presume that the damage stage is the end frame. + */ + shapenum = Get_Build_Frame_Count(shapefile) - 2; + } + } + } + } + } + } + } + + /* + ** Actually draw the building shape. + */ + IsTheaterShape = Class->IsTheater; + Techno_Draw_Object(shapefile, shapenum, x, y, window); + IsTheaterShape = false; + + /* + ** Patch for adding overlay onto weapon factory. Only add the overlay if + ** the building has more than 1 hp. Also, if the building's in radio + ** contact, he must be unloading a constructed vehicle, so draw that + ** vehicle before drawing the overlay. + */ + if (BState != BSTATE_CONSTRUCTION) { + + /* + ** A Tethered object is always rendered AFTER the building. + */ + if (IsTethered && In_Radio_Contact() && !Contact_With_Whom()->IsInLimbo) { + Contact_With_Whom()->Render(true); + } + + /* + ** Draw the weapon factory custom overlay graphic. + */ + if (*this == STRUCT_WEAP && Strength > 1) { + shapenum = Door_Stage(); + if (Health_Ratio() < 0x0080) shapenum += 10; + Techno_Draw_Object(WarFactoryOverlay, shapenum, x, y, window); + } + + /* + ** Draw any repair feedback graphic required. + */ + if (IsRepairing && IsWrenchVisible) { + CC_Draw_Shape(ObjectTypeClass::SelectShapes, SELECT_WRENCH, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + } + + TechnoClass::Draw_It(x, y, window); +} + + +/*********************************************************************************************** + * BuildingClass::Mark -- Building interface to map rendering system. * + * * + * This routine is used to mark the map cells so that when it renders * + * the underlying icons will also be updated as necessary. * + * * + * INPUT: mark -- Type of image change (MARK_UP, _DOWN, _CHANGE) * + * MARK_UP -- Building is removed. * + * MARK_CHANGE -- Building changes shape. * + * MARK_DOWN -- Building is added. * + * * + * OUTPUT: bool; Did the mark operation succeed? Failure could be the result of marking down * + * when the building is already marked down, or visa versa. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/31/1994 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + * 04/16/1994 JLB : Added health bar tracking. * + * 12/23/1994 JLB : Calls low level check before proceeding. * + * 01/27/1995 JLB : Special road spacer template added. * + *=============================================================================================*/ +bool BuildingClass::Mark(MarkType mark) +{ + Validate(); + if (TechnoClass::Mark(mark)) { + short const *offset = Overlap_List(); + short const *occupy = Occupy_List(); + CELL cell = Coord_Cell(Coord); + SmudgeType bib; + + switch (mark) { + case MARK_UP: + Map.Pick_Up(cell, this); + if (Class->Bib_And_Offset(bib, cell)) { + SmudgeClass * smudge = new SmudgeClass(bib); + if (smudge) { + smudge->Disown(cell); + delete smudge; + } + } + break; + + case MARK_DOWN: + + /* + ** Special wall logic is handled here. A building that is really a wall + ** gets converted into an overlay wall type when it is placed down. The + ** actual building object itself is destroyed. + */ + if (Class->IsWall) { + switch (Class->Type) { + case STRUCT_BRICK_WALL: + new OverlayClass(OVERLAY_BRICK_WALL, cell, House->Class->House); + break; + + case STRUCT_BARBWIRE_WALL: + new OverlayClass(OVERLAY_BARBWIRE_WALL, cell, House->Class->House); + break; + + case STRUCT_SANDBAG_WALL: + new OverlayClass(OVERLAY_SANDBAG_WALL, cell, House->Class->House); + break; + + case STRUCT_WOOD_WALL: + new OverlayClass(OVERLAY_WOOD_WALL, cell, House->Class->House); + break; + + case STRUCT_CYCLONE_WALL: + new OverlayClass(OVERLAY_CYCLONE_WALL, cell, House->Class->House); + break; + } + Transmit_Message(RADIO_OVER_OUT); + delete this; + + } else { + + if (Can_Enter_Cell(cell) == MOVE_OK) { + + /* + ** Determine if a bib is required for this building. If one is, then + ** create and place it. + */ + CELL newcell = cell; + if (Class->Bib_And_Offset(bib, newcell)) { + new SmudgeClass(bib, Cell_Coord(newcell), House->Class->House); + } + + Map.Place_Down(cell, this); + } else { + return(false); + } + } + break; + + default: + Map.Refresh_Cells(cell, offset); + Map.Refresh_Cells(cell, occupy); + break; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingClass::Fire_At -- Fires weapon at specified target. * + * * + * This routine does the actual firing of a projectile from the * + * building toward the specified target. Prior to calling this * + * routine, the building must have rotated into position and acquired * + * a suitable target. * + * * + * INPUT: target -- The target to fire upon. * + * * + * which -- Which weapon to use for firing. 0=primary, 1=secondary. * + * * + * OUTPUT: Returns with a pointer to the projectile just launched. This * + * may come in handy if additional adjustments to the projectile * + * are required. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/16/1994 JLB : Created. * + *=============================================================================================*/ +BulletClass * BuildingClass::Fire_At(TARGET target, int which) +{ + Validate(); + BulletClass * bullet; // Projectile. + WeaponTypeClass const * weapon = (which == 0) ? &Weapons[Class->Primary] : &Weapons[Class->Secondary]; + + bullet = TechnoClass::Fire_At(target, which); + if (bullet) { + if (*this == STRUCT_SAM) { + AnimClass *anim = new AnimClass((AnimType)(ANIM_SAM_N + Dir_Facing(PrimaryFacing.Current())), Center_Coord()); + if (anim) { + anim->Attach_To(this); + } + + } else { + + /* + ** Flash the muzzle, play sound, and perform any firing animation. + */ + Sound_Effect(weapon->Sound, Coord); + + if (weapon->Fires == BULLET_BULLET) { + new AnimClass((AnimType)(ANIM_GUN_N + Dir_Facing(PrimaryFacing.Current())), Fire_Coord(which)); + } else { + if (weapon->Fires == BULLET_LASER) { + int x,y,x1,y1; + COORDINATE source, dest; + source = Fire_Coord(which); + dest = As_Coord(target); + IsCharging = false; + IsCharged = false; + Set_Stage(0); + Set_Rate(0); + + if (Map.Push_Onto_TacMap(source, dest) && SpecialDialog == SDLG_NONE) { + + Map.Coord_To_Pixel(source, x, y); + Map.Coord_To_Pixel(dest, x1, y1); + x += Map.TacPixelX; + x1 += Map.TacPixelX; + y += Map.TacPixelY; + y1 += Map.TacPixelY; + Set_Logic_Page(SeenBuff); + LogicPage->Draw_Line(x+1, y, x1, y1, 0x7D); + LogicPage->Draw_Line(x-1, y, x1, y1, 0x7D); + LogicPage->Draw_Line(x, y, x1, y1, 0x7F); + Delay(1); // Make sure line is visible briefly + Map.Flag_To_Redraw(true); + } + new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), As_Coord(target) ); + } else { + new AnimClass(ANIM_MUZZLE_FLASH, Fire_Coord(which)); + } + } + Mark(MARK_CHANGE); + } + } + + return(bullet); +} + + +/*********************************************************************************************** + * BuildingClass::AI -- Handles non-graphic AI processing for buildings. * + * * + * This function is to handle the AI logic for the building. The graphic logic (facing, * + * firing, and animation) is handled elsewhere. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 12/26/1994 JLB : Handles production. * + * 06/11/1995 JLB : Revamped. * + *=============================================================================================*/ +void BuildingClass::AI(void) +{ + Validate(); + + /* + ** Process building animation state changes. Transition to a following state + ** if there is one specified and the current animation sequence has expired. + ** This process must occur before mission AI since the mission AI relies on + ** the bstate change to occur immediately before the MissionClass::AI. + */ + bool stagechange = Graphic_Logic(); + bool toloop = false; + + /* + ** Always refresh the SAM site if it has an animation change. + */ + if (*this == STRUCT_SAM && stagechange) Mark(MARK_CHANGE); + + if ((!Class->IsTurretEquipped && *this != STRUCT_OBELISK) || Mission == MISSION_CONSTRUCTION || Mission == MISSION_DECONSTRUCTION) { + if (stagechange) { + + /* + ** Check for animation end or if special case of MCV deconstructing when it is allowed + ** to convert back into an MCV. + */ + BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control(); + + /* + ** When the last frame of the current animation sequence is reached, flag that + ** a new mission may be started. This must occur before the animation actually + ** loops so that if a mission change does occur, it will have a chance to change + ** the building graphic before the last frame is replaced by the first frame of + ** the loop. + */ + if (Fetch_Stage() == ctrl->Start+ctrl->Count-1 || (Special.IsMCVDeploy && *this == STRUCT_CONST && Mission == MISSION_DECONSTRUCTION && Fetch_Stage() == (42-19))) { + IsReadyToCommence = true; + } + + /* + ** If the animation advances beyond the last frame, then start the animation + ** sequence over from the beginning. + */ + if (Fetch_Stage() >= ctrl->Start+ctrl->Count) { + toloop = true; + } + Mark(MARK_CHANGE); + } else { + if (BState == BSTATE_NONE || Fetch_Rate() == 0) { + IsReadyToCommence = true; + } + } + } + + /* + ** If there is a door that is animating, then it might cause this building + ** to be redrawn. Check for and flag to redraw as necessary. + */ + if (Time_To_Redraw()) { + Clear_Redraw_Flag(); + Mark(MARK_CHANGE); + } + + /* + ** The animation sequence has looped. Restart it and flag this loop condition. + ** This is used to tell the mission system that the animation has completed. It + ** also signals that now is a good time to act on any pending mission. + */ + if (toloop) { + BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control(); + if (BState == BSTATE_CONSTRUCTION || BState == BSTATE_IDLE) { + Set_Rate(Options.Normalize_Delay(ctrl->Rate)); + } else { + Set_Rate(ctrl->Rate); + } + Set_Stage(ctrl->Start); + Mark(MARK_CHANGE); + } + + /* + ** If now is a good time to act on a new mission, then do so. This process occurs + ** here because some outside event may have requested a mission change for the building. + ** Such outside requests (player input) must be initiated BEFORE the normal AI process. + */ + if (IsReadyToCommence && BState != BSTATE_CONSTRUCTION) { + + /* + ** Clear the commencement flag ONLY if something actually occured. By acting + ** this way, a building can set the IsReadyToCommence flag before it goes + ** to "sleep" knowing that it will wake up as soon as a new mission comes + ** along. + */ + if (Commence()) { + IsReadyToCommence = false; + } + } + + /* + ** Proceed with normal logic processing. This is where the mission processing + ** occurs. This call must be located after the animation sequence makes the + ** transition to the next frame (see above) in order for the mission logic to + ** act at the exact moment of graphic transition BEFORE it has a chance to + ** be displayed. + */ + TechnoClass::AI(); + + /* + ** If now is a good time to act on a new mission, then do so. This occurs here because + ** some AI event may have requested a mission change (usually from another mission + ** state machine). This must occur here before it has a chance to render. + */ + if (IsReadyToCommence) { + + /* + ** Clear the commencement flag ONLY if something actually occured. By acting + ** this way, a building can set the IsReadyToCommence flag before it goes + ** to "sleep" knowing that it will wake up as soon as a new mission comes + ** along. + */ + if (Commence()) { + IsReadyToCommence = false; + } + } + + /* + ** If a change of animation was requested, then make the change + ** now. The building animation system acts independantly but subordinate + ** to the mission state machine system. By performing the animation change-up + ** here, the mission AI system is ensured of immediate visual affect when it + ** decides to change the animation state of the building. + */ + if (QueueBState != BSTATE_NONE) { + if (BState != QueueBState) { + BState = QueueBState; + BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control(); + if (BState == BSTATE_CONSTRUCTION || BState == BSTATE_IDLE) { + Set_Rate(Options.Normalize_Delay(ctrl->Rate)); + } else { + Set_Rate(ctrl->Rate); + } + Set_Stage(ctrl->Start); + } + QueueBState = BSTATE_NONE; + } + + /* + ** If the building's strength has changed, then update the power + ** accordingly. + */ + if (Strength != LastStrength) { + int oldpower = Power_Output(); + LastStrength = Strength; + int newpower = Power_Output(); + House->Adjust_Power(newpower - oldpower); + } + + /* + ** Check to see if the destruction countdown timer is active. If so, then decrement it. + ** When this timer reaches zero, the building is removed from the map. All the explosions + ** are presumed to be in progress at this time. + */ + if (!Strength) { + if (CountDown.Expired()) { + Limbo(); + Drop_Debris(WhomToRepay); + delete this; + } + return; + } + + /* + ** Obelisk charging logic. + */ + if (*this == STRUCT_OBELISK && BState != BSTATE_CONSTRUCTION) { + if (Target_Legal(TarCom) && House->Power_Fraction() >= 0x0100) { + if (!IsCharged) { + if (IsCharging) { + if (stagechange) { + Mark(MARK_CHANGE); + if (Fetch_Stage() >= 4) { + IsCharged = true; + IsCharging = false; + Set_Rate(0); + } + } + } else { + IsCharged = false; + IsCharging = true; + Set_Stage(0); + Set_Rate(OBELISK_ANIMATION_RATE); + Sound_Effect(VOC_LASER_POWER, Coord); + } + } + } else { + if (IsCharging || IsCharged) { + Mark(MARK_CHANGE); + IsCharging = false; + IsCharged = false; + Set_Stage(0); + Set_Rate(0); + } + } + } + + /* + ** Handle any repair process that may be going on. + */ + if (IsRepairing) { + if ((Frame % 15) == 0) { + IsWrenchVisible = (IsWrenchVisible == false); + Mark(MARK_CHANGE); + int cost = Class->Repair_Cost(); + int step = Class->Repair_Step(); + + /* + ** Check for and expend any necessary monies to continue the repair. + */ + if (House->Available_Money() >= cost) { + House->Spend_Money(cost); + Strength += step; + + if (Strength >= Class->MaxStrength) { + Strength = Class->MaxStrength; + IsRepairing = false; + } + } else { + IsRepairing = false; + } + } + } + + /* + ** Handle any production tied to this building. Only computer controlled buildings have + ** production attached to the building itself. The player uses the sidebar interface for + ** all production control. + */ + if (Factory && Factory->Has_Completed() && PlacementDelay.Expired()) { + + switch (Exit_Object(Factory->Get_Object())) { + + /* + ** If the object could not leave the factory, then either request + ** a transport, place the (what must be a) building using another method, or + ** abort the production and refund money. + */ + case 0: + Factory->Abandon(); + delete Factory; + Factory = 0; + break; + + case 1: + PlacementDelay = TICKS_PER_SECOND*3; + break; + + case 2: + Factory->Completed(); + delete Factory; + Factory = 0; + break; + + } + } + + /* + ** For computer controlled buildings, determine what should be produced and start + ** production accordingly. + */ + if (!House->IsHuman && Mission != MISSION_CONSTRUCTION && Mission != MISSION_DECONSTRUCTION) { + + /* + ** Possibly start repair process if the building is below half strength. + */ + int ratio = 0x0040; + if (Scenario > 6) ratio = 0x0080; + if (Scenario > 10) ratio = 0x00C0; + if (Class->IsRepairable && Health_Ratio() <= ratio) { + if (House->Available_Money() >= REPAIR_THRESHHOLD) { + Repair(1); + } else { + if (IsTickedOff && Scenario > 2 && Random_Pick(0, 50) < Scenario && !Trigger) { + Sell_Back(1); + } + } + } + + /* + ** Buildings that produce other objects have special factory logic handled here. + */ + if (Class->ToBuild != RTTI_NONE) { + + if (Factory) { + + /* + ** If production has halted, then just abort production and make the + ** funds available for something else. + */ + if (PlacementDelay.Expired() && !Factory->Is_Building()) { + Factory->Abandon(); + delete Factory; + Factory = 0; + } + + } else { + + /* + ** Only look to start production if there is at least a small amount of + ** money available. In cases where there is no practical money left, then + ** production can never complete -- don't bother starting it. + */ + if (House->IsStarted && House->Available_Money() > 10) { + TechnoTypeClass const * techno = House->Suggest_New_Object(Class->ToBuild); + + /* + ** If a suitable object type was selected for production, then start + ** producing it now. + */ + if (techno) { + Factory = new FactoryClass; + if (Factory) { + if (!Factory->Set(*techno, *House)) { + delete Factory; + Factory = 0; + } else { + Factory->Start(); + } + } + } + } + } + } + } + + /* + ** Check for demolition timeout. When timeout has expired, the building explodes. + */ + if (IsGoingToBlow && CountDown.Expired()) { + SabotagedType = Class->Type; + int damage = 5000; + Take_Damage(damage, 0, WARHEAD_FIRE, As_Techno(WhomToRepay)); + Mark(MARK_CHANGE); + } + + /* + ** If the building was in a recoil state (as it would be just as it fires), then + ** restore the building. + */ + if (IsInRecoilState) { + IsInRecoilState = false; + Mark(MARK_CHANGE); + } + + /* + ** Turret equiped buildings must handle turret rotation logic here. This entails + ** rotating the turret to the desired facing as well as figuring out what that + ** desired facing should be. + */ + if (Class->IsTurretEquipped && Mission != MISSION_CONSTRUCTION && Mission != MISSION_DECONSTRUCTION) { + + /* + ** Rotate turret to match desired facing. + */ + if (PrimaryFacing.Is_Rotating()) { + if (*this == STRUCT_SAM) { + if (PrimaryFacing.Rotation_Adjust(15)) { + Mark(MARK_CHANGE); + } + } else { + if (PrimaryFacing.Rotation_Adjust(12)) { + Mark(MARK_CHANGE); + } + } + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Unlimbo -- Removes a building from limbo state. * + * * + * Use this routine to transform a building that has been held in limbo * + * state, into one that really exists on the map. Once a building as * + * been unlimboed, then it becomes a normal object in the game world. * + * * + * INPUT: pos -- The position to place the building on the map. * + * * + * dir (optional) -- not used for this class * + * * + * OUTPUT: bool; Was the unlimbo successful? * + * * + * WARNINGS: The unlimbo operation might not be successful if the * + * building could not be placed at the location specified. * + * * + * HISTORY: * + * 04/16/1994 JLB : Created. * + * 06/07/1994 JLB : Matches virtual function format for base class. * + * 05/09/1995 JLB : Handles wall placement. * + * 06/18/1995 JLB : Checks for wall legality before placing down. * + *=============================================================================================*/ +bool BuildingClass::Unlimbo(COORDINATE coord, DirType dir) +{ + Validate(); +#ifdef OBSOLETE + if (*this == STRUCT_ROAD) { + if (Can_Enter_Cell(Coord_Cell(coord), FACING_NONE) == MOVE_OK) { + ObjectClass * o = OverlayTypeClass::As_Reference(OVERLAY_ROAD).Create_One_Of(House); + if (o && o->Unlimbo(coord)) { + Transmit_Message(RADIO_OVER_OUT); + delete this; + return(true); + } + } + return(false); + } +#endif + + /* + ** If this is a wall type building, then it never gets unlimboed. Instead, it gets + ** converted to an overlay type. + */ + if (Class->IsWall) { + if (Can_Enter_Cell(Coord_Cell(coord), FACING_NONE) == MOVE_OK) { + OverlayType otype = OVERLAY_NONE; + switch (Class->Type) { + case STRUCT_SANDBAG_WALL: + otype = OVERLAY_SANDBAG_WALL; + break; + + case STRUCT_CYCLONE_WALL: + otype = OVERLAY_CYCLONE_WALL; + break; + + case STRUCT_BRICK_WALL: + otype = OVERLAY_BRICK_WALL; + break; + + case STRUCT_BARBWIRE_WALL: + otype = OVERLAY_BARBWIRE_WALL; + break; + + case STRUCT_WOOD_WALL: + otype = OVERLAY_WOOD_WALL; + break; + } + if (otype != OVERLAY_NONE) { + ObjectClass * o = OverlayTypeClass::As_Reference(otype).Create_One_Of(House); + if (o && o->Unlimbo(coord)) { + Map[Coord_Cell(coord)].Owner = House->Class->House; + Transmit_Message(RADIO_OVER_OUT); + delete this; + return(true); + } + } + } + return(false); + } + + /* + ** Normal building unlimbo process. + */ + if (TechnoClass::Unlimbo(coord, dir)) { + + /* + ** Ensure that the owning house knows about the + ** new object. + */ + House->BScan |= (1L << Class->Type); + House->ActiveBScan |= (1L << Class->Type); + + /* + ** Update the total factory type, assuming this building has a factory. + */ + switch (Class->ToBuild) { + case RTTI_AIRCRAFTTYPE: + House->AircraftFactories++; + break; + + case RTTI_INFANTRYTYPE: + House->InfantryFactories++; + break; + + case RTTI_UNITTYPE: + House->UnitFactories++; + break; + + case RTTI_BUILDINGTYPE: + House->BuildingFactories++; + break; + + default: + break; + } + + /* + ** Possibly the sidebar will be affected by this addition. + */ + House->IsRecalcNeeded = true; + LastStrength = 0; + + if ((!IsDiscoveredByPlayer && Map[Coord_Cell(coord)].IsVisible) || GameToPlay != GAME_NORMAL) { + Revealed(PlayerPtr); + } + if (!House->IsHuman) { + Revealed(House); + } + + if (IsOwnedByPlayer) { + Map.PowerClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingClass::Take_Damage -- Inflicts damage points upon a building. * + * * + * This routine will inflict damage points upon the specified building. * + * It will handle the damage animation and building destruction. Use * + * this routine whenever a building is attacked. * + * * + * INPUT: damage -- Amount of damage to inflict. * + * * + * distance -- The distance from the damage center point to the object's center point.* + * * + * warhead -- The kind of damage to inflict. * + * * + * source -- The source of the damage. This is used to change targeting. * + * * + * OUTPUT: true/false; Was the building destroyed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/21/1991 : Created. * + * 04/15/1994 JLB : Converted to member function. * + * 04/16/1994 JLB : Added warhead modifier to damage. * + * 06/03/1994 JLB : Added source of damage as target value. * + * 06/20/1994 JLB : Source is a base class pointer. * + * 11/22/1994 JLB : Shares base damage handler for techno objects. * + * 07/15/1995 JLB : Power ratio gets adjusted. * + *=============================================================================================*/ +ResultType BuildingClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source) +{ + Validate(); + ResultType res = RESULT_NONE; + int shakes; + + if (this != source) { + if (source) Base_Is_Attacked(source); + + short const *offset = Occupy_List(); + + /* + ** SPECIAL CASE: + ** SAM sites that are closed will take half damage, but never less than one point. + */ + if (*this == STRUCT_SAM && Status == SAM_UNDERGROUND) { + damage /= 2; + damage++; + } + + /* + ** Damage from an ion cannon against the Temple of Nod does more damage than + ** usual. + */ + if (GameToPlay == GAME_NORMAL && *this == STRUCT_TEMPLE && warhead == WARHEAD_PB) { + damage += damage/2; + } + + /* + ** Perform the low level damage assessment. + */ + res = TechnoClass::Take_Damage(damage, distance, warhead, source); + + switch (res) { + case RESULT_DESTROYED: + + /* + ** Destroy all attached objects. + */ + while (Attached_Object()) { + FootClass * obj = Detach_Object(); + + Detach_All(true); + delete obj; + } + + Sound_Effect(VOC_XPLOBIG4, Coord); + while (*offset != REFRESH_EOL) { + CELL cell = Coord_Cell(Coord) + *offset++; + + /* + ** If the building is destroyed, then lots of + ** explosions occur. + */ + new SmudgeClass(Random_Pick(SMUDGE_CRATER1, SMUDGE_CRATER6), Cell_Coord(cell)); + if (Random_Pick(0, 1) == 0) { + new AnimClass(ANIM_FIRE_SMALL, Coord_Scatter(Cell_Coord(cell), 0x0080), Random_Pick(0, 7), Random_Pick(1, 3)); + if (Random_Pick(0, 1) == 0) { + new AnimClass(ANIM_FIRE_MED, Coord_Scatter(Cell_Coord(cell), 0x0040), Random_Pick(0, 7), Random_Pick(1, 3)); + } + } + //Start_Profiler(); + new AnimClass(ANIM_FBALL1, Coord_Scatter(Cell_Coord(cell), 0x0040), Random_Pick(0,3)); + } + + shakes = Class->Cost_Of() / 400; + if (shakes) { + Shake_Screen(shakes); + } + Sound_Effect(VOC_CRUMBLE, Coord); + if (Mission == MISSION_DECONSTRUCTION) { + CountDown = 0; + Set_Rate(0); + } else { + CountDown = 8; + } + + /* + ** A destuction of the Temple by an ion cannon requires a global + ** remembering of this fact. The finale uses this information to + ** play the correct movie. + */ + if (*this == STRUCT_TEMPLE && warhead == WARHEAD_PB) { + TempleIoned = true; + } else { + TempleIoned = false; + } + break; + + case RESULT_HALF: + if (*this == STRUCT_PUMP) { + AnimClass *anim = new AnimClass(ANIM_OILFIELD_BURN, Coord_Add(Coord, 0x00400130L), 1); + if (anim) { + anim->Attach_To(this); + } + } + // Fall into next case. + + case RESULT_MAJOR: + Sound_Effect(VOC_XPLOBIG4, Coord); + while (*offset != REFRESH_EOL) { + CELL cell = Coord_Cell(Coord) + *offset++; + AnimClass * anim = NULL; + + /* + ** Show pieces of fire to indicate that a significant change in + ** damage level has occurred. + */ + if (warhead == WARHEAD_FIRE) { + switch (Random_Pick(0, 13)) { + case 0: + case 1: + case 2: + case 3: + case 4: + anim = new AnimClass(ANIM_ON_FIRE_SMALL, Coord_Scatter(Cell_Coord(cell), 0x0060), 0, Random_Pick(1, 3)); + break; + + case 5: + case 6: + case 7: + anim = new AnimClass(ANIM_ON_FIRE_MED, Coord_Scatter(Cell_Coord(cell), 0x0060), 0, Random_Pick(1, 3)); + break; + + case 8: + anim = new AnimClass(ANIM_ON_FIRE_BIG, Coord_Scatter(Cell_Coord(cell), 0x0060), 0, 1); + break; + + case 9: + case 10: + case 11: + case 12: + case 13: + break; + } + } else { + if (Random_Pick(0, 1) == 0) { + anim = new AnimClass(ANIM_FIRE_SMALL, Coord_Scatter(Cell_Coord(cell), 0x0060), Random_Pick(0, 7), Random_Pick(1, 3)); + } + } + + /* + ** If the animation was created, then attach it to the building. + */ + if (anim) { + anim->Attach_To(this); + } + } + break; + + case RESULT_NONE: + break; + } + + if (source && res != RESULT_NONE) { + + /* + ** If any damage occurred, then inform the house of this fact. If it is the player's + ** house, it might announce this fact. + */ + House->Attacked(); + + /* + ** Save the type of the house that's doing the damage, so if the building burns + ** to death credit can still be given for the kill + */ + WhoLastHurtMe = source->Owner(); + + /* + ** When certain buildings are hit, they "snap out of it" and + ** return fire if they are able and allowed. + */ + if (*this != STRUCT_SAM && + !House->Is_Ally(source) && + Class->Primary != WEAPON_NONE && + (!Target_Legal(TarCom) || !In_Range(TarCom))) { + + if (source->What_Am_I() != RTTI_AIRCRAFT && (!House->IsHuman || Special.IsSmartDefense)) { + Assign_Target(source->As_Target()); + } else { + + /* + ** Generate a random rotation effect since there is nothing else that this + ** building can do. + */ + if (!PrimaryFacing.Is_Rotating()) { + PrimaryFacing.Set_Desired(Random_Pick(DIR_N, DIR_MAX)); + } + } + } + } + } + + return(res); +} + + +/*********************************************************************************************** + * BuildingClass::Look -- Reveal map around building. * + * * + * Given a building, reveal the cells around the building in accordance * + * with the building's sighting range. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This is a very slow routine. Call only when necessary. * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void BuildingClass::Look(bool) +{ + Validate(); + if (IsOwnedByPlayer || IsDiscoveredByPlayer) { + Map.Sight_From(Coord_Cell(Center_Coord()), Class->SightRange, false); + } +} + + +/*********************************************************************************************** + * BuildingClass::new -- Allocates a building object from building pool. * + * * + * This routine will allocate a building slot from the building alloc * + * system. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocated building. If NULL is * + * returned, then this indicates a failure to allocate. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/11/1994 JLB : Created. * + * 04/21/1994 JLB : Converted to operator new. * + * 05/17/1994 JLB : Revamped allocation scheme * + * 07/29/1994 JLB : Simplified. * + *=============================================================================================*/ +void * BuildingClass::operator new(size_t ) +{ + void * ptr = Buildings.Allocate(); + if (ptr) { + ((BuildingClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * BuildingClass::delete -- Deallocates building object. * + * * + * This is the memory deallocation operation for a building object. * + * Since buildings are allocated out of a fixed memory block, all that * + * is needed is to flag the unit as inactive. * + * * + * INPUT: ptr -- Pointer to building to deallocate. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/21/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::operator delete(void *ptr) +{ + if (ptr) { + ((BuildingClass *)ptr)->IsActive = false; + } + Buildings.Free((BuildingClass *)ptr); +} + + +/*********************************************************************************************** + * BuildingClass::BuildingClass -- Constructor for buildings. * + * * + * This routine inserts a building into the object tracking system. * + * It is placed into a limbo state unless a location is provided for * + * it to unlimbo at. * + * * + * INPUT: type -- The structure type to make this object. * + * * + * house -- The owner of this building. * + * * + * pos -- The position to unlimbo the building. If -1 is * + * specified, then the building remains in a limbo * + * state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/21/1994 JLB : Created. * + * 08/07/1995 JLB : Fixed act like value to match expected value. * + *=============================================================================================*/ +BuildingClass::BuildingClass(StructType type, HousesType house) : + Class(&BuildingTypeClass::As_Reference(type)), + TechnoClass(house) +{ + PlacementDelay = 0; + LastStrength = 0; + ActLike = House->ActLike; + BState = BSTATE_NONE; + CountDown.Set(0); + Factory = 0; + House->CurBuildings++; + WhomToRepay = TARGET_NONE; + IsCaptured = false; + IsCharged = false; + IsCharging = false; + IsSurvivorless = false; + IsGoingToBlow = false; + IsReadyToCommence = false; + IsRepairing = false; + IsSecondShot = !Class->IsTwoShooter; + IsWrenchVisible = false; + QueueBState = BSTATE_NONE; + Strength = Class->MaxStrength; + WhoLastHurtMe = house; + Ammo = Class->MaxAmmo; + + /* + ** Make sure that newly built house specific building types will act like + ** the house they are supposed to act like, regardless of who the current + ** owner may happen to be. + */ + if ((type == STRUCT_AIRSTRIP || type == STRUCT_HAND) && house != HOUSE_BAD) { + ActLike = HOUSE_BAD; + IsCaptured = true; + } + if ((type == STRUCT_WEAP || type == STRUCT_BARRACKS) && house != HOUSE_GOOD) { + ActLike = HOUSE_GOOD; + IsCaptured = true; + } + + if (GameToPlay == GAME_INTERNET){ + House->BuildingTotals->Increment_Unit_Total( (int) type); + } + +} + + +/*********************************************************************************************** + * BuildingClass::~BuildingClass -- Destructor for building type objects. * + * * + * This destructor for building objects will put the building in limbo if possible. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/18/1995 JLB : Created. * + *=============================================================================================*/ +BuildingClass::~BuildingClass(void) +{ + if (GameActive && Class) { + if (House) { + House->CurBuildings--; + } + Limbo(); + } +} + + +/*********************************************************************************************** + * BuildingClass::Drop_Debris -- Drops rubble when building is destroyed. * + * * + * This routine is called when a building is destroyed. It handles * + * placing the rubble down. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + * 06/13/1995 JLB : Added smoke and normal infantry survivor possibility. * + * 07/16/1995 JLB : Survival rate depends on if captured or sabotaged. * + *=============================================================================================*/ +void BuildingClass::Drop_Debris(TARGET source) +{ + Validate(); + CELL const *offset; + CELL cell; + + /* + ** Special case for Moebius to run from destroyed technology + ** building. + */ + if (GameToPlay == GAME_NORMAL && *this == STRUCT_MISSION && PlayerPtr->ActLike == HOUSE_BAD && Scenario == 10) { + InfantryClass * i = new InfantryClass(INFANTRY_CHAN, House->Class->House); + + ScenarioInit++; + if (i->Unlimbo(Center_Coord(), DIR_N)) { + i->Trigger = TriggerClass::As_Pointer("CHAN"); + i->Strength = Random_Pick(5, (int)i->Class->MaxStrength); + ScenarioInit--; + i->Scatter(0, true); + ScenarioInit++; + i->Assign_Mission(MISSION_GUARD_AREA); + } else { + delete i; + } + ScenarioInit--; + } + + + /* + ** Generate random survivors from the destroyed building. + */ + cell = Coord_Cell(Coord); + offset = Occupy_List(); + int odds = 2; + if (Target_Legal(WhomToRepay)) odds -= 1; + if (IsCaptured) odds += 6; + while (*offset != REFRESH_EOL) { + CELL newcell; + + newcell = cell + *offset++; + + /* + ** Infantry could run out of a destroyed building. + */ + if (!House->IsToDie && !IsSurvivorless) { + InfantryClass * i = NULL; + + if (Random_Pick(0, odds) == 1) { + i = new InfantryClass(Crew_Type(), House->Class->House); + if (i) { + if (Class->Get_Buildup_Data() != NULL && i->Class->IsNominal) i->IsTechnician = true; + ScenarioInit++; + if (i->Unlimbo(Cell_Coord(newcell), DIR_N)) { + i->Strength = Random_Pick(5, (int)i->Class->MaxStrength); + i->Scatter(0, true); + if (source != TARGET_NONE && !House->Is_Ally(As_Object(source))) { + i->Assign_Mission(MISSION_ATTACK); + i->Assign_Target(source); + } else { + if (House->IsHuman) { + i->Assign_Mission(MISSION_GUARD); + } else { + i->Assign_Mission(MISSION_HUNT); + } + } + } else { + delete i; + } + ScenarioInit--; + } + } + } + + /* + ** Possibly add some smoke rising from the ashes of the building. + */ + switch (Random_Pick(0, 5)) { + case 0: + case 1: + case 2: + new AnimClass(ANIM_SMOKE_M, Coord_Scatter(Cell_Coord(newcell), 0x0050, false), Random_Pick(0, 5), Random_Pick(1, 2)); + break; + + default: + break; + } + + /* + ** The building always scars the ground in some fashion. + */ + if (Random_Pick(0, 3) == 0) { + new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), Cell_Coord(newcell)); + } else { + new SmudgeClass(Random_Pick(SMUDGE_CRATER1, SMUDGE_CRATER6), Coord_Scatter(Cell_Coord(newcell), 0x0080, false)); + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Active_Click_With -- Handles clicking on the map while the building is selected.* + * * + * This interface routine handles when the player clicks on the map while this building * + * is currently selected. This is used to assign an override target to a turret or * + * guard tower. * + * * + * INPUT: target -- The target that was clicked upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + Validate(); + if (action == ACTION_ATTACK) { + Player_Assign_Mission(MISSION_ATTACK, object->As_Target()); + } + + if (action == ACTION_SELF && Class->IsFactory) { + OutList.Add(EventClass(EventClass::PRIMARY, As_Target())); + } +} + + +/*********************************************************************************************** + * BuildingClass::Active_Click_With -- Handles cell selection for buildings. * + * * + * This routine really only serves one purpose -- to allow targeting of the ground for * + * buildings that are euipped with weapons. * + * * + * INPUT: action -- The requested action to perform. * + * * + * cell -- The cell location to perform the action upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Active_Click_With(ActionType action, CELL cell) +{ + Validate(); + if (action == ACTION_ATTACK) { + Player_Assign_Mission(MISSION_ATTACK, ::As_Target(cell)); + } +} + + +/*********************************************************************************************** + * BuildingClass::Assign_Target -- Assigns a target to the building. * + * * + * Assigning of a target to a building makes sense if the building is one that can attack. * + * This routine would be used to assign the attack target to a turret or guard tower. * + * * + * INPUT: target -- The target that was clicked on while this building was selected. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + * 11/02/1994 JLB : Checks for range before assigning target. * + *=============================================================================================*/ +void BuildingClass::Assign_Target(TARGET target) +{ + Validate(); + if (*this != STRUCT_SAM && !In_Range(target, 0)) { + target = TARGET_NONE; + } + + TechnoClass::Assign_Target(target); +} + + +/*********************************************************************************************** + * BuildingClass::Init -- Initialize the building system to an empty null state. * + * * + * This routine initializes the building system in preparation for a scenario load. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Init(void) +{ + BuildingClass *ptr; + + Buildings.Free_All(); + + ptr = new BuildingClass(); + VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0]; + delete ptr; +} + + +/*********************************************************************************************** + * BuildingClass::Exit_Object -- Initiates an object to leave the building. * + * * + * This function is used to cause an object to exit the building. It is called when a * + * factory produces a vehicle or other mobile object and that object needs to exit the * + * building to join the ranks of a regular unit. Typically, the object is placed down on * + * the map such that it overlaps the building and then it is given a movement order so that * + * it will move to an adjacent free cell. * + * * + * INPUT: base -- Pointer to the object that is to exit the building. * + * * + * OUTPUT: Returns the success rating for the exit attempt; * + * 0 = complete failure (refund money please) * + * 1 = temporarily prevented (try again later please) * + * 2 = successful * + * * + * WARNINGS: The building is placed in radio contact with the object. The object is in a * + * teathered condition. This condition will be automatically broken when the * + * object reaches the adjacent square. * + * * + * HISTORY: * + * 11/28/1994 JLB : Created. * + * 04/10/1995 JLB : Handles building production by computer. * + * 06/17/1995 JLB : Handles refinery exit. * + *=============================================================================================*/ +int BuildingClass::Exit_Object(TechnoClass * base) +{ + Validate(); + if (!base) return(0); + + TechnoTypeClass const * ttype = (TechnoTypeClass const *)&base->Class_Of(); + + /* + ** A unit exiting a building is always considered to be "locked". That means, it + ** will be considered as to have legally entered the visible map domain. + */ + base->IsLocked = true; + + /* + ** Find a good cell to unload the object to. The object, probably a vehicle + ** will drive/walk to the adjacent free cell. + */ + switch (base->What_Am_I()) { + CELL cell; + + case RTTI_AIRCRAFT: + if (!In_Radio_Contact()) { + AircraftClass *air = (AircraftClass *)base; + + air->Altitude = 0; + ScenarioInit++; + if (air->Unlimbo(Docking_Coord(), air->Pose_Dir())) { + Transmit_Message(RADIO_HELLO, air); + Transmit_Message(RADIO_TETHER); + ScenarioInit--; + return(2); + } + ScenarioInit--; + } else { + AircraftClass *air = (AircraftClass *)base; + + if (Cell_X(Coord_Cell(Center_Coord())) - Map.MapCellX < Map.MapCellWidth/2) { + cell = XY_Cell(Map.MapCellX-1, Random_Pick(0, Map.MapCellHeight-1)+Map.MapCellY); + } else { + cell = XY_Cell(Map.MapCellX+Map.MapCellWidth, Random_Pick(0, Map.MapCellHeight-1)+Map.MapCellY); + } + ScenarioInit++; + if (air->Unlimbo(Cell_Coord(cell), DIR_N)) { + air->Assign_Destination(::As_Target(Nearby_Location(air))); + air->Assign_Mission(MISSION_MOVE); + ScenarioInit--; + return(2); + } + ScenarioInit--; + } + break; + + case RTTI_INFANTRY: + case RTTI_UNIT: + switch (Class->Type) { + case STRUCT_REFINERY: + if (base->What_Am_I() == RTTI_UNIT) { + CELL cell = Coord_Cell(Center_Coord()); + UnitClass * unit = (UnitClass *)base; + + cell = Adjacent_Cell(cell, FACING_SW); + ScenarioInit++; + if (unit->Unlimbo(Coord_Add(unit->Coord, 0x00550060L), DIR_SW_X2)) { + unit->PrimaryFacing = DIR_SW_X2; + Transmit_Message(RADIO_HELLO, unit); + Transmit_Message(RADIO_TETHER); + unit->Assign_Mission(MISSION_HARVEST); + unit->Force_Track(DriveClass::OUT_OF_REFINERY, Cell_Coord(cell)); + unit->Set_Speed(128); + } + ScenarioInit--; + } else { + base->Scatter(true); + } + break; + + case STRUCT_AIRSTRIP: + if (Create_Special_Reinforcement(House, &AircraftTypeClass::As_Reference(AIRCRAFT_CARGO), ttype, TMISSION_UNLOAD, As_Target())) { + delete base; + return(2); + } + return(0); + + case STRUCT_WEAP: + ScenarioInit++; + if (base->Unlimbo(Coord_Add(Coord, Class->ExitPoint), DIR_SW)) { +// base->Assign_Mission(MISSION_MOVE); +// base->Assign_Destination(::As_Target(As_Cell(Coord)+MAP_CELL_W*2)); + base->Mark(MARK_UP); + base->Coord = Coord_Add(Coord, Class->ExitPoint); + base->Mark(MARK_DOWN); + Transmit_Message(RADIO_HELLO, base); + Transmit_Message(RADIO_TETHER); + Assign_Mission(MISSION_UNLOAD); + ScenarioInit--; + return(2); + } + ScenarioInit--; + break; + + case STRUCT_BARRACKS: + case STRUCT_HAND: + CELL cell; + bool found = false; + + cell = Find_Exit_Cell(base); + if (cell) found = true; + +#ifdef OBSOLETE + CELL const *ptr; + bool found = false; + + ptr = Class->ExitList; + while (*ptr != REFRESH_EOL) { + cell = Coord_Cell(Coord) + *ptr++; + if (base->Can_Enter_Cell(cell) == MOVE_OK) { + found = true; + break; + } + } +#endif + + if (found) { + DirType dir = Direction(cell); + COORDINATE start = Coord_Add(Coord, Class->ExitPoint); + + ScenarioInit++; + if (base->Unlimbo(start, dir)) { + + base->Assign_Mission(MISSION_MOVE); + base->Assign_Destination(::As_Target(cell)); + + /* + ** Establish radio contact so unload coordination can occur. This + ** radio contact should always succeed. + */ + if (Transmit_Message(RADIO_HELLO, base) == RADIO_ROGER) { + Transmit_Message(RADIO_UNLOAD); + } + ScenarioInit--; + return(2); + } + ScenarioInit--; + } + break; + } + break; + + case RTTI_BUILDING: + + if (!House->IsHuman) { + /* + ** Find the next available spot to place this newly created building. If the + ** building could be placed at the desired location, fine. If not, then this + ** routine will return failure. The calling routine will probably abandon this + ** building in preference to building another. + */ + BaseNodeClass * node = Base.Next_Buildable(((BuildingClass *)base)->Class->Type); + if (node) { + if (Flush_For_Placement(base, Coord_Cell(node->Coord))) { + return(1); + } + if (base->Unlimbo(node->Coord)) { + return(2); + } + } + } + break; + } + + /* + ** Failure to exit the object results in a false return value. + */ + return(0); +} + + +/*********************************************************************************************** + * BuildingClass::Update_Buildables -- Informs sidebar of additional construction options. * + * * + * This routine will tell the sidebar of objects that can be built. The function is called * + * whenever a building matures. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/11/1994 JLB : Created. * + * 12/23/1994 JLB : Only updates for PLAYER buildings. * + *=============================================================================================*/ +void BuildingClass::Update_Buildables(void) +{ + Validate(); + if (House == PlayerPtr && !IsInLimbo && IsDiscoveredByPlayer) { + switch (Class->ToBuild) { + StructType i; + UnitType u; + InfantryType f; + AircraftType a; + + case RTTI_BUILDINGTYPE: + for (i = STRUCT_FIRST; i < STRUCT_COUNT; i++) { + if (PlayerPtr->Can_Build(i, ActLike)) { +// if (BuildingTypeClass::As_Reference(i).Who_Can_Build_Me(true, true, ActLike)) { + Map.Add(RTTI_BUILDINGTYPE, i); +// } + } + } + break; + + case RTTI_UNITTYPE: + for (u = UNIT_FIRST; u < UNIT_COUNT; u++) { + if (PlayerPtr->Can_Build(u, ActLike)) { +// if (UnitTypeClass::As_Reference(u).Who_Can_Build_Me(true, true, ActLike)) { + Map.Add(RTTI_UNITTYPE, u); +// } + } + } + break; + + case RTTI_INFANTRYTYPE: + for (f = INFANTRY_FIRST; f < INFANTRY_COUNT; f++) { + if (PlayerPtr->Can_Build(f, ActLike)) { +// if (InfantryTypeClass::As_Reference(f).Who_Can_Build_Me(true, true, ActLike)) { + Map.Add(RTTI_INFANTRYTYPE, f); +// } + } + } + break; + + case RTTI_AIRCRAFTTYPE: + for (a = AIRCRAFT_FIRST; a < AIRCRAFT_COUNT; a++) { + if (PlayerPtr->Can_Build(a, ActLike)) { +// if (AircraftTypeClass::As_Reference(a).Who_Can_Build_Me(true, true, ActLike)) { + Map.Add(RTTI_AIRCRAFTTYPE, a); +// } + } + } + break; + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Fire_Out -- Handles when attached animation expires. * + * * + * This routine is used to perform any fixups necessary when the attached animation has * + * terminated. This occurs when the fire & smoke animation that a SAM site produces stops. * + * At that point, normal reload procedures can commence. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/30/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Fire_Out(void) +{ + Validate(); +// SAM = SAM_READY; +// IsFiring = false; +} + + +/*********************************************************************************************** + * BuildingClass::Limbo -- Handles power adjustment as building goes into limbo. * + * * + * This routine will handle the power adjustments for the associated house when the * + * building goes into limbo. This means that its power drain or production is subtracted * + * from the house accumulated totals. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the building limboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +bool BuildingClass::Limbo(void) +{ + Validate(); +// HouseClass *housep; +// RTTIType bld_type; + + if (!IsInLimbo) { + + /* + ** Update the total factory type, assuming this building has a factory. + */ + switch (Class->ToBuild) { + case RTTI_AIRCRAFTTYPE: + House->AircraftFactories--; + break; + + case RTTI_INFANTRYTYPE: + House->InfantryFactories--; + break; + + case RTTI_UNITTYPE: + House->UnitFactories--; + break; + + case RTTI_BUILDINGTYPE: + House->BuildingFactories--; + break; + + default: + break; + } + + House->IsRecalcNeeded = true; + + /* + ** Update the power status of the owner's house. + */ + House->Adjust_Power(-Power_Output()); + House->Adjust_Drain(-Class->Drain); + House->Adjust_Capacity(-Class->Capacity, true); + if (House == PlayerPtr) { + Map.PowerClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); + } + +#ifdef OBSOLETE + /* + ** If this building is building something, the FactoryClass doing the + ** building must be deleted, as well as the object being built. + ** - If this building is controlled by Computer AI, this building's Factory + ** pointer will point to the factory doing the building. + ** - Otherwise, the owner House's Factory indices will indicate what's + ** being built. (The player's sidebar also contains indices to indicate + ** what's being built, but those just echo the house's indices.) + */ + if (Factory) { + Factory->Abandon(); + delete Factory; + Factory = 0; + } + + /* + ** If the owner HouseClass is building something, and this building can + ** build that thing, we may be the last building for that house that can + ** build that thing; if so, abandon production of it. + */ + housep = HouseClass::As_Pointer(Owner()); + if (housep) { + FactoryClass * factory = 0; + bld_type = RTTI_NONE; + if (housep->AircraftFactory != -1 && Class->ToBuild == RTTI_AIRCRAFTTYPE) { + bld_type = RTTI_AIRCRAFTTYPE; + factory = Factories.Raw_Ptr(housep->AircraftFactory); + } + if (housep->InfantryFactory != -1 && Class->ToBuild==RTTI_INFANTRYTYPE) { + bld_type = RTTI_INFANTRYTYPE; + factory = Factories.Raw_Ptr(housep->InfantryFactory); + } + if (housep->UnitFactory != -1 && Class->ToBuild==RTTI_UNITTYPE) { + bld_type = RTTI_UNITTYPE; + factory = Factories.Raw_Ptr(housep->UnitFactory); + } + if (housep->BuildingFactory != -1 && Class->ToBuild==RTTI_BUILDINGTYPE) { + bld_type = RTTI_BUILDINGTYPE; + factory = Factories.Raw_Ptr(housep->BuildingFactory); + } + if (housep->SpecialFactory != -1 && Class->ToBuild==RTTI_SPECIAL) { + bld_type = RTTI_SPECIAL; + } + + if (bld_type != RTTI_NONE) { + if (factory) { + TechnoClass * object = factory->Get_Object(); + IsInLimbo = true; + if (object && !object->Techno_Type_Class()->Who_Can_Build_Me(true, false, housep->Class->House)) { + housep->Abandon_Production(bld_type); + } + IsInLimbo = false; + } + } + } +#endif + + /* + ** This could be a building that builds. If so, then the sidebar may need adjustment. + ** Set IsInLimbo to true to "fool" the sidebar into knowing that this building + ** isn't available. Set it back to false so the rest of the Limbo code works. + ** Otherwise, the sidebar won't properly remove non-available buildables. + */ + if (IsOwnedByPlayer && !ScenarioInit) { + IsInLimbo = true; + Map.Recalc(); + IsInLimbo = false; + } +#ifdef NEVER + if (!House->IsHuman) { + Update_Specials(); + } +#endif + + } + return(TechnoClass::Limbo()); +} + + +/*********************************************************************************************** + * BuildingClass::Fire_Coord -- Calculates the coordinate that projectile would appear. * + * * + * This routine is used to determine where a projectile would appear if this building * + * were to fire. The location usually depends on the current rotation setting of the * + * turret or else on the direction of the target that will be fired upon. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with coordinate that the projectile should appear at if the building * + * were to fire. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +COORDINATE BuildingClass::Fire_Coord(int ) const +{ + Validate(); + COORDINATE coord = Center_Coord(); // Center of firing building. + + /* + ** Make adjustments to the firing coordinate to account for turret + ** position. This depends on building type and turret facing. + */ + switch (Class->Type) { + default: + case STRUCT_GTOWER: + case STRUCT_ATOWER: + coord = Coord_Move(coord, DIR_N, 0x0030); + if (Target_Legal(TarCom)) { + coord = Coord_Move(coord, ::Direction(coord, As_Coord(TarCom)), 0x0040); + } + break; + case STRUCT_OBELISK: + coord = Coord_Move(coord, DIR_N, 0x00A8); +// coord = Coord_Move(coord, DIR_N, 0x0058); + coord = Coord_Move(coord, DIR_W, 0x0018); + break; + + case STRUCT_SAM: + case STRUCT_TURRET: + coord = Coord_Move(coord, DIR_N, 0x0030); + coord = Coord_Move(coord, PrimaryFacing.Current(), 0x0080); + break; + } + + return(coord); +} + + +/*********************************************************************************************** + * BuildingClass::Greatest_Threat -- Searches for target that building can fire upon. * + * * + * This routine intercepts the Greatest_Threat function so that it can add the ability * + * to search for ground targets, if this isn't a SAM site. * + * * + * INPUT: threat -- The base threat control value. Typically, it might be THREAT_RANGE * + * or THREAT_NORMAL. * + * * + * OUTPUT: Returns with a suitable target. If none could be found, then TARGET_NONE is * + * returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + *=============================================================================================*/ +TARGET BuildingClass::Greatest_Threat(ThreatType threat) const +{ + Validate(); + if (*this != STRUCT_SAM) { + threat = threat | (THREAT_INFANTRY|THREAT_BOATS|THREAT_VEHICLES|THREAT_RANGE); + + if (!House->IsHuman) { + threat = threat | THREAT_BUILDINGS; + } +// threat = threat & ~THREAT_AIR; + } else { + threat = threat | THREAT_AREA; + } + + if (Class->Primary != WEAPON_NONE && BulletTypeClass::As_Reference(Weapons[Class->Primary].Fires).IsAntiAircraft) { + threat = threat | THREAT_AIR; + } + return(TechnoClass::Greatest_Threat(threat)); +} + + +/*********************************************************************************************** + * BuildingClass::Grand_Opening -- Handles construction completed special operations. * + * * + * This routine is called when construction has finished. Typically, this enables * + * new production options for factories. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/08/1995 JLB : Created. * + * 06/13/1995 JLB : Added helipad. * + *=============================================================================================*/ +void BuildingClass::Grand_Opening(bool captured) +{ + Validate(); + + /* + ** Adjust the owning house according to the power, drain, and Tiberium capacity that + ** this building has. + */ + House->Adjust_Drain(Class->Drain); + House->Adjust_Capacity(Class->Capacity); + House->IsRecalcNeeded = true; + + /* SPECIAL CASE: + ** Refineries get a free harvester. Add a harvester to the reinforcement list + ** at this time. + */ + if (*this == STRUCT_REFINERY && !ScenarioInit && !captured && !Debug_Map && (!House->IsHuman || PurchasePrice == 0 || PurchasePrice > Class->Raw_Cost())) { + CELL cell = Coord_Cell(Adjacent_Cell(Center_Coord(), DIR_SW)); +// if (!Map[cell].Cell_Unit()) { + UnitClass * unit = new UnitClass(UNIT_HARVESTER, House->Class->House); + if (unit) { + + /* + ** Try to place down the harvesters. If it could not be placed, then try + ** to place it in a nearby location. + */ + if (!unit->Unlimbo(Cell_Coord(cell), DIR_SW)) { + cell = Nearby_Location(unit); + + /* + ** If the harvester could still not be placed, then refund the money + ** to the owner and then bail. + */ + if (!unit->Unlimbo(Cell_Coord(cell), DIR_SW)) { + House->Refund_Money(unit->Class->Cost_Of()); + delete unit; + } + } + } else { + + /* + ** If the harvester could not be created in the first place, then give + ** the full refund price to the owning player. + */ + House->Refund_Money(UnitTypeClass::As_Reference(UNIT_HARVESTER).Cost_Of()); + } + } +// } + + /* + ** Helicopter pads get a free attack helicopter. + */ + if (*this == STRUCT_HELIPAD && !captured && (!House->IsHuman || PurchasePrice == 0 || PurchasePrice > Class->Raw_Cost())) { + ScenarioInit++; + AircraftClass * air = 0; + if (House->ActLike == HOUSE_GOOD) { + air = new AircraftClass(AIRCRAFT_ORCA, House->Class->House); + } else { + air = new AircraftClass(AIRCRAFT_HELICOPTER, House->Class->House); + } + if (air) { + air->Altitude = 0; + if (air->Unlimbo(Docking_Coord(), air->Pose_Dir())) { + air->Assign_Mission(MISSION_GUARD); + air->Transmit_Message(RADIO_HELLO, this); + Transmit_Message(RADIO_TETHER); + } + } + ScenarioInit--; + } +} + + +/*********************************************************************************************** + * BuildingClass::Repair -- Initiates or terminates the repair process. * + * * + * This routine will start, stop, or toggle the repair process. When a building repairs, it * + * occurs incrementally over time. * + * * + * INPUT: control -- Determines how to control the repair process. * + * 0: Turns repair process off (if it was on). * + * 1: Turns repair process on (if it was off). * + * -1:Toggles repair process to other state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/08/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Repair(int control) +{ + Validate(); + switch (control) { + case -1: + IsRepairing = (IsRepairing == false); + break; + + case 1: + if (IsRepairing) return; + IsRepairing = true; + break; + + case 0: + if (!IsRepairing) return; + IsRepairing = false; + break; + } + + /* + ** At this point, we know that the repair state has changed. Perform + ** appropriate action. + */ + VocType sound = VOC_NONE; + if (IsRepairing) { + if (Strength == Class->MaxStrength) { + sound = VOC_SCOLD; + } else { + sound = VOC_BUTTON; + Clicked_As_Target(); + IsWrenchVisible = true; + } + } else { + sound = VOC_BUTTON; + } + if (IsOwnedByPlayer) { + Sound_Effect(sound, Coord); + } +} + + +/*********************************************************************************************** + * BuildingClass::Sell_Back -- Controls the sell back (demolish) operation. * + * * + * This routine will initiate or stop the sell back process for a building. It is called * + * when the player clicks on a building when the sell mode is active. * + * * + * INPUT: control -- The action to perform. 0 = turn deconstruction off, 1 = deconstruct, * + * -1 = toggle deconstruction state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Sell_Back(int control) +{ + Validate(); + if (Class->Get_Buildup_Data()) { + bool decon = false; + switch (control) { + case -1: + decon = (Mission != MISSION_DECONSTRUCTION); + break; + + case 1: + if (Mission == MISSION_DECONSTRUCTION) return; + decon = true; + break; + + case 0: + if (Mission != MISSION_DECONSTRUCTION) return; + decon = false; + break; + } + + /* + ** At this point, we know that the repair state has changed. Perform + ** appropriate action. + */ + if (decon) { +// Transmit_Message(RADIO_RUN_AWAY); +// Transmit_Message(RADIO_OVER_OUT); + Assign_Mission(MISSION_DECONSTRUCTION); + if (IsOwnedByPlayer) { + Clicked_As_Target(); + } + } + if (IsOwnedByPlayer) { + Sound_Effect(VOC_BUTTON); + } + } +} + + +/*********************************************************************************************** + * BuildingClass::What_Action -- Determines action to perform if click on specified object. * + * * + * This routine will determine what action to perform if the mouse was clicked on the * + * object specified. This determination is used to control the mouse imagery and the * + * function process when the mouse button is pressed. * + * * + * INPUT: object -- Pointer to the object that, if clicked on, will control what action * + * is to be performed. * + * * + * OUTPUT: Returns with the ActionType that will occur if the mouse is clicked over the * + * object specified while the building is currently selected. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/18/1995 JLB : Created. * + *=============================================================================================*/ +ActionType BuildingClass::What_Action(ObjectClass * object) const +{ + Validate(); + ActionType action = TechnoClass::What_Action(object); + + if (action == ACTION_SELF) { + if (Class->IsFactory && PlayerPtr == House) { + switch (Class->ToBuild) { + case RTTI_AIRCRAFTTYPE: + if (House->AircraftFactories < 2) { + action = ACTION_NONE; + } + break; + + case RTTI_INFANTRYTYPE: + if (House->InfantryFactories < 2) { + action = ACTION_NONE; + } + break; + + case RTTI_UNITTYPE: + if (House->UnitFactories < 2) { + action = ACTION_NONE; + } + break; + + default: + action = ACTION_NONE; + break; + } + + } else { + action = ACTION_NONE; + } + } + + /* + ** Don't allow targeting of SAM sites, even if the CTRL key + ** is held down. + */ + if (action == ACTION_ATTACK && *this == STRUCT_SAM) { + action = ACTION_NONE; + } + + if (action == ACTION_MOVE) { + action = ACTION_NONE; + } + + return(action); +} + + +/*********************************************************************************************** + * BuildingClass::What_Action -- Determines what action will occur. * + * * + * This routine examines the cell specified and returns with the action that will be * + * performed if that cell were clicked upon while the building is selected. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: Returns the ActionType that indicates what should occur if the mouse is clicked * + * on this cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/18/1995 JLB : Created. * + *=============================================================================================*/ +ActionType BuildingClass::What_Action(CELL cell) const +{ + Validate(); + ActionType action = TechnoClass::What_Action(cell); + + if (action == ACTION_MOVE) { + action = ACTION_NONE; + } + + /* + ** Don't allow targeting of SAM sites, even if the CTRL key + ** is held down. + */ + if (action == ACTION_ATTACK && *this == STRUCT_SAM) { + action = ACTION_NONE; + } + + return(action); +} + + +/*********************************************************************************************** + * BuildingClass::Begin_Mode -- Begins an animation mode for the building. * + * * + * This routine will start the building animating. This animation will loop indefinately * + * until explicitly stopped. * + * * + * INPUT: bstate -- The animation state to initiate. * + * * + * OUTPUT: none * + * * + * WARNINGS: The buliding graphic state will reflect the first stage of this animation the * + * very next time it is rendered. * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 07/02/1995 JLB : Uses normalize animation rate where applicable. * + *=============================================================================================*/ +void BuildingClass::Begin_Mode(BStateType bstate) +{ + Validate(); + QueueBState = bstate; + if (BState == BSTATE_NONE || bstate == BSTATE_CONSTRUCTION || ScenarioInit) { + BState = bstate; + QueueBState = BSTATE_NONE; + BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control(); + + int rate = ctrl->Rate; + if (Class->IsRegulated && bstate != BSTATE_CONSTRUCTION) { + rate = Options.Normalize_Delay(rate); + } + Set_Rate(rate); + Set_Stage(ctrl->Start); + } +} + + +/*********************************************************************************************** + * BuildingClass::Read_INI -- Reads buildings from INI file. * + * * + * This is the basic scenario initialization of building function. It * + * is called when reading the scenario startup INI file and it handles * + * creation of all specified buildings. * + * * + * INI entry format: * + * Housename, Typename, Strength, Cell, Facing, Triggername * + * * + * INPUT: buffer -- Pointer to the loaded INI file data. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Read_INI(char *buffer) +{ + BuildingClass *b; // Working unit pointer. + char *tbuffer; // Accumulation buffer of unit IDs. + HousesType bhouse; // Building house. + StructType classid; // Building type. + int len; // Size of data in buffer. + CELL cell; // Cell of building. + char buf[128]; + char *trigname; // building's trigger's name + + len = strlen(buffer) + 2; + tbuffer = buffer + len; + + /* + ** Read the entire building INI section into HIDBUF + */ + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); + + while (*tbuffer != '\0') { + + /* + ** Get a building entry. + */ + WWGetPrivateProfileString(INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, buffer); + + /* + ** 1st token: house name. + */ + bhouse = HouseTypeClass::From_Name(strtok(buf, ",")); + + /* + ** 2nd token: building name. + */ + classid = BuildingTypeClass::From_Name(strtok(NULL, ",")); + + if (bhouse != HOUSE_NONE && classid != STRUCT_NONE) { + int strength; + DirType facing; + + /* + ** 3rd token: strength. + */ + strength = atoi(strtok(NULL, ",")); + + /* + ** 4th token: cell #. + */ + cell = atoi(strtok(NULL, ",")); + + /* + ** 5th token: facing. + */ + facing = (DirType)atoi(strtok(NULL, ",")); + + /* + ** 6th token: triggername (can be NULL). + */ + trigname = strtok(NULL,","); + + b = new BuildingClass(classid, bhouse); + if (b) { + if (b->Unlimbo(Cell_Coord(cell), facing)) { + strength = MIN(strength, 0x100); + strength = Fixed_To_Cardinal(b->Class->MaxStrength, strength); + b->Strength = strength; + b->IsALemon = false; + b->Trigger = TriggerClass::As_Pointer(trigname); + if (b->Trigger) { + b->Trigger->AttachCount++; + } + } else { + + /* + ** If the building could not be unlimboed on the map, then this indicates + ** a serious error. Delete the building. + */ + delete b; + } + } + } + tbuffer += strlen(tbuffer)+1; + } +} + + +/*********************************************************************************************** + * BuildingClass::Write_INI -- Writes all building data to an INI file. * + * * + * This routine is used to write the buildings into an INI file. It is necessary for the * + * scenario editor save game option. * + * * + * INI entry format: * + * Housename, Typename, Strength, Cell, Facing, Triggername * + * * + * INPUT: buffer -- The buffer that holds the INI data. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Write_INI(char *buffer) +{ + int index; + char uname[10]; + char buf[127]; + char *tbuffer; // Accumulation buffer of unit IDs. + + /* + ** First, clear out all existing building data from the ini file. + */ + tbuffer = buffer + strlen(buffer) + 2; + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-strlen(buffer), buffer); + while (*tbuffer != '\0') { + WWWritePrivateProfileString(INI_Name(), tbuffer, NULL, buffer); + tbuffer += strlen(tbuffer)+1; + } + + /* + ** Write the data out. + */ + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass * building; + + building = Buildings.Ptr(index); + if (!building->IsInLimbo) { + + sprintf(uname, "%03d", index); + sprintf(buf, "%s,%s,%d,%u,%d,%s", + building->House->Class->IniName, + building->Class->IniName, + building->Health_Ratio(), + Coord_Cell(building->Coord), + building->PrimaryFacing.Current(), + building->Trigger ? building->Trigger->Get_Name() : "None" + ); + WWWritePrivateProfileString(INI_Name(), uname, buf, buffer); + } + } +} + + +/*********************************************************************************************** + * BuildingClass::As_Target -- Convert the building into a target value. * + * * + * Use this routine to take the building and convert it into a target number. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the target number for this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +TARGET BuildingClass::As_Target(void) const +{ + Validate(); + return(Build_Target(KIND_BUILDING, Buildings.ID(this))); +} + + +/*********************************************************************************************** + * BuildingClass::Center_Coord -- Fetches the center coordinate for the building. * + * * + * This routine is used to set the center coordinate for this building. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate for the center location for the building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE BuildingClass::Center_Coord(void) const +{ + Validate(); + return(Coord_Add(Coord, CenterOffset[Class->Size])); +} + + +COORDINATE BuildingClass::Docking_Coord(void) const +{ + Validate(); + if (*this == STRUCT_HELIPAD) { + return(Coord_Add(Coord, XYP_COORD(24, 18))); + } + if (*this == STRUCT_AIRSTRIP) { + return(Coord_Add(Coord, XYP_COORD(18, 30))); + } + return(TechnoClass::Docking_Coord()); +} + + +/*********************************************************************************************** + * BuildingClass::Can_Fire -- Determines if this building can fire. * + * * + * Use this routine to see if the building can fire its weapon. * + * * + * * + * INPUT: target -- The target that firing upon is desired. * + * * + * which -- Which weapon to use when firing. 0=primary, 1=secondary. * + * * + * OUTPUT: Returns with the fire possibility code. If firing is allowed, then FIRE_OK is * + * returned. Other cases will result in appropriate fire code value that indicates * + * why firing is not allowed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/03/1995 JLB : Created. * + *=============================================================================================*/ +FireErrorType BuildingClass::Can_Fire(TARGET target, int which) const +{ + Validate(); + FireErrorType canfire = TechnoClass::Can_Fire(target, which); + + if (canfire == FIRE_OK) { + + /* + ** Double check to make sure that the facing is roughly toward + ** the target. If the difference is too great, then firing is + ** temporarily postponed. + */ + if (Class->IsTurretEquipped) { + /* + ** If the turret is rotating then firing must be delayed. + */ + if (PrimaryFacing.Is_Rotating()) { + return(FIRE_ROTATING); + } + + int diff = PrimaryFacing.Difference(Direction(TarCom)); + if (ABS(diff) > 8) { + return(FIRE_FACING); + } + } + + /* + ** Advanced guard towers need power to fire. + */ + if (*this == STRUCT_ATOWER && House->Power_Fraction() < 0x0100) { + return(FIRE_BUSY); + } + + /* + ** If an obelisk can fire, check the state of charge. If it isn't charging + ** up, start it charging up and return FIRE_BUSY. If it is charging but + ** isn't done yet, return FIRE_BUSY. If it's done charging, stop the + ** charging process, clear the stage timer, and return FIRE_OK. + */ + if (Class->Primary == WEAPON_OBELISK_LASER && !IsCharged) { + return(FIRE_BUSY); + } + } + return(canfire); +} + + +/*********************************************************************************************** + * BuildingClass::Toggle_Primary -- Toggles the primary factory state. * + * * + * This routine will change the primary factory state of this building. The primary * + * factory is the one that units will be produced from (by default). * + * * + * INPUT: none * + * * + * OUTPUT: Is this building NOW the primary factory? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/03/1995 JLB : Created. * + *=============================================================================================*/ +bool BuildingClass::Toggle_Primary(void) +{ + Validate(); + if (IsLeader) { + IsLeader = false; + } else { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + if (!building->IsInLimbo && building->Owner() == Owner() && building->Class->ToBuild == Class->ToBuild) { + building->IsLeader = false; + } + } + IsLeader = true; + if (House == PlayerPtr) { + Speak(VOX_PRIMARY_SELECTED); + } + } + Mark(MARK_CHANGE); + return(IsLeader); +} + + +/*********************************************************************************************** + * BuildingClass::Captured -- Captures the building. * + * * + * This routine will change the owner of the building. It handles updating any related * + * game systems as a result. Factories are the most prone to have great game related * + * consequences when captured. This could also affect the sidebar and building ownership. * + * * + * INPUT: newowner -- Pointer to the house that is now the new owner. * + * * + * OUTPUT: Was the capture attempt successful? * + * * + * WARNINGS: Capturing could fail if the house is already owned by the one specified or * + * the building isn't allowed to be captured. * + * * + * HISTORY: * + * 05/03/1995 JLB : Created. * + * 07/05/1995 JLB : Fixed production problem with capturing enemy buildings. * + *=============================================================================================*/ +bool BuildingClass::Captured(HouseClass * newowner) +{ + Validate(); + if (Class->IsCaptureable && newowner != House) { + switch (Owner()) { + case HOUSE_GOOD: + Speak(VOX_GDI_CAPTURED); + break; + + case HOUSE_BAD: + Speak(VOX_NOD_CAPTURED); + break; + } + + if (House == PlayerPtr) { + Map.PowerClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); + } + + /* + ** Add this building to the list of buildings captured this game. For internet stats purposes + */ + if (GameToPlay == GAME_INTERNET){ + newowner->CapturedBuildings->Increment_Unit_Total (Class->Type); + } + + House->Adjust_Power(-Power_Output()); + LastStrength = 0; + House->Adjust_Drain(-Class->Drain); + int booty = House->Adjust_Capacity(-Class->Capacity, true); + + /* + ** If there is something loaded, then it gets captured as well. + */ + TechnoClass * tech = Attached_Object(); + if (tech) tech->Captured(newowner); + + /* + ** If something isn't technically attached, but is sitting on this + ** building for another reason (e.g., helicopter on helipad), then it + ** gets captured as well. + */ + tech = Contact_With_Whom(); + if (tech) { + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER && ::Distance(tech->Center_Coord(), Docking_Coord()) < 0x0040) { + tech->Captured(newowner); + } else { + Transmit_Message(RADIO_RUN_AWAY); + Transmit_Message(RADIO_OVER_OUT); + } + } + + /* + ** Decrement the factory counter for the original owner. + */ + switch (Class->ToBuild) { + case RTTI_UNITTYPE: + House->UnitFactories--; + break; + + case RTTI_INFANTRYTYPE: + House->InfantryFactories--; + break; + + case RTTI_BUILDINGTYPE: + House->BuildingFactories--; + break; + + case RTTI_AIRCRAFTTYPE: + House->AircraftFactories--; + break; + + default: + break; + } + +#ifdef NEVER + if (IsOwnedByPlayer && !ScenarioInit) { + Map.Recalc(); + } + if (!House->IsHuman) { + Update_Specials(); + } +#endif + + /* + ** Flag that both owners now need to update their buildable lists. + */ + House->IsRecalcNeeded = true; + newowner->IsRecalcNeeded = true; + + IsCaptured = true; + TechnoClass::Captured(newowner); + + SmudgeType bib; + CELL cell = Coord_Cell(Coord); + if (Class->Bib_And_Offset(bib, cell)) { + SmudgeClass * smudge = new SmudgeClass(bib); + if (smudge) { + smudge->Disown(cell); + delete smudge; + } + new SmudgeClass(bib, Cell_Coord(cell), House->Class->House); + } + + House->Harvested(booty); + + /* + ** Increment the factory count for the new owner. + */ + switch (Class->ToBuild) { + case RTTI_UNITTYPE: + House->UnitFactories++; + break; + + case RTTI_INFANTRYTYPE: + House->InfantryFactories++; + break; + + case RTTI_BUILDINGTYPE: + House->BuildingFactories++; + break; + + case RTTI_AIRCRAFTTYPE: + House->AircraftFactories++; + break; + + default: + break; + } + + IsRepairing = false; + Grand_Opening(true); + + Mark(MARK_CHANGE); + + /* + ** Perform a look operation when catpured if it was the player + ** that performed the capture. + */ + if (House == PlayerPtr) { + Look(false); + } + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingClass::Sort_Y -- Returns the building coordinate used for sorting. * + * * + * The coordinate value returned from this function should be used for sorting purposes. * + * It has special offset adjustment applied so that vehicles don't overlap (as much). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a coordinate value suitable to be used for sorting. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + * 06/19/1995 JLB : Handles buildings that come with bibs built-in. * + *=============================================================================================*/ +COORDINATE BuildingClass::Sort_Y(void) const +{ + Validate(); + if (*this == STRUCT_REPAIR) { + return(Coord); + } + if (*this == STRUCT_BARRACKS /*|| *this == STRUCT_POWER*/) { + return(Center_Coord()); + } + if (*this == STRUCT_REFINERY) { + return(Center_Coord()); + } + return(Coord_Add(Center_Coord(), XY_Coord(0, (Class->Height()*256)/3))); +} + + +/*********************************************************************************************** + * BuildingClass::Can_Enter_Cell -- Determines if building can be placed down. * + * * + * This routine will determine if the building can be placed down at the location * + * specified. * + * * + * INPUT: cell -- The cell to examine. This is usually the cell of the upper left corner * + * of the building if it were to be placed down. * + * * + * OUTPUT: Returns with the move legality value for placement at the location specified. This * + * will either be MOVE_OK or MOVE_NO. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +MoveType BuildingClass::Can_Enter_Cell(CELL cell, FacingType) const +{ + Validate(); + return(Class->Legal_Placement(cell) ? MOVE_OK : MOVE_NO); +} + + +/*********************************************************************************************** + * BuildingClass::Can_Demolish -- Can the player demolish (sell back) the building? * + * * + * Determines if the player can sell this building. Selling is possible if the building * + * is not currently in construction or deconstruction animation. * + * * + * INPUT: none * + * * + * OUTPUT: Can the building be demolished at this time? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 07/01/1995 JLB : If there is no buildup data, then the building can't be sold. * + * 07/17/1995 JLB : Cannot sell a refinery that has a harvester attached. * + *=============================================================================================*/ +bool BuildingClass::Can_Demolish(void) const +{ + Validate(); + if (Class->Get_Buildup_Data() && BState != BSTATE_CONSTRUCTION && !Mission != MISSION_DECONSTRUCTION && Mission != MISSION_CONSTRUCTION) { + if (*this == STRUCT_REFINERY && Is_Something_Attached()) return(false); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Guard -- Handles guard mission for combat buildings. * + * * + * Buildings that can attack are given this mission. They will wait until a suitable target * + * comes within range and then launch into the attack mission. Buildings that have no * + * weaponry will just sit in this routine forever. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before this routine will be called * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Mission_Guard(void) +{ + Validate(); + /* + ** If this building has a weapon, then search for a target to attack. When + ** a target is found, switch into attack mode to deal with the threat. + */ + if (Class->Primary != WEAPON_NONE) { + + /* + ** Weapon equipped buildings are ALWAYS ready to launch into another mission if + ** they are sitting around in guard mode. + */ + IsReadyToCommence = true; + + /* + ** If there is no target available, then search for one. + */ + if (!Target_Legal(TarCom)) { + ThreatType threat = THREAT_NORMAL; + Assign_Target(Greatest_Threat(threat)); + } + + /* + ** There is a valid target. Switch into attack mode right away. + */ + if (Target_Legal(TarCom)) { + Assign_Mission(MISSION_ATTACK); + return(1); + } + } else { + + /* + ** This is the very simple state machine that basically does + ** nothing. This is the mode that non weapon equipped buildings + ** are normally in. + */ + enum { + INITIAL_ENTRY, + IDLE + }; + switch (Status) { + case INITIAL_ENTRY: + Begin_Mode(BSTATE_IDLE); + Status = IDLE; + break; + + case IDLE: + /* + ** Special case to break out of guard mode if this is a repair + ** facility and there is a customer waiting at the grease pit. + */ + if (*this == STRUCT_REPAIR && + In_Radio_Contact() && + Contact_With_Whom()->Is_Techno() && + ((TechnoClass *)Contact_With_Whom())->Mission == MISSION_ENTER && + Distance(Contact_With_Whom()) < 0x0040 && + Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + + Assign_Mission(MISSION_REPAIR); + return(1); + } + break; + } + return(TICKS_PER_SECOND*5); + } + return(TICKS_PER_SECOND/2); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Construction -- Handles mission construction. * + * * + * This routine will handle mission construction. When this mission is complete, the * + * building will begin normal operation. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Mission_Construction(void) +{ + Validate(); + enum { + INITIAL, + DURING + }; + switch (Status) { + case INITIAL: + Begin_Mode(BSTATE_CONSTRUCTION); + Transmit_Message(RADIO_BUILDING); + if (IsOwnedByPlayer) { + Sound_Effect(VOC_CONSTRUCTION, Coord); + } + Status = DURING; + break; + + case DURING: + if (IsReadyToCommence) { + + /* + ** When construction is complete, then transmit this + ** to the construction yard so that it can stop its + ** construction animation. + */ + Transmit_Message(RADIO_COMPLETE); // "I'm finished." + Transmit_Message(RADIO_OVER_OUT); // "You're free." + Begin_Mode(BSTATE_IDLE); + Grand_Opening(); + Assign_Mission(MISSION_GUARD); + PrimaryFacing = Class->StartFace; + } + break; + } + return(1); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Deconstruction -- Handles building deconstruction. * + * * + * This state machine is only used when the building is deconstructing as a result of * + * selling. When this mission is finished, the building will no longer exist. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 08/13/1995 JLB : Enable selling of units on a repair bay. * + * 08/20/1995 JLB : Scatters infantry from scattered starting points. * + *=============================================================================================*/ +int BuildingClass::Mission_Deconstruction(void) +{ + Validate(); + /* + ** Always force repair off. + */ + Repair(0); + + enum { + INITIAL, + HOLDING, + DURING + }; + switch (Status) { + case INITIAL: + + /* + ** Special check for the repair bay which has the ability to sell + ** whatever is on it. If there is something on the repair bay, then + ** it will be sold. If there is nothing on the repair bay, then + ** the repair bay itself will be sold. + */ + if (*this == STRUCT_REPAIR && Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER && Distance(Contact_With_Whom()) < 0x0080) { + TechnoClass * tech = Contact_With_Whom(); + Transmit_Message(RADIO_OVER_OUT); + tech->Sell_Back(1); +// House->Refund_Money(tech->Refund_Amount()); +// tech->Limbo(); + Assign_Mission(MISSION_GUARD); + return(1); + } + + IsReadyToCommence = false; + Transmit_Message(RADIO_RUN_AWAY); + Status = HOLDING; + break; + + case HOLDING: + if (!IsTethered) { + + /* + ** The crew will evacuate from the building. The number of crew + ** members leaving is equal to the unrecovered cost of the building + ** divided by 100 (the typical cost of a minigunner infantryman). + */ + if (!Special.IsMCVDeploy || *this != STRUCT_CONST) { + int divisor = 200; + if (IsCaptured) divisor *= 2; + int count = (Class->Raw_Cost()+(divisor-1)) / divisor; + bool engine = false; + count = Bound(count, 1, 5); + + while (count) { + + /* + ** Ensure that the player only gets ONE engineer and not from a captured + ** construction yard. + */ + InfantryType typ = Crew_Type(); + while (typ == INFANTRY_E7 && engine) { + typ = Crew_Type(); + } + if (typ == INFANTRY_E7) engine = true; + + InfantryClass * infantry = new InfantryClass(typ, House->Class->House); + if (infantry) { + ScenarioInit++; + COORDINATE coord = Coord_Add(Center_Coord(), XYP_COORD(0, -12)); + coord = Map[Coord_Cell(coord)].Closest_Free_Spot(coord, false); + + if (infantry->Unlimbo(coord, DIR_N)) { + if (infantry->Class->IsNominal) infantry->IsTechnician = true; + ScenarioInit--; + infantry->Scatter(0, true); + ScenarioInit++; + infantry->Assign_Mission(MISSION_GUARD_AREA); + } else { + delete infantry; + } + ScenarioInit--; + } + count--; + } + } + + if (IsOwnedByPlayer) { + Sound_Effect(VOC_CASHTURN, Coord); + } + Status = DURING; + Begin_Mode(BSTATE_CONSTRUCTION); + Detach_All(true); + Transmit_Message(RADIO_OVER_OUT); + IsReadyToCommence = false; + break; + } + Transmit_Message(RADIO_RUN_AWAY); + break; + + case DURING: + if (IsReadyToCommence) { + + /* + ** Construction yards that deconstruct, really just revert back + ** to an MCV. + */ + if (Special.IsMCVDeploy && *this == STRUCT_CONST && House->IsHuman) { + ScenarioInit++; + UnitClass * unit = new UnitClass(UNIT_MCV, House->Class->House); + ScenarioInit--; + if (unit) { + + /* + ** Unlimbo the MCV onto the map. The MCV should start in the same + ** health condition that the construction yard was in. + */ + int ratio = Health_Ratio(); + int money = Refund_Amount(); + + delete this; + + if (unit->Unlimbo(Coord_Snap(Adjacent_Cell(Coord, DIR_SE)), DIR_SW)) { + unit->Strength = Fixed_To_Cardinal(unit->Class_Of().MaxStrength, ratio); + } else { + + /* + ** If, for some strange reason, the MCV could not be placed on the + ** map, then give the player some money to compensate. + */ + House->Refund_Money(money); + } + } else { + House->Refund_Money(Refund_Amount()); + delete this; + } + + } else { + + /* + ** A sold building still counts as a kill, but it just isn't directly + ** attributed to the enemy. + */ + WhoLastHurtMe = HOUSE_NONE; + Record_The_Kill(NULL); + + /* + ** The player gets part of the money back for the sell. + */ + House->Refund_Money(Refund_Amount()); + Limbo(); + + /* + ** Finally, delete the building from the game. + */ + delete this; + } + House->IsRecalcNeeded = true; + } + break; + } + return(1); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Attack -- Handles attack mission for building. * + * * + * Buildings that can attack are processed by this attack mission state machine. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Mission_Attack(void) +{ + Validate(); + if (*this == STRUCT_SAM) { + switch (Status) { + + /* + ** The launcher is underground and awaiting the acquisition of + ** a target. + */ + case SAM_UNDERGROUND: + IsReadyToCommence = true; + if (Target_Legal(TarCom)) { + Set_Rate(2); + Set_Stage(0); + Status = SAM_RISING; + return(1); + } else { + Assign_Mission(MISSION_GUARD); + } + break; + + /* + ** The launcher is rising into the ready position so that it + ** may rotate to face the target. + */ + case SAM_RISING: + if (Fetch_Stage() == 15) { + Set_Rate(0); + PrimaryFacing = DIR_N; + if (!Target_Legal(TarCom)) { + Status = SAM_LOWERING; + } else { + Status = SAM_READY; + } + } + return(1); + + /* + ** This is the target tracking state of the launcher. It will rotate + ** to face the current TarCom of the launcher. + */ + case SAM_READY: + if (!Target_Legal(TarCom) || !Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->Altitude == 0) { + Assign_Target(TARGET_NONE); + Status = SAM_LOCKING; + return(TICKS_PER_SECOND); + } else { + if (!PrimaryFacing.Is_Rotating()) { + DirType facing = Direction(TarCom); + if (PrimaryFacing.Difference(facing)) { + PrimaryFacing.Set_Desired(facing); + } else { + Status = SAM_FIRING; + } + } + } + return(1); + + /* + ** The launcher is in the process of firing. + */ + case SAM_FIRING: + if (!Target_Legal(TarCom) || !Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->Altitude == 0) { + Assign_Target(TARGET_NONE); + Status = SAM_LOCKING; + } else { + FireErrorType error = Can_Fire(TarCom, 0); + if (error == FIRE_ILLEGAL || error == FIRE_CANT || error == FIRE_RANGE) { + Assign_Target(TARGET_NONE); + Status = SAM_LOCKING; + } else { + if (error == FIRE_FACING) { + Status = SAM_READY; + } else { + if (error == FIRE_OK) { + Fire_At(TarCom, 0); + Status = SAM_READY2; + return(1); + } + } + } + } + return(1); + + case SAM_READY2: + if (!Target_Legal(TarCom) || !Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->Altitude == 0) { + Assign_Target(TARGET_NONE); + Status = SAM_LOCKING; + return(TICKS_PER_SECOND); + } else { + if (!PrimaryFacing.Is_Rotating()) { + DirType facing = Direction(TarCom); + if (PrimaryFacing.Difference(facing)) { + PrimaryFacing.Set_Desired(facing); + } else { + Status = SAM_FIRING2; + } + } + } + return(1); + + case SAM_FIRING2: + if (!Target_Legal(TarCom) || !Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->Altitude == 0) { + Assign_Target(TARGET_NONE); + Status = SAM_LOCKING; + } else { + FireErrorType error = Can_Fire(TarCom, 0); + if (error == FIRE_ILLEGAL || error == FIRE_CANT || error == FIRE_RANGE) { + Assign_Target(TARGET_NONE); + Status = SAM_LOCKING; + } else { + if (error == FIRE_FACING) { + Status = SAM_READY2; + } else { + if (error == FIRE_OK) { + Fire_At(TarCom, 0); + Status = SAM_LOCKING; + return(TICKS_PER_SECOND*3); + } + } + } + } + return(1); + + /* + ** Rotating to face north in preparation for lowering to reload. + */ + case SAM_LOCKING: + if (!PrimaryFacing.Is_Rotating()) { + if (PrimaryFacing == DIR_N) { + Set_Rate(2); + Set_Stage(48); + Status = SAM_LOWERING; + } else { + PrimaryFacing.Set_Desired(DIR_N); + } + } + return(1); + + /* + ** Lowering into the ground in order to reload. + */ + case SAM_LOWERING: + if (Fetch_Stage() >= 63) { + Set_Rate(0); + Set_Stage(0); + Status = SAM_UNDERGROUND; + return(TICKS_PER_SECOND); + } else { + if (Fetch_Rate() == 0) { + Set_Rate(2); + } + } + return(1); + + default: + break; + } + + } else { + IsReadyToCommence = true; + switch (Can_Fire(TarCom, 0)) { + case FIRE_ILLEGAL: + case FIRE_CANT: + case FIRE_RANGE: + case FIRE_AMMO: + Assign_Target(TARGET_NONE); + Assign_Mission(MISSION_GUARD); + Commence(); + break; + + case FIRE_FACING: + PrimaryFacing.Set_Desired(Direction(TarCom)); + return(2); + + case FIRE_REARM: + case FIRE_BUSY: + return(1); + + case FIRE_CLOAKED: + Do_Uncloak(); + break; + + case FIRE_OK: + Fire_At(TarCom, 0); + return(1); + } + } + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Harvest -- Handles refinery unloading harvesters. * + * * + * This state machine handles the refinery when it unloads the harvester. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Mission_Harvest(void) +{ + Validate(); + enum { + INITIAL, // Dock the Tiberium canister. + WAIT_FOR_DOCK, // Waiting for docking to complete. + MIDDLE, // Offload "bails" of tiberium. + WAIT_FOR_UNDOCK, // Waiting for undocking to complete. + EXITING // Cause the harvester to drive away. + }; + switch (Status) { + case INITIAL: + Begin_Mode(BSTATE_ACTIVE); + Status = WAIT_FOR_DOCK; + break; + + case WAIT_FOR_DOCK: + if (IsReadyToCommence) { + IsReadyToCommence = false; + Status = MIDDLE; + Begin_Mode(BSTATE_AUX1); + } + break; + + case MIDDLE: + if (IsReadyToCommence) { + IsReadyToCommence = false; + + /* + ** Force any bib squaters to scatter. + */ + bool old = Special.IsScatter; + Special.IsScatter = true; + Map[Adjacent_Cell(Coord_Cell(Center_Coord()), DIR_SW)].Incoming(0, true); + Special.IsScatter = old; + + FootClass * techno = Attached_Object(); + if (techno) { + int bail = techno->Offload_Tiberium_Bail(); + + if (bail) { + House->Harvested(bail); + if (techno->Tiberium_Load()) { + return(1); + } + } + } + Begin_Mode(BSTATE_AUX2); + Status = WAIT_FOR_UNDOCK; + } + break; + + case WAIT_FOR_UNDOCK: + if (IsReadyToCommence) { + + /* + ** Detach harvester and go back into idle state. + */ + Exit_Object(Detach_Object()); + Assign_Mission(MISSION_GUARD); + } + break; + } + return(1); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Repair -- Handles the repair (active) state for building. * + * * + * This state machine is used when the building is active in some sort of repair or * + * construction mode. The construction yard will animate. The repair facility will repair * + * anything that it docked on it. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 06/25/1995 JLB : Handles repair facility * + * 07/29/1995 JLB : Repair rate is controlled by power rating. * + *=============================================================================================*/ +int BuildingClass::Mission_Repair(void) +{ + Validate(); + if (*this == STRUCT_CONST) { + enum { + INITIAL, + DURING + }; + switch (Status) { + case INITIAL: + Begin_Mode(BSTATE_ACTIVE); + Status = DURING; + break; + + case DURING: + if (!In_Radio_Contact()) { + Assign_Mission(MISSION_GUARD); + } + break; + } + return(1); + } + + if (*this == STRUCT_REPAIR) { + enum { + INITIAL, + IDLE, + DURING + }; + switch (Status) { + case INITIAL: + if (!In_Radio_Contact()) { + Begin_Mode(BSTATE_IDLE); + Assign_Mission(MISSION_GUARD); + return(1); + } + IsReadyToCommence = false; + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER && Distance(Contact_With_Whom()) < 0x0010) { + Status = IDLE; + return(TICKS_PER_SECOND/4); + } + break; + + case IDLE: + if (!In_Radio_Contact()) { + Assign_Mission(MISSION_GUARD); + return(1); + } + + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + if (Contact_With_Whom()->Health_Ratio() < 0x0100 && Transmit_Message(RADIO_REPAIR) == RADIO_ROGER) { + Status = DURING; + Begin_Mode(BSTATE_ACTIVE); + IsReadyToCommence = false; + } else { + if (!House->IsHuman) { + Transmit_Message(RADIO_RUN_AWAY); + } + } +// } else { +// Assign_Mission(MISSION_GUARD); +// return(1); + } + break; + + case DURING: + if (!In_Radio_Contact()) { + Begin_Mode(BSTATE_IDLE); + Status = IDLE; + return(1); + } + if (IsReadyToCommence && Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + IsReadyToCommence = false; + long param = Health_Ratio(); + if (Transmit_Message(RADIO_REPAIR, param) != RADIO_ROGER) { +#ifdef OBSOLETE + if (House->Available_Money() < 10) { + Transmit_Message(RADIO_RUN_AWAY); + } +#endif + Begin_Mode(BSTATE_IDLE); + Status = IDLE; +#ifdef OBSOLETE + } else { + int time = Bound(Fixed_To_Cardinal(TICKS_PER_SECOND, House->Power_Fraction()), 0, TICKS_PER_SECOND); + time = (TICKS_PER_SECOND + (TICKS_PER_SECOND/2)) - time; + return(time); +#endif + } + } + break; + } + return(TICKS_PER_SECOND/2); + } + + if (*this == STRUCT_HELIPAD) { + enum { + INITIAL, + DURING + }; + switch (Status) { + case INITIAL: + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER && Transmit_Message(RADIO_PREPARED) == RADIO_NEGATIVE) { + Begin_Mode(BSTATE_ACTIVE); + Contact_With_Whom()->Assign_Mission(MISSION_SLEEP); + Status = DURING; + return(1); + } + Assign_Mission(MISSION_GUARD); + break; + + case DURING: + if (IsReadyToCommence) { + if (!In_Radio_Contact() || Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_NEGATIVE) { + Assign_Mission(MISSION_GUARD); + return(1); + } + + if (Transmit_Message(RADIO_PREPARED) == RADIO_ROGER) { + Contact_With_Whom()->Assign_Mission(MISSION_GUARD); + Assign_Mission(MISSION_GUARD); + return(1); + } + + if (Transmit_Message(RADIO_RELOAD) != RADIO_ROGER) { + Assign_Mission(MISSION_GUARD); + Contact_With_Whom()->Assign_Mission(MISSION_GUARD); + return(1); + } else { + int time = Bound(Fixed_To_Cardinal(TICKS_PER_SECOND, House->Power_Fraction()), 0, TICKS_PER_SECOND); + time = (TICKS_PER_SECOND*3) - time; + IsReadyToCommence = false; + return(time); + } + } + break; + } + return(3); + } + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Missile -- State machine for nuclear missile launch. * + * * + * This handles the Temple of Nod launching its nuclear missile. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Commented. * + *=============================================================================================*/ +int BuildingClass::Mission_Missile(void) +{ + Validate(); + enum { + INITIAL, + DOOR_OPENING, + LAUNCH_UP, + LAUNCH_DOWN, + DONE_LAUNCH + }; + + if (*this == STRUCT_TEMPLE) { + switch (Status) { + + /* + ** The initial case is responsible for starting the door + ** opening on the building. + */ + case INITIAL: + IsReadyToCommence = false; + Begin_Mode(BSTATE_ACTIVE); + Status = DOOR_OPENING; + return(1); + + /* + ** This polls for the case when the door is actually open and + ** then kicks off the missile smoke. + */ + case DOOR_OPENING: + if (IsReadyToCommence) { + Begin_Mode(BSTATE_IDLE); + new AnimClass(ANIM_ATOM_DOOR, Center_Coord()); + Status = LAUNCH_UP; + return(14); + } + return(1); + + /* + ** Once the smoke has been going for a little while this + ** actually handles launching the missile into the air. + */ + case LAUNCH_UP: + { + BulletClass *bullet = new BulletClass(BULLET_NUKE_UP); + if (bullet) { + COORDINATE launch = Coord_Move(Center_Coord(), (DirType)1, 0x1A0); + bullet->Assign_Target(TARGET_NONE); + bullet->Payback = NULL; + bullet->Strength = 1; + if (!bullet->Unlimbo(launch, DIR_N)) { + delete bullet; + bullet = NULL; + } else { + bullet->PrimaryFacing.Set_Current(DIR_N); + Sound_Effect(VOC_NUKE_FIRE, launch); + if (House == PlayerPtr) { + Speak(VOX_NUKE_LAUNCHED); + } + } + } + + if (bullet) { + Status = LAUNCH_DOWN; + return(8 * TICKS_PER_SECOND); + } + } + return(1); + + /* + ** Once the missile is in the air, this handles waiting for + ** the missile to be off the screen and then launching one down + ** over the target. + */ + case LAUNCH_DOWN: + { + BulletClass *bullet = new BulletClass(BULLET_NUKE_DOWN); + if (bullet) { +// Theme.Queue_Song(THEME_NONE); + COORDINATE start = Cell_Coord(XY_Cell(Cell_X(House->NukeDest), 1)); + bullet->Assign_Target(::As_Target(House->NukeDest)); + bullet->Payback = NULL; + bullet->Strength = 1; + if (!bullet->Unlimbo(start, DIR_S)) { + delete bullet; + } else { + bullet->PrimaryFacing.Set_Current(DIR_S); + } + Speak(VOX_INCOMING_NUKE); + Sound_Effect(VOC_NUKE_FIRE, start); + } + if (bullet) { + Status = DONE_LAUNCH; + return(7 * TICKS_PER_SECOND); + } + } + return(1); + + /* + ** Once the missile is done launching this handles allowing + ** the building to sit there with its door open. + */ + case DONE_LAUNCH: + Assign_Mission(MISSION_GUARD); + return(60); + } + } + return(60); +} + + +/*********************************************************************************************** + * BuildingClass::Revealed -- Reveals the building to the specified house. * + * * + * This routine will reveal the building to the specified house. It will handle updating * + * the sidebar for player owned buildings. A player owned building that hasn't been * + * revealed, is in a state of pseudo-limbo. It cannot be used for any of its special * + * abilities even though it exists on the map for all other purposes. * + * * + * INPUT: house -- The house that this building is being revealed to. * + * * + * OUTPUT: Was this building revealed by this procedure? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +bool BuildingClass::Revealed(HouseClass * house) +{ + Validate(); + if (TechnoClass::Revealed(house)) { + + if (!ScenarioInit) { + House->JustBuilt = Class->Type; + } + House->IsRecalcNeeded = true; + + /* + ** Perform any grand opening here so that in the scenarios where a player + ** owned house is not yet revealed, it won't be reflected in the sidebar + ** selection icons. + */ + if (!In_Radio_Contact() && (house == House || GameToPlay != GAME_NORMAL) && Mission != MISSION_CONSTRUCTION) { + Grand_Opening(); + } + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingClass::Enter_Idle_Mode -- The building will enter its idle mode. * + * * + * This routine is called when the exact mode of the building isn't known. By examining * + * the building's condition, this routine will assign an appropriate mission. * + * * + * INPUT: initial -- This this being called during scenario init? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Enter_Idle_Mode(bool initial) +{ + Validate(); + /* + ** Assign an appropriate mission for the building. If the ScenarioInit flag is true, then + ** this must be an initial building. Start such buildings in idle state. For other buildings + ** it indicates that it is being placed during game play and thus it must start in + ** the "construction" mission. + */ + MissionType mission = MISSION_GUARD; + if (!initial || ScenarioInit || Debug_Map) { + Begin_Mode(BSTATE_IDLE); + mission = MISSION_GUARD; + } else { + Begin_Mode(BSTATE_CONSTRUCTION); + mission = MISSION_CONSTRUCTION; + } + Assign_Mission(mission); +} + + +/*************************************************************************** + * BuildingClass::Update_Specials -- removes computer specials * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1995 PWG : Created. * + *=========================================================================*/ +void BuildingClass::Update_Specials(void) +{ + Validate(); +} + + +/*********************************************************************************************** + * BuildingClass::Pip_Count -- Determines "full" pips to display for building. * + * * + * This routine will determine the number of pips that should be filled in when rendering * + * the building. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of pips to display as filled in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/28/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Pip_Count(void) const +{ + Validate(); + return(Fixed_To_Cardinal(Class->Max_Pips(), House->Tiberium_Fraction())); +} + + +/*********************************************************************************************** + * BuildingClass::Death_Announcement -- Announce the death of this building. * + * * + * This routine is called when the building is destroyed by "unnatural" means. Typically * + * as a result of combat. If the building is known to the player, then it should be * + * announced. * + * * + * INPUT: source -- The object most directly responsible for the building's death. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Death_Announcement(TechnoClass const * ) const +{ + Validate(); + if (IsDiscoveredByPlayer || IsOwnedByPlayer) { + if (House != PlayerPtr && GameToPlay != GAME_NORMAL) { + if (Options.IsDeathAnnounce) Speak(VOX_ENEMY_STRUCTURE); + } else { + if (House == PlayerPtr || Options.IsDeathAnnounce) { + if (!Options.IsDeathAnnounce) { + Speak(VOX_STRUCTURE_LOST); + } else { + switch (House->ActLike) { + case HOUSE_GOOD: + Speak(VOX_GDI_STRUCTURE); + break; + + case HOUSE_BAD: + Speak(VOX_NOD_STRUCTURE); + break; + + default: + break; + } + } + } + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Fire_Direction -- Fetches the direction of firing. * + * * + * This routine will return with the default direction to use when firing from this * + * building. This is the facing of the turret except for the case of non-turret equipped * + * buildings that have a weapon (e.g., guard tower). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the default firing direction for this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +DirType BuildingClass::Fire_Direction(void) const +{ + Validate(); + if (Class->IsTurretEquipped) { + return(PrimaryFacing.Current()); + } + return(Direction(TarCom)); +} + + +/*********************************************************************************************** + * BuildingClass::Remap_Table -- Fetches the remap table to use for this building. * + * * + * Use this routine to fetch the remap table to use. This override function is needed * + * because the default remap table for techno objects presumes the object is a unit. * + * Buildings aren't units. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the proper remap table to use for this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void const * BuildingClass::Remap_Table(void) +{ + Validate(); + return(House->Remap_Table(IsBlushing, false)); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Unload -- Handles the unload mission for a building. * + * * + * This is the unload mission for a building. This really only applies to the weapon's * + * factory, since it needs the sophistication of an unload mission due to the door * + * animation. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Mission_Unload(void) +{ + Validate(); + if (*this == STRUCT_WEAP) { + enum { + INITIAL, + OPEN, + LEAVE, + CLOSE + }; + UnitClass * unit; + switch (Status) { + case INITIAL: + unit = (UnitClass *)Contact_With_Whom(); + if (unit) { + unit->Assign_Mission(MISSION_GUARD); + unit->Commence(); + } + Open_Door(2, 11); + Status = OPEN; + break; + + case OPEN: + if (Is_Door_Open()) { + unit = (UnitClass *)Contact_With_Whom(); + if (unit) { + unit->Assign_Mission(MISSION_MOVE); + unit->Force_Track(DriveClass::OUT_OF_WEAPON_FACTORY, Adjacent_Cell(Center_Coord(), FACING_SW)); + unit->Set_Speed(128); + Status = LEAVE; + } else { + Close_Door(2, 11); + Status = CLOSE; + } + } + break; + + case LEAVE: + if (!IsTethered) { + Close_Door(2, 11); + Status = CLOSE; + } + break; + + case CLOSE: + if (Is_Door_Closed()) { + Enter_Idle_Mode(); + } + break; + } + return(TICKS_PER_SECOND/2); + } + Assign_Mission(MISSION_GUARD); + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * BuildingClass::Power_Output -- Fetches the current power output from this building. * + * * + * This routine will return the current power output for this building. The power output * + * is adjusted according to the damage level of the building. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the current power output for this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Power_Output(void) const +{ + Validate(); + if (Class->Power) { + return(Fixed_To_Cardinal(Class->Power, Cardinal_To_Fixed(Class->MaxStrength, LastStrength))); + } + return(0); +} + + +/*********************************************************************************************** + * BuildingClass::Detach -- Handles target removal from the game system. * + * * + * This routine is called when the specified target is about to be removed from the game * + * system. * + * * + * INPUT: target -- The target to be removed from this building's targeting computer. * + * * + * all -- Is the target about to be completely eliminated? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Detach(TARGET target, bool all) +{ + Validate(); + TechnoClass::Detach(target, all); + if (target == WhomToRepay) { + WhomToRepay = TARGET_NONE; + } +} + + +/*********************************************************************************************** + * BuildingClass::Refund_Amount -- Fetches the refund amount if building is sold. * + * * + * This routine will return the amount of money to be refunded to the building's owner * + * if the building is sold. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the refund amount available for this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Refund_Amount(void) const +{ + Validate(); + int cost = TechnoClass::Refund_Amount(); + + /* + ** Add in any Tiberium that was stored within the building. + */ + if (IsV107 && Class->Capacity > 0) { + cost += Fixed_To_Cardinal(Class->Capacity, Cardinal_To_Fixed(House->Capacity, House->Tiberium)); + } + return(cost); +} + + +/*********************************************************************************************** + * BuildingClass::Crew_Type -- This determines the crew that this object generates. * + * * + * When selling very cheap buildings (such as the silo), a technician will pop out since * + * generating minigunners would be overkill -- the player could use this loophole to * + * gain an advantage. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the infantry type that this building will generate as a survivor. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1995 JLB : Created. * + *=============================================================================================*/ +InfantryType BuildingClass::Crew_Type(void) const +{ + Validate(); + switch (Class->Type) { + case STRUCT_STORAGE: + if (Random_Pick(0, 1) == 0) { + return(INFANTRY_C1); + } else { + return(INFANTRY_C7); + } + + case STRUCT_CONST: + if (!IsCaptured && House->IsHuman && Random_Pick(0, 3) == 0) { + return(INFANTRY_E7); + } + break; + + default: + break; + } + return(TechnoClass::Crew_Type()); +} + + +/*********************************************************************************************** + * BuildingClass::Detach_All -- Possibly abandons production according to factory type. * + * * + * When this routine is called, it indicates that the building is about to be destroyed * + * or captured. In such a case any production it may be doing, must be abandoned. * + * * + * INPUT: all -- Is the object about the be completely destroyed? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Detach_All(bool all) +{ + Validate(); + /* + ** If it is producing something, then it must be abandoned. + */ + if (Factory) { + Factory->Abandon(); + delete Factory; + Factory = 0; + } + + /* + ** If the owner HouseClass is building something, and this building can + ** build that thing, we may be the last building for that house that can + ** build that thing; if so, abandon production of it. + */ + if (House) { + int fnum = -1; + + switch (Class->ToBuild) { + case RTTI_AIRCRAFTTYPE: + fnum = House->AircraftFactory; + break; + + case RTTI_INFANTRYTYPE: + fnum = House->InfantryFactory; + break; + + case RTTI_UNITTYPE: + fnum = House->UnitFactory; + break; + + case RTTI_BUILDINGTYPE: + fnum = House->BuildingFactory; + break; + + case RTTI_SPECIAL: + fnum = House->SpecialFactory; + break; + + } + + /* + ** Convert the factory number into a real factory pointer. + */ + FactoryClass * factory = 0; + if (fnum != -1) { + factory = Factories.Raw_Ptr(fnum); + } + + /* + ** If a factory was found, then temporarily disable this building and then + ** detmermine if any object that is being produced can still be produced. If + ** not, then the object being produced must be abandoned. + */ + if (factory) { + TechnoClass * object = factory->Get_Object(); + IsInLimbo = true; + if (object && !object->Techno_Type_Class()->Who_Can_Build_Me(true, false, House->Class->House)) { + House->Abandon_Production(Class->ToBuild); + } + IsInLimbo = false; + } + } + + TechnoClass::Detach_All(all); +} + + +/*********************************************************************************************** + * BuildingClass::Flush_For_Placement -- Handles clearing a zone for object placement. * + * * + * This routine is used to clear the way for placement of the specified object (usually * + * a building). If there are friendly units blocking the placement area, they are told * + * to scatter. Enemy blocking units are attacked. * + * * + * INPUT: techno -- Pointer to the object that is desired to be placed. * + * * + * cell -- The cell that placement wants to occur at. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1995 JLB : Created. * + *=============================================================================================*/ +bool BuildingClass::Flush_For_Placement(TechnoClass * techno, CELL cell) +{ + Validate(); + bool again = false; + if (techno && cell > 0) { + short const * list = techno->Class_Of().Occupy_List(true); + + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + + if (Map.In_Radar(newcell)) { + TechnoClass * occupier = Map[newcell].Cell_Techno(); + if (occupier) { + again = true; + if (occupier->House->Is_Ally(this)) { + Map[newcell].Incoming(0, true); + } else { + Base_Is_Attacked(occupier); + } + } + } + } + } + return(again); +} + + +void BuildingClass::Hidden(void) +{ +// if (IsDiscoveredByPlayer && House->IsHuman) { +// House->Adjust_Drain(-Class->Drain); +// } + TechnoClass::Hidden(); +} + + +CELL BuildingClass::Find_Exit_Cell(TechnoClass const * techno) const +{ + CELL const *ptr; + CELL origin = Coord_Cell(Coord); + bool found = false; + + ptr = Class->ExitList; + if (ptr) { + while (*ptr != REFRESH_EOL) { + CELL cell = origin + *ptr++; + if (Map.In_Radar(cell) && techno->Can_Enter_Cell(cell) == MOVE_OK) { + return(cell); + } + } + } + return(0); +} + +/*********************************************************************************************** + * BuildingClass::Passes_Proximity_Check -- Determines if building placement is near friendly sq* + * * + * This routine is used by the building placement cursor logic to determine whether the * + * at the current cursor position if the building would be adjacent to another friendly * + * building. In cases where this is not true, then the building cannot be placed at all. * + * This determination is returned by the function. * + * * + * INPUT: homecell -- The cell that the building would like to be placed down at. * + * * + * OUTPUT: bool; Can the pending building object be placed at the present cursor location * + * checking only for proximity to friendly buildings? If this isn't for a * + * building type object, then this routine always returns true. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/06/1994 JLB : Created. * + * 06/07/1994 JLB : Handles concrete check. * + *=============================================================================================*/ +bool BuildingClass::Passes_Proximity_Check(CELL homecell) +{ + /* + ** In editor mode, the proximity check always passes. + */ + if (Debug_Map || !House->IsHuman) { + return(true); + } + + /* + ** Scan through all cells that the building foundation would cover. If any adjacent + ** cells to these are of friendly persuasion, then consider the proximity check to + ** have been a success. + */ + short const * ptr = Occupy_List(true); + while (*ptr != REFRESH_EOL) { + CELL cell = homecell + *ptr++; + + if (!Map.In_Radar(cell)) return(false); + + for (FacingType facing = FACING_N; facing < FACING_COUNT; facing++) { + CELL newcell = Adjacent_Cell(cell, facing); + + TechnoClass * base = Map[newcell].Cell_Techno(); + + /* + ** The special cell ownership flag allows building adjacent + ** to friendly walls and bibs even though there is no official + ** building located there. + */ + if (Map[newcell].Owner == House->Class->House) { + return(true); + } + + if (base && base->What_Am_I() == RTTI_BUILDING && base->House->Class->House == House->Class->House) { + return(true); + } + } + } + return(false); +} diff --git a/BUILDING.H b/BUILDING.H new file mode 100644 index 0000000..bed3b6c --- /dev/null +++ b/BUILDING.H @@ -0,0 +1,304 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\building.h_v 2.20 16 Oct 1995 16:47:54 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BUILDING.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BUILDING_H +#define BUILDING_H + +#include "tarcom.h" +#include "radio.h" +#include "cargo.h" +#include "mission.h" +#include "bullet.h" +#include "target.h" +#include "factory.h" + +#define MAX_DOOR_STAGE 18 // # of frames of door opening on weapons factory +#define DOOR_OPEN_STAGE 9 // frame on which the door is entirely open +#define MAX_REPAIR_ANIM_STAGE 5 // # of stages of anim for repair center cycling + +/**************************************************************************** +** For each instance of a building in the game, there is one of +** these structures. This structure holds information that is specific +** and dynamic for a particular building. +*/ +class BuildingClass : public TechnoClass +{ + public: + BuildingTypeClass const * const Class; + operator StructType(void) const {return Class->Type;}; + + /* + ** If this building is in the process of producing something, then this + ** will point to the factory manager. + */ + FactoryClass * Factory; + + /* + ** This is the house that originally owned this factory. Objects buildable + ** by this house type will be produced from this factory regardless of who + ** the current owner is. + */ + HousesType ActLike; + + /* + ** If the building is at a good point to change orders, then this + ** flag will be set to true. + */ + unsigned IsReadyToCommence:1; + + /* + ** If this building is currently spending money to repair itself, then + ** this flag is true. It will automatically be set to false when the building + ** has reached full strength, when money is exhausted, or if the player + ** specifically stops the repair process. + */ + unsigned IsRepairing:1; + + /* + ** If repair is currently in progress and this flag is true, then a wrench graphic + ** will be overlaid on the building to give visual feedback for the repair process. + */ + unsigned IsWrenchVisible:1; + + /* + ** This flag is set when a commando has raided the building and planted + ** plastic explosives. When the CommandoCountDown timer expires, the + ** building takes massive damage. + */ + unsigned IsGoingToBlow:1; + + /* + ** If this building was destroyed by some method that would prevent + ** survivors, then this flag will be true. + */ + unsigned IsSurvivorless:1; + + /* + ** These state control variables are used by the oblisk for the charging + ** animation. + */ + unsigned IsCharging:1; + unsigned IsCharged:1; + + /* + ** A building that has been captured will not contain the full compliment + ** of crew. This is true even if it subsiquently gets captured back. + */ + unsigned IsCaptured:1; + + /* + ** Special countdown to destruction value. If the building is destroyed, + ** it won't actually be removed from the map until this value reaches + ** zero. This delay is for cosmetic reasons. + */ + TCountDownTimerClass CountDown; + + /* + ** This is the current animation processing state that the building is + ** in. + */ + BStateType BState; + BStateType QueueBState; + + /* + ** For multiplayer games, this keeps track of the last house to damage + ** this building, so if it burns to death or otherwise gradually dies, + ** proper credit can be given for the kill. + */ + HousesType WhoLastHurtMe; + + /* + ** This is the saboteur responsible for this building's destruction. + */ + TARGET WhomToRepay; + + /* + ** This is a record of the last strength of the building. Every so often, + ** it will compare this strength to the current strength. If there is a + ** discrepency, then the owner power is adjusted accordingly. + */ + int LastStrength; + + /* + ** This is the countdown timer that regulates placement retry logic + ** for factory type buildings. + */ + TCountDownTimerClass PlacementDelay; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + static void * BuildingClass::operator new(size_t size); + static void BuildingClass::operator delete(void *ptr); + BuildingClass(void) : Class(0) {}; + BuildingClass(StructType type, HousesType house); + virtual ~BuildingClass(void); + virtual RTTIType What_Am_I(void) const {return RTTI_BUILDING;}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + static void Init(void); + + TARGET Target_Scan(void); + BuildingTypeClass::AnimControlType const * Fetch_Anim_Control(void) {return (&Class->Anims[BState]);}; + + /* + ** Query functions. + */ + virtual CELL Find_Exit_Cell(TechnoClass const * techno) const; + virtual InfantryType Crew_Type(void) const; + virtual int Pip_Count(void) const; + virtual bool Can_Player_Move(void) const {return(false);}; + virtual ActionType What_Action(ObjectClass * target) const; + virtual ActionType What_Action(CELL cell) const; + virtual bool Can_Demolish(void) const; + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual int Refund_Amount(void) const; + virtual DirType Fire_Direction(void) const; + int Power_Output(void) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Docking_Coord(void) const; + virtual COORDINATE Fire_Coord(int which) const; + virtual COORDINATE Center_Coord(void) const; + virtual COORDINATE Sort_Y(void) const; + virtual COORDINATE Target_Coord(void) const {return Center_Coord();}; + + /* + ** Object entry and exit from the game system. + */ + virtual void Detach(TARGET target, bool all); + virtual void Detach_All(bool all=true); + virtual void Grand_Opening(bool captured = false); + virtual void Update_Buildables(void); + virtual MoveType Can_Enter_Cell(CELL cell, FacingType = FACING_NONE) const; + virtual bool Unlimbo(COORDINATE , DirType dir = DIR_N); + virtual bool Limbo(void); + bool Passes_Proximity_Check(CELL homecell); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void const * Remap_Table(void); + virtual int Exit_Object(TechnoClass * base); + virtual void Draw_It(int x, int y, WindowNumberType window); + virtual bool Mark(MarkType mark); + virtual void Look(bool incremental=false); + virtual void Fire_Out(void); + void Begin_Mode(BStateType bstate); + + /* + ** User I/O. + */ + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual void Active_Click_With(ActionType action, CELL cell); + + /* + ** Combat related. + */ + virtual void Death_Announcement(TechnoClass const * source=0) const; + virtual FireErrorType Can_Fire(TARGET, int which) const; + virtual TARGET Greatest_Threat(ThreatType threat) const; + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0); + virtual TARGET As_Target(void) const; + virtual bool Captured(HouseClass * newowner); + + /* + ** AI. + */ + virtual void Hidden(void); + virtual bool Revealed(HouseClass * house); + virtual void Repair(int control); + virtual void Sell_Back(int control); + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual void AI(void); + virtual void Assign_Target(TARGET target); + virtual bool Toggle_Primary(void); + bool Flush_For_Placement(TechnoClass * techno, CELL cell); + + virtual int Mission_Unload(void); + virtual int Mission_Repair(void); + virtual int Mission_Attack(void); + virtual int Mission_Harvest(void); + virtual int Mission_Guard(void); + virtual int Mission_Construction(void); + virtual int Mission_Deconstruction(void); + virtual int Mission_Missile(void); + virtual void Enter_Idle_Mode(bool initial=false); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** File I/O. + */ + static void Read_INI(char *buffer); + static void Write_INI(char *buffer); + static char *INI_Name(void) {return "STRUCTURES";}; + bool Load(FileClass & file); + bool Save(FileClass & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + void Update_Specials(void); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + private: + void Drop_Debris(TARGET source = TARGET_NONE); + virtual BulletClass * Fire_At(TARGET target, int which); + + static COORDINATE const CenterOffset[BSIZE_COUNT]; + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; +}; + +#endif diff --git a/BULLET.CPP b/BULLET.CPP new file mode 100644 index 0000000..0966cb8 --- /dev/null +++ b/BULLET.CPP @@ -0,0 +1,786 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\bullet.cpv 2.18 16 Oct 1995 16:50:00 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BULLET.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : March 18, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BulletClass::AI -- Logic processing for bullet. * + * BulletClass::As_Target -- Converts the bullet into a target value. * + * BulletClass::BulletClass -- Bullet constructor. * + * BulletClass::BulletClass -- Default constructor for bullet objects. * + * BulletClass::Detach -- Removes specified target from this bullet's targeting system. * + * BulletClass::Draw_It -- Displays the bullet at location specified. * + * BulletClass::Init -- Clears the bullets array for scenario preparation. * + * BulletClass::Mark -- Performs related map refreshing under bullet. * + * BulletClass::Occupy_List -- Determines the bullet occupation list. * + * BulletClass::Unlimbo -- Transitions a bullet object into the game render/logic system. * + * BulletClass::delete -- Bullet memory delete. * + * BulletClass::new -- Allocates memory for bullet object. * + * BulletClass::Validate -- validates bullet pointer * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#define GRAVITY 3 + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * BulletClass::VTable; + + +/*********************************************************************************************** + * BulletClass::Validate -- validates bullet pointer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int BulletClass::Validate(void) const +{ + int num; + + num = Bullets.ID(this); + if (num < 0 || num >= BULLET_MAX) { + Validate_Error("BULLET"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * BulletClass::BulletClass -- Default constructor for bullet objects. * + * * + * This is the default constructor for bullet objects. A bullet constructed by this routine * + * is not in a usable state for game purposes. It must be constructed by the full * + * (parameterized) constructor -- usually called as part of the overloaded "new" operator. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Do not use bullets that were constructed solely by this routine. * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +BulletClass::BulletClass(void) : + Class(0) +{ + Payback = NULL; + IsToAnimate = false; + Altitude = 0; + Riser = 0; + TarCom = TARGET_NONE; + Strength = 0; + IsLocked = true; + IsInaccurate = false; +} + + +/*********************************************************************************************** + * BulletClass::new -- Allocates memory for bullet object. * + * * + * This function will "allocate" a block of memory for a bullet object. * + * This memory block is merely lifted from a fixed pool of blocks. * + * * + * INPUT: size -- The size of the memory block needed. * + * * + * OUTPUT: Returns with a pointer to an available bullet object block. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + *=============================================================================================*/ +void * BulletClass::operator new(size_t ) +{ + void * ptr = Bullets.Allocate(); + if (ptr) { + ((BulletClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * BulletClass::delete -- Bullet memory delete. * + * * + * Since bullets memory is merely "allocated" out of a pool, it never * + * actually gets deleted. * + * * + * INPUT: ptr -- Generic pointer to bullet object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + *=============================================================================================*/ +void BulletClass::operator delete(void *ptr) +{ + if (ptr) { + ((BulletClass *)ptr)->IsActive = false; + } + Bullets.Free((BulletClass *)ptr); +} + + +/*********************************************************************************************** + * BulletClass::BulletClass -- Bullet constructor. * + * * + * This is the constructor for the bullet class. It handles all * + * initialization of the bullet and starting it in motion toward its * + * target. * + * * + * INPUT: id -- The type of bullet this is (could be missile). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + * 06/20/1994 JLB : Firer is a base class pointer. * + * 12/10/1994 JLB : Auto calculate range optional. * + * 12/12/1994 JLB : Handles small arms as an instantaneous effect. * + * 12/23/1994 JLB : Fixed scatter algorithm for non-homing projectiles. * + * 12/31/1994 JLB : Removed range parameter (not needed). * + *=============================================================================================*/ +BulletClass::BulletClass(BulletType id) : + Class(&BulletTypeClass::As_Reference(id)) +{ + Altitude = 0; + IsInaccurate = false; + IsLocked = true; +// IsLocked = false; + IsToAnimate = false; + Payback = 0; + Riser = 0; + Strength = Class->MaxStrength; + TarCom = TARGET_NONE; +} + + +/*********************************************************************************************** + * BulletClass::Occupy_List -- Determines the bullet occupation list. * + * * + * This function will determine the cell occupation list and return a pointer to it. Most * + * bullets are small and the list is usually short, but on occasion, it can be a list that * + * rivals the size of regular vehicles. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the cell offset list that covers all the cells a bullet * + * is over. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + * 01/05/1995 JLB : Handles projectiles with altitude. * + *=============================================================================================*/ +short const *BulletClass::Occupy_List(void) const +{ + Validate(); + switch (*this) { + case BULLET_FLAME: + return(Coord_Spillage_List(Coord, 25)); + + case BULLET_NUKE_UP: + case BULLET_NUKE_DOWN: + return(Coord_Spillage_List(Coord, 48)); + + default: + if (Altitude) { + static CELL _list[10]; + const short * ptr = Coord_Spillage_List(Coord, 5); + int index = 0; + CELL cell1 = Coord_Cell(Coord); + + while (ptr[index] != REFRESH_EOL) { + _list[index] = ptr[index]; + index++; + } + + COORDINATE coord = XY_Coord(0, Altitude); + coord = Coord_Sub(Coord, coord); + CELL cell2 = Coord_Cell(coord); + ptr = Coord_Spillage_List(coord, 5); + while (*ptr != REFRESH_EOL) { + _list[index++] = (*ptr++) + (cell2 - cell1); + } + _list[index] = REFRESH_EOL; + return(_list); + } + } + return(Coord_Spillage_List(Coord, 10)); +} + + +/*********************************************************************************************** + * BulletClass::Mark -- Performs related map refreshing under bullet. * + * * + * This routine marks the objects under the bullet so that they will * + * be redrawn. This is necessary as the bullet moves -- objects under * + * its path need to be restored. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + *=============================================================================================*/ +bool BulletClass::Mark(MarkType mark) +{ + Validate(); + if (ObjectClass::Mark(mark)) { + if (!Class->IsInvisible) { + Map.Refresh_Cells(Coord_Cell(Coord), Occupy_List()); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BulletClass::AI -- Logic processing for bullet. * + * * + * This routine will perform all logic (flight) logic on the bullet. * + * Primarily this is motion, fuse tracking, and detonation logic. Call * + * this routine no more than once per bullet per game tick. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + *=============================================================================================*/ +void BulletClass::AI(void) +{ + Validate(); + COORDINATE coord; + + ObjectClass::AI(); + + /* + ** Balistic objects are handled here. + */ + bool forced = false; // Forced explosion. + if (Class->IsArcing) { + Altitude += Riser; + + if (Altitude <= 0) { + forced = true; + } + if (Riser > -100) { + Riser -= GRAVITY; + } + } + if (Class->IsDropping) { + Altitude += Riser; + + if (Altitude <= 0) { + forced = true; + } + if (Riser > -100) { + Riser -= 1; + } + } + + /* + ** Homing projectiles constantly change facing to face toward the target but + ** they only do so every other game frame (improves game speed and makes + ** missiles not so deadly). + */ + if ((Frame & 0x01) && Class->IsHoming && Target_Legal(TarCom)) { + PrimaryFacing.Set_Desired(Direction256(Coord, ::As_Coord(TarCom))); + } + + /* + ** Move the projectile forward according to its speed + ** and direction. + */ + coord = Coord; + if (Class->IsFlameEquipped) { + if (IsToAnimate) { + new AnimClass(ANIM_SMOKE_PUFF, coord, 1); + } + IsToAnimate = !IsToAnimate; + } + + /* + ** Handle any body rotation at this time. This process must + ** occur every game fame in order to achieve smooth rotation. + */ + if (PrimaryFacing.Is_Rotating()) { + PrimaryFacing.Rotation_Adjust(Class->ROT); + } + + switch (Physics(coord, PrimaryFacing)) { + + /* + ** When a projectile reaches the edge of the world, it + ** vanishes from existence -- presumed to explode off + ** map. + */ + case IMPACT_EDGE: +// if (IsLocked) { + Mark(); + delete this; +// } + break; + + default: + case IMPACT_NONE: + + /* + ** The projectile has moved. Check its fuse. If detonation + ** is signaled, then do so. Otherwise, just move. + */ + case IMPACT_NORMAL: + Mark(); +// IsLocked = true; + if (!Class->IsHigh) { + CellClass * cellptr = &Map[Coord_Cell(coord)]; + if (cellptr->Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(cellptr->Overlay).IsHigh) { + forced = true; + Coord = coord = Cell_Coord(Coord_Cell(coord)); + } + } + + /* + ** Bullets are generaly more effective when they are fired at aircraft. + */ + if (Class->IsAntiAircraft && As_Aircraft(TarCom) && Distance(TarCom) < 0x0080) { + forced = true; + + if (*this == BULLET_TOW) { + Strength += Strength/3; + } else { + Strength += Strength/2; + } + } + + if (!forced && (Class->IsDropping || !Fuse_Checkup(coord))) { + Coord = coord; + Mark(); + + /* + ** Certain projectiles loose strength when they travel. + */ + if (*this == BULLET_BULLET) { + if (Strength > 5) Strength--; + } + + } else { + + /* + ** When the target is reached, explode and do the damage + ** required of it. For homing objects, don't force the explosion to + ** match the target position. Non-homing projectiles adjust position so + ** that they hit the target. This compensates for the error in line of + ** flight logic. + */ + Mark(); + if (!forced && !Class->IsArcing && !Class->IsHoming && Fuse_Target()) { + Coord = Fuse_Target(); + } + + /* + ** Non-aircraft targets apply damage to the ground. + */ + if (!Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->In_Which_Layer() == LAYER_GROUND) { + Explosion_Damage(Coord, Strength, Payback, Class->Warhead); + } else { + + /* + ** Special damage apply for SAM missiles. This is the only way that missile + ** damage affects the aircraft target. + */ + if (Distance(TarCom) < 0x0080) { + AircraftClass * object = As_Aircraft(TarCom); + + int str = Strength; + if (object) object->Take_Damage(str, 0, Class->Warhead, Payback); + } + } + + /* + ** For projectiles that are invisible while travelling toward the target, + ** allow scatter effect for the impact animation. + */ + if (Class->IsInvisible) { + Coord = Coord_Scatter(Coord, 0x0020); + } + if (Class->Explosion != ANIM_NONE) { + new AnimClass(Class->Explosion, Coord); + } + delete this; + return; + } + break; + } +} + + +/*********************************************************************************************** + * BulletClass::Draw_It -- Displays the bullet at location specified. * + * * + * This routine displays the bullet visual at the location specified. * + * * + * INPUT: x,y -- The center coordinate to render the bullet at. * + * * + * window -- The window to clip to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + * 06/27/1994 JLB : Takes a window clipping parameter. * + * 01/08/1995 JLB : Handles translucent colors if necessary. * + *=============================================================================================*/ +void BulletClass::Draw_It(int x, int y, WindowNumberType window) +{ + Validate(); + int facing = Facing_To_32(PrimaryFacing); + + /* + ** Certain projectiles aren't visible. This includes small bullets (which are actually + ** invisible) and flame thrower flames (which are rendered as an animation instead of a projectile). + */ + if (Class->IsInvisible) return; + + /* + ** If there is no shape loaded for this object, then + ** it obviously can't be rendered -- just bail. + */ + void const * shapeptr = Class->Get_Image_Data(); + if (!shapeptr) return; + + /* + ** Get the basic shape number for this projectile. + */ + int shapenum = 0; + if (!Class->IsFaceless) { + shapenum = UnitClass::BodyShape[facing]; + } + + /* + ** For tumbling projectiles, fetch offset stage. + */ + if (*this == BULLET_NAPALM) { + shapenum += Frame % 6; + } + + if (*this == BULLET_GRENADE) { + shapenum += Frame % 8; +// Timer++; + } + + /* + ** For flying projectiles, draw the shadow and adjust the actual projectile body + ** render position. + */ + if (Altitude) { + CC_Draw_Shape(shapeptr, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, NULL, Map.FadingShade); + y -= Lepton_To_Pixel(Altitude); + } + + /* + ** Draw the main body of the projectile. + */ + ShapeFlags_Type flags = SHAPE_NORMAL; + if (Class->IsTranslucent) { + flags = SHAPE_GHOST; + } + CC_Draw_Shape(shapeptr, shapenum, x, y, window, flags|SHAPE_CENTER|SHAPE_WIN_REL, NULL, Map.UnitShadow); +} + + +/*********************************************************************************************** + * BulletClass::Init -- Clears the bullets array for scenario preparation. * + * * + * This routine will zero out the bullet tracking list and object array in preparation for * + * the start of a new scenario. All bullets cease to exists after this function is * + * called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/15/1994 JLB : Created. * + *=============================================================================================*/ +void BulletClass::Init(void) +{ + BulletClass *ptr; + + Bullets.Free_All(); + + ptr = new BulletClass(); + VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0]; + delete ptr; +} + + +/*********************************************************************************************** + * BulletClass::Detach -- Removes specified target from this bullet's targeting system. * + * * + * When an object is removed from the game system, it must be removed all targeting and * + * tracking systems as well. This routine is used to remove the specified object from the * + * bullet. If the object isn't part of this bullet's tracking system, then no action is * + * performed. * + * * + * INPUT: target -- The target to remove from this tracking system. * + * * + * all -- Is the target going away for good as opposed to just cloaking/hiding? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void BulletClass::Detach(TARGET target, bool all) +{ + Validate(); + ObjectClass * obj = As_Object(target); + + if (obj == Payback) { + Payback = 0; + } + + if (all && target == TarCom) { + TarCom = TARGET_NONE; + } +} + + +/*********************************************************************************************** + * BulletClass::Unlimbo -- Transitions a bullet object into the game render/logic system. * + * * + * This routine is used to take a bullet object that is in limbo and transition it to the * + * game system. A bullet object so transitioned, will be drawn and logic processing * + * performed. In effect, it comes into existance. * + * * + * INPUT: coord -- The location where the bullet object is to appear. * + * * + * dir -- The initial facing for the bullet object. * + * * + * OUTPUT: bool; Was the unlimbo successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/10/1995 JLB : Created. * + *=============================================================================================*/ +bool BulletClass::Unlimbo(COORDINATE coord, DirType dir) +{ + Validate(); + /* + ** Try to unlimbo the bullet as far as the base class is concerned. Use the already + ** set direction and strength if the "punt" values were passed in. This allows a bullet + ** to be setup prior to being launched. + */ + if (ObjectClass::Unlimbo(coord)) { + COORDINATE tcoord = As_Coord(TarCom); + + /* + ** Homing projectiles (missiles) do NOT override facing. They just fire in the + ** direction specified and let the chips fall where they may. + */ + if (!Class->IsHoming && !Class->IsDropping) { + dir = Direction(tcoord); + } + + /* + ** Possibly adjust the target if this projectile is inaccurate. This occurs whenever + ** certain weapons are trained upon targets they were never designed to attack. Example: when + ** turrets or anti-tank missiles are fired at infantry. Indirect + ** fire is inherently inaccurate. + */ + if (IsInaccurate || Class->IsInaccurate || + ((Is_Target_Cell(TarCom) || Is_Target_Infantry(TarCom)) && (Class->Warhead == WARHEAD_AP || Class->IsFueled))) { + + /* + ** Inaccuracy for low velocity or homing projectiles manifests itself as a standard + ** Cicular Error of Probability (CEP) algorithm. High speed projectiles usually + ** just overshoot the target by extending the straight line flight. + */ + if (Class->IsHoming || Class->IsArcing) { + int scatterdist = ::Distance(coord, tcoord)/3; + scatterdist = MIN(scatterdist, 0x0200); + + if (*this == BULLET_GRENADE) { + scatterdist = ::Distance(coord, tcoord)/4; + scatterdist = MIN(scatterdist, 0x0080); + } + + dir = (DirType)((dir + (Random_Pick(0, 10)-5)) & 0x00FF); + tcoord = Coord_Scatter(tcoord, Random_Pick(0, scatterdist)); + } else { + tcoord = Coord_Move(tcoord, dir, Random_Pick(0, 0x0100)); + } + + /* + ** Limit scatter to the weapon range of the firer. + */ + if (Payback) { + if (!Payback->In_Range(tcoord, 0) && !Payback->In_Range(tcoord, 1)) { + tcoord = Coord_Move(tcoord, ::Direction(tcoord, Coord), Distance(tcoord) - MAX(Payback->Weapon_Range(0), Payback->Weapon_Range(1))); + } + } + } + + /* + ** For very fast and invisible projectiles, just make the projectile exist at the target + ** location and dispense with the actual flight. + */ + if (Class->MaxSpeed == MPH_LIGHT_SPEED && Class->IsInvisible) { + Coord = tcoord; + } + + /* + ** Set the range equal to either the class defined range or the calculated + ** number of game frames it would take for the projectile to reach the target. + */ + int range = 0xFF; + if (!Class->Range) { + if (!Class->IsDropping) { + range = (::Distance(tcoord, Coord) / Class->MaxSpeed) + 4; + } + } else { + range = Class->Range; + } + + /* + ** Projectile speed is usually the default value for that projectile, but + ** certian projectiles alter speed according to the distance to the + ** target. + */ + int speed = Class->MaxSpeed; + if (speed == MPH_LIGHT_SPEED) speed = MPH_IMMOBILE; + if (Class->IsArcing) { + speed = Class->MaxSpeed + (Distance(tcoord)>>5); + + /* + ** Set minimum speed (i.e., distance) for arcing projectiles. + */ + speed = MAX(speed, 25); + } + if (!Class->IsDropping) { + Fly_Speed(255, (MPHType)speed); + } + + /* + ** Arm the fuse. + */ + Arm_Fuse(Coord, tcoord, range, ((As_Aircraft(TarCom)!=0) ? 0 : Class->Arming)); + + /* + ** Projectiles that make a ballistic flight to impact point must determine a + ** vertical component for the projectile launch. This is crudely simulated + ** by biasing ground speed according to target distance and then giving + ** enough vertical velocity to keep the projectile airborne for the + ** desired amount of time. The mathematically correct solution would be to + ** calculate launch angle (given fixed projectile velocity) and then derive + ** the vertical and horizontal components. This solution would require a + ** of square root and an arcsine lookup table. OUCH! + */ + Altitude = 0; + Riser = 0; + if (Class->IsArcing) { + Altitude = 1; + Riser = ((Distance(tcoord)/2) / (speed+1))*GRAVITY; + Riser = MAX(Riser, 10); + } + if (Class->IsDropping) { + Altitude = Pixel_To_Lepton(24); + Riser = 0; + } + + PrimaryFacing = dir; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BulletClass::As_Target -- Converts the bullet into a target value. * + * * + * This support routine is used to convert the bullet (as a pointer) into a target * + * value (which is a number). * + * * + * INPUT: none * + * * + * OUTPUT: Returns the bullet as a target value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +TARGET BulletClass::As_Target(void) const +{ + Validate(); + return(Build_Target(KIND_BULLET, Bullets.ID(this))); +} + diff --git a/BULLET.H b/BULLET.H new file mode 100644 index 0000000..db97db0 --- /dev/null +++ b/BULLET.H @@ -0,0 +1,153 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\bullet.h_v 2.18 16 Oct 1995 16:47:40 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BULLET.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BULLET_H +#define BULLET_H + +#include "object.h" +#include "fly.h" +#include "fuse.h" + + +class BulletClass : public ObjectClass, + public FlyClass, + public FuseClass +{ + public: + + /* + ** This specifies exactly what kind of bullet this is. All of the static attributes + ** for this bullet is located in the BulletTypeClass pointed to by this variable. + */ + BulletTypeClass const * const Class; + operator BulletType(void) const {return Class->Type;}; + + /* + ** Records who sent this "present" so that an appropriate "thank you" can + ** be returned. + */ + TechnoClass * Payback; + + /* + ** This is the facing that the projectile is travelling. + */ + FacingClass PrimaryFacing; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + static void * BulletClass::operator new(size_t size); + static void BulletClass::operator delete(void *ptr); + BulletClass(void); + BulletClass(BulletType id); + virtual ~BulletClass(void) {if (GameActive) BulletClass::Limbo();}; + virtual RTTIType What_Am_I(void) const {return RTTI_BULLET;}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + static void Init(void); + + virtual void Assign_Target(TARGET target) {TarCom = target;}; + virtual bool Unlimbo(COORDINATE , DirType facing = DIR_N); + virtual LayerType In_Which_Layer(void) const {return LAYER_TOP;}; + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual void Detach(TARGET target, bool all); + virtual void Draw_It(int x, int y, WindowNumberType window); + virtual bool Mark(MarkType mark=MARK_CHANGE); + virtual void AI(void); + virtual short const * Occupy_List(void) const; + virtual short const * Overlap_List(void) const {return Occupy_List();}; + virtual TARGET As_Target(void) const; + + /* + ** File I/O. + */ + bool Load(FileClass & file); + bool Save(FileClass & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + /* + ** If this bullet is forced to be inaccurate because of some outside means. A tank + ** firing while moving is a good example. + */ + unsigned IsInaccurate:1; + + private: + + // Crude animation flag. + unsigned IsToAnimate:1; + + /* + ** This is the height of the projectile. It starts at a low height, rises to an + ** apogee and then drops to explode upon impact. The height is used to render + ** the bullet's vertical offset. + */ + int Altitude; + + /* + ** This is a modifier for the altitude that rises and falls in order to simulate + ** the arc of the projectile. This value is added to the height every game tick + ** while simultaneously being reduced itself. The net effect, is a rising + ** projectile that slows and then eventually drops. + */ + signed char Riser; + + /* + ** This is the target of the projectile. It is especially significant for those projectiles + ** that home in on a target. + */ + TARGET TarCom; + + /* + ** Is this missle allowed to come in from out of bounds? + */ + unsigned IsLocked:1; + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; +}; + +#endif diff --git a/CARGO.CPP b/CARGO.CPP new file mode 100644 index 0000000..a8e7367 --- /dev/null +++ b/CARGO.CPP @@ -0,0 +1,183 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\cargo.cpv 2.18 16 Oct 1995 16:49:50 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CARGO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : 10/31/94 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CargoClass::Attach -- Add unit to cargo hold. * + * CargoClass::Attached_Object -- Determine attached unit pointer. * + * CargoClass::Detach_Object -- Removes a unit from the cargo hold. * + * CargoClass::Debug_Dump -- Displays the cargo value to the monochrome screen. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * CargoClass::Debug_Dump -- Displays the cargo value to the monochrome screen. * + * * + * This routine is used to dump the current cargo value to the monochrome monitor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void CargoClass::Debug_Dump(MonoClass * mono) const +{ + if (How_Many()) { + mono->Set_Cursor(63, 3); + mono->Printf("(%d)%04X", How_Many(), Attached_Object()); + } +} +#endif + + +/*********************************************************************************************** + * CargoClass::Attach -- Add unit to cargo hold. * + * * + * This routine will add the specified unit to the cargo hold. The * + * unit will chain to any existing units in the hold. The chaining is * + * in a LIFO order. * + * * + * INPUT: object-- Pointer to the object to attach to the cargo hold. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/23/1994 JLB : Created. * + * 10/31/94 JLB : Handles chained objects. * + *=============================================================================================*/ +void CargoClass::Attach(FootClass * object) +{ + /* + ** If there is no object, then no action is necessary. + */ + if (!object) return; + + object->Limbo(); + + /* + ** Attach any existing cargo hold object to the end of the list as indicated by the + ** object pointer passed into this routine. This is necessary because several objects may + ** be attached at one time or several objects may be attached as a result of several calls + ** to this routine. Either case must be handled properly. + */ + ObjectClass * o = object->Next; + while (o) { + if (!o->Next) break; + o = o->Next; + } + if (o) { + o->Next = CargoHold; + } else { + object->Next = CargoHold; + } + + /* + ** Finally, assign the object pointer as the first object attached to this cargo hold. + */ + CargoHold = object; + Quantity = 0; + object = CargoHold; + while (object) { + Quantity++; + object = (FootClass *)object->Next; + } +} + + +/*********************************************************************************************** + * CargoClass::Detach_Object -- Removes a unit from the cargo hold. * + * * + * This routine will take a unit from the cargo hold and extract it. * + * The unit extracted is the last unit added to the hold. If there * + * is no unit in the hold or the occupant is not a unit, then NULL is * + * returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the unit that has been extracted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/23/1994 JLB : Created. * + * 06/07/1994 JLB : Handles generic object types. * + *=============================================================================================*/ +FootClass * CargoClass::Detach_Object(void) +{ + FootClass * unit = Attached_Object(); + + if (unit) { + CargoHold = (FootClass *)unit->Next; + unit->Next = 0; + Quantity--; + } + return(unit); +} + + +/*********************************************************************************************** + * CargoClass::Attached_Object -- Determine attached unit pointer. * + * * + * This routine will return with a pointer to the attached unit if one * + * is present. One would need to know this if this is a transport * + * unit and it needs to unload. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to the attached unit. If there is no * + * attached unit, then return NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/07/1992 JLB : Created. * + * 06/07/1994 JLB : Handles generic object types. * + *=============================================================================================*/ +FootClass * CargoClass::Attached_Object(void) const +{ + if (Is_Something_Attached()) { + return(CargoHold); + } + return(NULL); +} + + diff --git a/CARGO.H b/CARGO.H new file mode 100644 index 0000000..6adefc4 --- /dev/null +++ b/CARGO.H @@ -0,0 +1,91 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\cargo.h_v 2.20 16 Oct 1995 16:45:14 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CARGO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CARGO_H +#define CARGO_H + +class FootClass; + +/**************************************************************************** +** This class handles the basic cargo logic. +*/ +class CargoClass { + public: + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + CargoClass(void) {Quantity = 0;CargoHold = 0;}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const; + #endif + void AI(void) {}; + + int How_Many(void) const {return Quantity;}; + bool Is_Something_Attached(void) const {return (CargoHold != 0);}; + FootClass * Attached_Object(void) const; + FootClass * Detach_Object(void); + void Attach(FootClass * object); + + /* + ** File I/O. + */ + void Code_Pointers(void); + void Decode_Pointers(void); + + private: + + /* + ** This is the number of objects attached to this cargo hold. For transporter + ** objects, they might contain more than one object. + */ + unsigned char Quantity; + + /* + ** This is the target value of any attached object. A value of zero indicates + ** that no object is attached. + */ + FootClass * CargoHold; +}; + +#endif + diff --git a/CCDDE.CPP b/CCDDE.CPP new file mode 100644 index 0000000..4f1603e --- /dev/null +++ b/CCDDE.CPP @@ -0,0 +1,422 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : CCDDE.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 10/04/95 * + * * + * Last Update : August 5th, 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * C&C's interface to the DDE class * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * DDE_Callback -- DDE server callback function * + * DDEServerClass::DDEServerClass -- class constructor * + * DDEServerClass::Enable -- Enables the DDE callback * + * DDEServerClass::Disable -- Disables the DDE callback * + * DDEServerClass::~DDEServerClass -- class destructor * + * DDESC::Callback -- callback function. Called from the DDE_Callback wrapper function * + * DDESC::Get_MPlayer_Game_Info -- returns a pointer to the multiplayer setup info from wchat * + * DDESC::Delete_MPlayer_Game_Info -- clears out multi player game setup info * + * DDESC::Time_Since_Heartbeat -- returns the time in ticks since the last heartbeat from wchat* + * * + * Send_Data_To_DDE_Server -- sends a packet to WChat * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#define WIN32 +#include +#include "ccdde.h" +#include +#include + +DDEServerClass DDEServer; //Instance of the DDE Server class + +Instance_Class *DDE_Class = NULL; // pointer for client callback + // this *must* be called DDE_Class + +BOOL CC95AlreadyRunning = FALSE; //Was there an instance of C&C 95 already running when we started? + +extern HWND MainWindow; +extern TimerClass GameTimer; +extern BOOL GameTimerInUse; +extern void CCDebugString (char *string); + + +/*********************************************************************************************** + * DDE_Callback -- DDE server callback function * + * * + * Just acts as a wrapper for the DDEServerClass callback function * + * * + * INPUT: ptr to data from client * + * length of data * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 3:19PM ST : Created * + *=============================================================================================*/ +BOOL CALLBACK DDE_Callback (unsigned char *data, long length) +{ + return (DDEServer.Callback(data, length)); +} + + + + +/*********************************************************************************************** + * DDEServerClass::DDEServerClass -- class constructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 3:20PM ST : Created * + *=============================================================================================*/ +DDEServerClass::DDEServerClass(void) +{ + MPlayerGameInfo = NULL; //Flag that we havnt received a start game info packet yet + + DDE_Class = new Instance_Class ("CONQUER", "WCHAT"); + + DDE_Class->Enable_Callback( TRUE ); + IsEnabled = TRUE; + + if (DDE_Class->Test_Server_Running(DDE_Class->local_name)){ + CC95AlreadyRunning = TRUE; + }else{ + DDE_Class->Register_Server( DDE_Callback ); + } +} + + + +void DDEServerClass::Enable(void) +{ + if (!IsEnabled){ + DDE_Class->Enable_Callback( TRUE ); + IsEnabled = TRUE; + } +} + + + +/*********************************************************************************************** + * DDEServerClass::Disable -- Disables the DDE callback * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/96 9:44PM ST : Created * + *=============================================================================================*/ +void DDEServerClass::Disable(void) +{ + if (IsEnabled){ + DDE_Class->Enable_Callback( FALSE ); + IsEnabled = FALSE; + } +} + + + + + + +/*********************************************************************************************** + * DDEServerClass::~DDEServerClass -- class destructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 3:20PM ST : Created * + *=============================================================================================*/ +DDEServerClass::~DDEServerClass(void) +{ + Delete_MPlayer_Game_Info(); + delete( DDE_Class ); +} + + + +/*********************************************************************************************** + * DDESC::Callback -- callback function. Called from the DDE_Callback wrapper function * + * * + * * + * * + * INPUT: data from DDE client * + * length of data * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: Data has length and type as first 2 ints * + * * + * HISTORY: * + * 6/8/96 3:21PM ST : Created * + *=============================================================================================*/ +BOOL DDEServerClass::Callback(unsigned char *data, long length) +{ + + /* + ** If the packet length < 0 then this is a special advisory packet + */ + if ( length<0 ) { + + switch( length ) { + + case DDE_ADVISE_CONNECT: + CCDebugString("C&C95 - DDE advisory: client connect detected."); + return TRUE; + + case DDE_ADVISE_DISCONNECT: + CCDebugString("C&C95 - DDE advisory: client disconnect detected."); + return TRUE; + + default: + CCDebugString("C&C95 - DDE advisory: Unknown DDE advise type."); + return FALSE; + } + + }else{ + + /* + ** Packet must be at least the length of the packet type & size fields to be valid + */ + if (length < 2*sizeof(int)) { + CCDebugString ("C&C95 - Received invalid packet."); + return (FALSE); + } + + /* + ** Find out what kind of packet this is and its length. + */ + int *packet_pointer = (int *)data; + int actual_length = ntohl(*packet_pointer++); + int packet_type = ntohl(*packet_pointer++); + + /* + ** Strip the ID int from the start of the packet + */ + data += 2*sizeof (int); + length -= 2*sizeof (int); + actual_length -= 2*sizeof (int); + + /* + ** Take the appropriate action for the packet type + */ + switch ( packet_type ){ + + /* + ** This is a packet with the info required for starting a new internet game. This is really + * just C&CSPAWN.INI sent from WChat instead of read from disk. + */ + case DDE_PACKET_START_MPLAYER_GAME: + CCDebugString("C&C95 - Received start game packet."); + Delete_MPlayer_Game_Info(); + MPlayerGameInfo = new char [actual_length + 1]; + memcpy (MPlayerGameInfo, data, actual_length); + *(MPlayerGameInfo + actual_length) = 0; //Terminator in case we treat it as a string + MPlayerGameInfoLength = actual_length; + LastHeartbeat = 0; + break; + + case DDE_TICKLE: + CCDebugString("C&C95 - Received 'tickle' packet."); + //SetForegroundWindow ( MainWindow ); + //ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + break; + + case DDE_PACKET_HEART_BEAT: + CCDebugString("C&C95 - Received heart beat packet."); + if (GameTimerInUse){ + LastHeartbeat = GameTimer.Time(); + }else{ + LastHeartbeat = 0; + } + break; + + default: + CCDebugString("C&C95 - Received unrecognised packet."); + break; + + } + } + + return (TRUE); + +} + + + +/*********************************************************************************************** + * DDESC::Get_MPlayer_Game_Info -- returns a pointer to the multiplayer setup info from wchat * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: ptr to data in .INI file format * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 3:23PM ST : Created * + *=============================================================================================*/ +char *DDEServerClass::Get_MPlayer_Game_Info (void) +{ + return (MPlayerGameInfo); +} + + + +/*********************************************************************************************** + * DDESC::Delete_MPlayer_Game_Info -- clears out multi player game setup info * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 3:24PM ST : Created * + *=============================================================================================*/ +void DDEServerClass::Delete_MPlayer_Game_Info(void) +{ + if (MPlayerGameInfo){ + delete [] MPlayerGameInfo; + MPlayerGameInfo = NULL; + } +} + + + +/*********************************************************************************************** + * DDESC::Time_Since_Heartbeat -- returns the time in ticks since the last heartbeat from wchat* + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: time since heartbeat * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/9/96 11:05PM ST : Created * + *=============================================================================================*/ +int DDEServerClass::Time_Since_Heartbeat(void) +{ + return (GameTimer.Time() - LastHeartbeat); +} + + + + +/*********************************************************************************************** + * Send_Data_To_DDE_Server -- sends a packet to WChat * + * * + * * + * * + * INPUT: ptr to the data to send * + * length of data * + * packet type identifier * + * * + * OUTPUT: true if packet successfully sent * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/9/96 11:07PM ST : Created * + *=============================================================================================*/ +BOOL Send_Data_To_DDE_Server (char *data, int length, int packet_type) +{ +#if (0) + BOOL app_exists; + + app_exists = DDE_Class->Test_Server_Running(DDE_Class->remote_name); + + if (app_exists != TRUE) { + CCDebugString("Connection to server failed!"); + return(FALSE); + } +#endif //(0) + + if( DDE_Class->Open_Poke_Connection(DDE_Class->remote_name) == FALSE) { + CCDebugString("C&C95 - Failed to connect for POKE!"); + return (FALSE); + } + + char *poke_data = new char [length + 2*sizeof(int)]; + + int *poke_data_int = (int*)poke_data; + + *poke_data_int = htonl (length + 2*sizeof(int)); + *(poke_data_int+1)= htonl (packet_type); + + memcpy (poke_data + 8, data, length); + + + if(DDE_Class->Poke_Server( (LPBYTE) poke_data, ntohl(*poke_data_int) ) == FALSE) { + CCDebugString("C&C95 - POKE failed!\n"); + DDE_Class->Close_Poke_Connection(); // close down the link + delete poke_data; + return (FALSE); + } + + DDE_Class->Close_Poke_Connection(); // close down the link + + delete poke_data; + + return (TRUE); +} + + diff --git a/CCDDE.H b/CCDDE.H new file mode 100644 index 0000000..1057a92 --- /dev/null +++ b/CCDDE.H @@ -0,0 +1,88 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : CCDDE.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : 10/04/95 * + * * + * Last Update : August 5th, 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * C&C's interface to the DDE class * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifdef WIN32 + +#include "dde.h" + +class DDEServerClass { + + public: + + DDEServerClass (void); + ~DDEServerClass (void); + + + char *Get_MPlayer_Game_Info (void); //Returns pointer to game info + int Get_MPlayer_Game_Info_Length(){return(MPlayerGameInfoLength);}; //Len of game info + BOOL Callback(unsigned char *data, long length); //DDE callback function + void Delete_MPlayer_Game_Info(void); //release the game info memory + void Enable(void); //Enable the DDE callback + void Disable(void); //Disable the DDE callback + int Time_Since_Heartbeat(void); //Returns the time since the last hearbeat from WChat + + /* + ** Enumeration for DDE packet types from WChat + */ + enum { + DDE_PACKET_START_MPLAYER_GAME, //Start game packet. This includes game options + DDE_PACKET_GAME_RESULTS, //Game results packet. The game statistics. + DDE_PACKET_HEART_BEAT, //Heart beat packet so we know WChat is still there. + DDE_TICKLE, //Message to prompt other app to take focus. + DDE_CONNECTION_FAILED + }; + + + private: + + char *MPlayerGameInfo; //Pointer to game start packet + int MPlayerGameInfoLength; //Length of game start packet. + BOOL IsEnabled; //Flag for DDE callback enable + int LastHeartbeat; // Time since last heartbeat packet was received from WChat + +}; + + +extern DDEServerClass DDEServer; +extern BOOL Send_Data_To_DDE_Server (char *data, int length, int packet_type); + + +#endif //WIN32 diff --git a/CCFILE.CPP b/CCFILE.CPP new file mode 100644 index 0000000..1ee39de --- /dev/null +++ b/CCFILE.CPP @@ -0,0 +1,626 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CCFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : March 20, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CCFileClass::CCFileClass -- Default constructor for file object. * + * CCFileClass::CCFileClass -- Filename based constructor for C&C file. * + * CCFileClass::Close -- Closes the file. * + * CCFileClass::Is_Available -- Checks for existence of file on disk or in mixfile. * + * CCFileClass::Is_Open -- Determines if the file is open. * + * CCFileClass::Open -- Opens a file from either the mixfile system or the rawfile system. * + * CCFileClass::Read -- Reads data from the file. * + * CCFileClass::Seek -- Moves the current file pointer in the file. * + * CCFileClass::Size -- Determines the size of the file. * + * CCFileClass::Write -- Writes data to the file (non mixfile files only). * + * CCFileClass::Error -- Handles displaying a file error message. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +//#include +//#include +//#include +//#include +//#include +//#include +//#include "ccfile.h" + + +/*********************************************************************************************** + * CCFileClass::Error -- Handles displaying a file error message. * + * * + * Display an error message as indicated. If it is allowed to retry, then pressing a key * + * will return from this function. Otherwise, it will exit the program with "exit()". * + * * + * INPUT: error -- The error number (same as the DOSERR.H error numbers). * + * * + * canretry -- Can this routine exit normally so that retrying can occur? If this is * + * false, then the program WILL exit in this routine. * + * * + * filename -- Optional filename to report with this error. If no filename is * + * supplied, then no filename is listed in the error message. * + * * + * OUTPUT: none, but this routine might not return at all if the "canretry" parameter is * + * false or the player pressed ESC. * + * * + * WARNINGS: This routine may not return at all. It handles being in text mode as well as * + * if in a graphic mode. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void CCFileClass::Error(int , int , char const * ) +{ +#ifdef DEMO + if (strstr(File_Name(), "\\")) { + if (!Force_CD_Available(-1)) { + Prog_End(); + exit(EXIT_FAILURE); + } + } + +#else + + if (!Force_CD_Available(RequiredCD)) { + Prog_End(); + exit(EXIT_FAILURE); + } + +#endif +} + + +/*********************************************************************************************** + * CCFileClass::CCFileClass -- Filename based constructor for C&C file. * + * * + * Use this constructor for a file when the filename is known at construction time. * + * * + * INPUT: filename -- Pointer to the filename to use for this file object. * + * * + * OUTPUT: none * + * * + * WARNINGS: The filename pointer is presumed to be inviolate throughout the duration of * + * the file object. If this is not guaranteed, then use the default constructor * + * and then set the name manually. * + * * + * HISTORY: * + * 03/20/1995 JLB : Created. * + *=============================================================================================*/ +CCFileClass::CCFileClass(char const *filename) : + CDFileClass(), + FromDisk(false), + Pointer(0), + Position(0), + Length(0), + Start(0) +{ + Set_Name(filename); +} + + +/*********************************************************************************************** + * CCFileClass::CCFileClass -- Default constructor for file object. * + * * + * This is the default constructor for a C&C file object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1995 JLB : Created. * + *=============================================================================================*/ +CCFileClass::CCFileClass(void) +{ + FromDisk = false; + Pointer = 0; + Position = 0; + Length = 0; + Start = 0; +} + + +/*********************************************************************************************** + * CCFileClass::Write -- Writes data to the file (non mixfile files only). * + * * + * This routine will write data to the file, but NOT to a file that is part of a mixfile. * + * * + * INPUT: buffer -- Pointer to the buffer that holds the data to be written. * + * * + * size -- The number of bytes to write. * + * * + * OUTPUT: Returns the number of bytes actually written. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +long CCFileClass::Write(void const *buffer, long size) +{ + + /* + ** If this is part of a mixfile, then writing is not allowed. Error out with a fatal + ** message. + */ + if (Pointer || FromDisk) { + Error(EACCES, false, File_Name()); + } + + return(CDFileClass::Write(buffer, size)); +} + + +/*********************************************************************************************** + * CCFileClass::Read -- Reads data from the file. * + * * + * This routine determines if the file is part of the mixfile system. If it is, then * + * the file is copied from RAM if it is located there. Otherwise it is read from disk * + * according to the correct position of the file within the parent mixfile. * + * * + * INPUT: buffer -- Pointer to the buffer to place the read data. * + * * + * size -- The number of bytes to read. * + * * + * OUTPUT: Returns the actual number of bytes read (this could be less than requested). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +long CCFileClass::Read(void *buffer, long size) +{ + int opened = false; + + if (!Is_Open()) { + if (Open()) { + opened = true; + } + } + + /* + ** If the file is part of a loaded mixfile, then a mere copy is + ** all that is required for the read. + */ + if (Pointer) { + long maximum = Length - Position; + + size = MIN(maximum, size); + if (size) { + Mem_Copy(Add_Long_To_Pointer(Pointer, Position), buffer, size); + Position += size; + } + if (opened) Close(); + return(size); + } + + /* + ** If the file is part of a mixfile, but the mixfile is located + ** on disk, then a special read operation is necessary. + */ + if (FromDisk) { + long maximum = Length - Position; + + size = MIN(maximum, size); + if (size > 0) { + CDFileClass::Seek(Start + Position, SEEK_SET); + size = CDFileClass::Read(buffer, size); + Position += size; + } + if (opened) Close(); + return(size); + } + + long s = CDFileClass::Read(buffer, size); + if (opened) Close(); + return(s); +} + + +/*********************************************************************************************** + * CCFileClass::Seek -- Moves the current file pointer in the file. * + * * + * This routine will change the current file pointer to the position specified. It follows * + * the same rules the a normal Seek() does, but if the file is part of the mixfile system, * + * then only the position value needs to be updated. * + * * + * INPUT: pos -- The position to move the file to relative to the position indicated * + * by the "dir" parameter. * + * * + * dir -- The direction to affect the position change against. This can be * + * either SEEK_CUR, SEEK_END, or SEEK_SET. * + * * + * OUTPUT: Returns with the position of the new location. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +long CCFileClass::Seek(long pos, int dir) +{ + if (Pointer || FromDisk) { + switch (dir) { + case SEEK_END: + Position = Length; + break; + + case SEEK_SET: + Position = 0; + break; + + case SEEK_CUR: + default: + break; + } + Position += pos; + if (Position < 0) Position = 0; + if (Position > Length) Position = Length; + return(Position); + } + return(CDFileClass::Seek(pos, dir)); +} + + +/*********************************************************************************************** + * CCFileClass::Size -- Determines the size of the file. * + * * + * If the file is part of the mixfile system, then the size of the file is already * + * determined and available. Otherwise, go to the low level system to find the file * + * size. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the size of the file in bytes. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +long CCFileClass::Size(void) +{ + if (Pointer || FromDisk) return(Length); + + return(CDFileClass::Size()); +} + + +/*********************************************************************************************** + * CCFileClass::Is_Available -- Checks for existence of file on disk or in mixfile. * + * * + * This routine will examine the mixfile system looking for the file. If the file could * + * not be found there, then the disk is examined directly. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file available for opening? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +int CCFileClass::Is_Available(int ) +{ + if (MixFileClass::Offset(File_Name())) { + return(true); + } + return(CDFileClass::Is_Available()); +} + + +/*********************************************************************************************** + * CCFileClass::Is_Open -- Determines if the file is open. * + * * + * A mixfile is open if there is a pointer to the mixfile data. In absence of this, * + * the the file is open if the file handle is valid. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file open? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +int CCFileClass::Is_Open(void) const +{ + + /* + ** If the file is part of a cached file, then return that it is opened. A closed file + ** doesn't have a valid pointer. + */ + if (Pointer) return(true); + return(CDFileClass::Is_Open()); +} + + +/*********************************************************************************************** + * CCFileClass::Close -- Closes the file. * + * * + * If this is a mixfile file, then only the pointers need to be adjusted. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +void CCFileClass::Close(void) +{ + FromDisk = false; + Pointer = 0; + Position = 0; // Starts at beginning offset. + Start = 0; + Length = 0; + CDFileClass::Close(); +} + + +/*********************************************************************************************** + * CCFileClass::Open -- Opens a file from either the mixfile system or the rawfile system. * + * * + * This routine will open the specified file. It examines the mixfile system to find a * + * match. If one is found then the file is "opened" in a special cached way. Otherwise * + * it is opened as a standard DOS file. * + * * + * INPUT: rights -- The access rights desired. * + * * + * OUTPUT: bool; Was the file opened successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +int CCFileClass::Open(int rights) +{ + /* + ** Always close the file if it was open. + */ + Close(); + + /* + ** Perform a preliminary check to see if the specified file + ** exists on the disk. If it does, then open this file regardless + ** of whether it also exists in RAM. This is slower, but allows + ** upgrade files to work. + */ + if ((rights & WRITE) || CDFileClass::Is_Available()) { + return(CDFileClass::Open(rights)); + } + + /* + ** Check to see if file is part of a mixfile and that mixfile is currently loaded + ** into RAM. + */ + MixFileClass *mixfile = 0; + if (MixFileClass::Offset(File_Name(), &Pointer, &mixfile, &Start, &Length)) { + + /* + ** If the mixfile is located on disk, then fake out the file system to read from + ** the mixfile, but think it is reading from a solitary file. + */ + if (!Pointer) { + long start = Start; + long length = Length; + + /* + ** This is a legitimate open to the file. All access to the file through this + ** file object will be appropriately adjusted for mixfile support however. Also + ** note that the filename attached to this object is NOT the same as the file + ** attached to the file handle. + */ + char const * dupfile = strdup(File_Name()); + Open(mixfile->Filename, READ); + Searching(false); // Disable multi-drive search. + Set_Name(dupfile); + Searching(true); + if (dupfile) free((void *)dupfile); + Start = start; + Length = length; + FromDisk = true; + } + + } else { + + /* + ** The file cannot be found in any mixfile, so it must reside as + ** an individual file on the disk. Or else it is just plain missing. + */ + return(CDFileClass::Open(rights)); + } + return(true); +} + + +/*********************************************************************************** +** Backward compatibility section. +*/ +//extern "C" { + +static CCFileClass Handles[10]; + +#ifdef NEVER +bool __cdecl Set_Search_Drives(char const *) +{ + CCFileClass::Set_Search_Path(path); + return(true); +} +#endif + +int __cdecl Open_File(char const *file_name, int mode) +{ + for (int index = 0; index < sizeof(Handles)/sizeof(Handles[0]); index++) { + if (!Handles[index].Is_Open()) { + Handles[index].Set_Name(file_name); + if (Handles[index].Open(mode)) { +// if (Handles[index].Open(file_name, mode)) { + return(index); + } + break; + } + } + return(WW_ERROR); +} + +VOID __cdecl Close_File(int handle) +{ + if (handle != WW_ERROR && Handles[handle].Is_Open()) { + Handles[handle].Close(); + } +} + +LONG __cdecl Read_File(int handle, VOID *buf, ULONG bytes) +{ + if (handle != WW_ERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Read(buf, bytes)); + } + return(0); +} + +LONG __cdecl Write_File(int handle, VOID const *buf, ULONG bytes) +{ + if (handle != WW_ERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Write(buf, bytes)); + } + return(0); +} + +int __cdecl Find_File(char const *file_name) +{ + CCFileClass file(file_name); + return(file.Is_Available()); +} + +#ifdef NEVER +int __cdecl Delete_File(char const *file_name) +{ + return(CCFileClass(file_name).Delete()); +} + +int __cdecl Create_File(char const *file_name) +{ + return(CCFileClass(file_name).Create()); +} + +ULONG __cdecl Load_Data(char const *name, VOID *ptr, ULONG size) +{ + return(CCFileClass(name).Read(ptr, size)); +} +#endif + +VOID * __cdecl Load_Alloc_Data(char const *name, int ) +{ + CCFileClass file(name); + + return(Load_Alloc_Data(file)); +} + +ULONG __cdecl File_Size(int handle) +{ + if (handle != WW_ERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Size()); + } + return(0); +} + +#ifdef NEVER +ULONG __cdecl Write_Data(char const *name, VOID const *ptr, ULONG size) +{ + return(CCFileClass(name).Write(ptr, size)); +} +#endif + +ULONG __cdecl Seek_File(int handle, LONG offset, int starting) +{ + if (handle != WW_ERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Seek(offset, starting)); + } + return(0); +} + +void WWDOS_Shutdown(void) +{ + for (int index = 0; index < 10; index++) { + Handles[index].Set_Name(NULL); + } +} + +#ifdef NEVER +bool __cdecl Multi_Drive_Search(bool on) +{ +// return(CCFileClass::Multi_Drive_Search(on)); + return(on); +} + +VOID __cdecl WWDOS_Init(VOID) +{ +} + +VOID __cdecl WWDOS_Shutdown(VOID) +{ +} + +int __cdecl Find_Disk_Number(char const *) +{ + return(0); +} +#endif + +//ULONG cdecl Load_Uncompress(BYTE const *file, BuffType uncomp_buff, BuffType dest_buff, VOID *reserved_data) +//{ +// return(Load_Uncompress(CCFileClass(file), uncomp_buff, dest_buff, reserved_data)); +// return(CCFileClass(file).Load_Uncompress(uncomp_buff, dest_buff, reserved_data)); +//} + +//extern "C" { +//int MaxDevice; +//int DefaultDrive; +//char CallingDOSInt; + +//} + + +void Unfragment_File_Cache(void) +{ +} + diff --git a/CCFILE.H b/CCFILE.H new file mode 100644 index 0000000..8612ae4 --- /dev/null +++ b/CCFILE.H @@ -0,0 +1,114 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\ccfile.h_v 2.18 16 Oct 1995 16:45:28 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CCFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 17, 1994 * + * * + * Last Update : October 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CCFILE_H +#define CCFILE_H + +#include +#include +#include "mixfile.h" +#include "cdfile.h" + + +/* +** This derived class for file access knows about mixfiles (packed files). It can handle opening +** a file that is embedded within a mixfile. This is true if the mixfile is cached or resides on +** disk. It is functionally similar to pakfiles, except much faster and less RAM intensive. +*/ +class CCFileClass : public CDFileClass +{ + public: + CCFileClass(char const *filename); + CCFileClass(void); + virtual ~CCFileClass(void) {}; + + // Delete should be overloaded here as well. Don't allow deletes of mixfiles. + + virtual int Open(char const *filename, int rights=READ) {Set_Name(filename);return Open(rights);}; + virtual int Open(int rights=READ); + virtual int Is_Open(void) const; + virtual int Is_Available(int forced=false); + virtual long Read(void *buffer, long size); + virtual long Seek(long pos, int dir=SEEK_CUR); + virtual long Size(void); + virtual long Write(void const *buffer, long size); + virtual void Close(void); + virtual void Error(int error, int canretry = false, char const * filename=NULL); + + private: + + /* + ** This flag indicates that the file is part of a mixfile and the mixfile resides on + ** disk. The file handle for this file is a legitimate DOS handle, although special + ** handling is necessary that takes into account the embedded nature of the file. + */ + bool FromDisk; + + /* + ** This indicates the file is actually part of a resident image of the mixfile + ** itself. In this case, the embedded file handle is invalid. All file access actually + ** gets routed through the cached version of the file. This is a pointer to the start + ** of the RAM image of the file. + */ + void * Pointer; + + /* + ** This is the starting offset of the beginning of the file. This value is only valid + ** if the file is part of a mixfile that resides on disk. It serves as the counterpart + ** to the "Pointer" variable. + */ + long Start; + + /* + ** This is the current seek position of the file. It is duplicated here if the file is + ** part of a mixfile since the DOS seek position is not accurate. This value will + ** range from zero to the size of the file in bytes. + */ + long Position; + + /* + ** This is the size of the file if it was embedded in a mixfile. The size must be manually + ** kept track of because the DOS file size is invalid. + */ + long Length; + + // Force these to never be invoked. + CCFileClass const operator = (CCFileClass const & c); + CCFileClass (CCFileClass const & ) {}; +}; + +#endif diff --git a/CC_ICON.RC b/CC_ICON.RC new file mode 100644 index 0000000..a2324fa --- /dev/null +++ b/CC_ICON.RC @@ -0,0 +1,16 @@ +/**************************************************************************** + + +CC_ICON.RC + +produced by Borland Resource Workshop + + +*****************************************************************************/ + +#include "cc_icon.rh" + + + +ICON_1 ICON "conquer.ico" + diff --git a/CC_ICON.RH b/CC_ICON.RH new file mode 100644 index 0000000..206d6bc --- /dev/null +++ b/CC_ICON.RH @@ -0,0 +1 @@ +#define ICON_1 1 diff --git a/CDATA.CPP b/CDATA.CPP new file mode 100644 index 0000000..2e499c6 --- /dev/null +++ b/CDATA.CPP @@ -0,0 +1,2773 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\cdata.cpv 2.18 16 Oct 1995 16:50:22 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 16, 1994 * + * * + * Last Update : July 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TemplateTypeClass::Create_And_Place -- Creates and places a template object on the map. * + * TemplateTypeClass::Create_One_Of -- Creates an object of this template type. * + * TemplateTypeClass::Display -- Displays a generic representation of template. * + * TemplateTypeClass::From_Name -- Determine template from ASCII name. * + * TemplateTypeClass::Init -- Loads graphic data for templates. * + * TemplateTypeClass::Occupy_List -- Determines occupation list. * + * TemplateTypeClass::One_Time -- Performs one-time initialization * + * TemplateTypeClass::Prep_For_Add -- Prepares to add template to scenario. * + * TemplateTypeClass::TemplateTypeClass -- Constructor for template type objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +static char const _slope00000001[] = {7,-1}; +static char const _slope000000101[] = {6,8,-1}; +static char const _slope00000011[] = {6,7,-1}; +static char const _slope0000001[] = {6,-1}; +static char const _slope000001001[] = {5,8,-1}; +static char const _slope000001[] = {5,-1}; +static char const _slope000101[] = {3,5,-1}; +static char const _slope00011010000100000001000011[] = {3,4,6,11,19,25,25,-1}; +static char const _slope00011010010100100001000011[] = {3,4,6,9,11,14,19,24,25,-1}; +static char const _slope0001[] = {3,-1}; +static char const _slope001001001[] = {2,5,8,-1}; +static char const _slope00110000000011[] = {2,3,12,13,-1}; +static char const _slope00110010010011[] = {2,3,6,9,12,13,-1}; +static char const _slope001111001[] = {2,3,4,5,8,-1}; +static char const _slope0011[] = {2,3,-1}; +static char const _slope001[] = {2,-1}; +static char const _slope01000000000000000000001[] = {1,22,-1}; +static char const _slope01000000100000010000001[] = {1,8,15,22,-1}; +static char const _slope0111[] = {1,2,3,-1}; +static char const _slope01[] = {1,-1}; +static char const _slope1001001[] = {0,3,6,-1}; +static char const _slope1001[] = {0,3,-1}; +static char const _slope1100000000000000001100011[] = {0,1,18,19,23,24,-1}; +static char const _slope1100001000001000001100011[] = {0,1,6,12,18,19,23,24,-1}; +static char const _slope1101101[] = {0,1,3,4,6,-1}; +static char const _slope1101[] = {0,1,3,-1}; +static char const _slope111[] = {0,1,2,-1}; +static char const _slope111010011[] = {0,1,2,4,7,8,-1}; +static char const _slope11101[] = {0,1,2,4,-1}; +static char const _slope111111011[] = {0,1,2,3,4,5,7,8,-1}; +static char const _slope11111111[] = {0,1,2,3,4,5,6,7,-1}; +static char const _slope111111[] = {0,1,2,3,4,5,-1}; +static char const _slope1[] = {0,-1}; + +static TemplateTypeClass const Empty( + TEMPLATE_CLEAR1, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE|THEATERF_JUNGLE, + "CLEAR1", + TXT_CLEAR, + LAND_CLEAR, + 1,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Clear( + TEMPLATE_CLEAR1, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE|THEATERF_JUNGLE, + "CLEAR1", + TXT_CLEAR, + LAND_CLEAR, + 1,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road1( + TEMPLATE_ROAD1, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D01", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road2( + TEMPLATE_ROAD2, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D02", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road3( + TEMPLATE_ROAD3, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D03", + TXT_ROAD, + LAND_CLEAR, + 1,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road4( + TEMPLATE_ROAD4, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D04", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road5( + TEMPLATE_ROAD5, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D05", + TXT_ROAD, + LAND_CLEAR, + 3,4, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road6( + TEMPLATE_ROAD6, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D06", + TXT_ROAD, + LAND_CLEAR, + 2,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road7( + TEMPLATE_ROAD7, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D07", + TXT_ROAD, + LAND_CLEAR, + 3,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road8( + TEMPLATE_ROAD8, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D08", + TXT_ROAD, + LAND_CLEAR, + 3,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road9( + TEMPLATE_ROAD9, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D09", + TXT_ROAD, + LAND_CLEAR, + 4,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road10( + TEMPLATE_ROAD10, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D10", + TXT_ROAD, + LAND_CLEAR, + 4,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road11( + TEMPLATE_ROAD11, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D11", + TXT_ROAD, + LAND_CLEAR, + 2,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road12( + TEMPLATE_ROAD12, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D12", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road13( + TEMPLATE_ROAD13, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D13", + TXT_ROAD, + LAND_CLEAR, + 4,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road14( + TEMPLATE_ROAD14, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D14", + TXT_ROAD, + LAND_CLEAR, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road15( + TEMPLATE_ROAD15, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D15", + TXT_ROAD, + LAND_CLEAR, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road16( + TEMPLATE_ROAD16, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D16", + TXT_ROAD, + LAND_CLEAR, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road17( + TEMPLATE_ROAD17, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D17", + TXT_ROAD, + LAND_CLEAR, + 3,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road18( + TEMPLATE_ROAD18, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D18", + TXT_ROAD, + LAND_CLEAR, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road19( + TEMPLATE_ROAD19, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D19", + TXT_ROAD, + LAND_CLEAR, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road20( + TEMPLATE_ROAD20, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D20", + TXT_ROAD, + LAND_CLEAR, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road21( + TEMPLATE_ROAD21, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D21", + TXT_ROAD, + LAND_CLEAR, + 3,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road22( + TEMPLATE_ROAD22, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D22", + TXT_ROAD, + LAND_CLEAR, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road23( + TEMPLATE_ROAD23, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D23", + TXT_ROAD, + LAND_CLEAR, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road24( + TEMPLATE_ROAD24, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D24", + TXT_ROAD, + LAND_CLEAR, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road25( + TEMPLATE_ROAD25, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D25", + TXT_ROAD, + LAND_CLEAR, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road26( + TEMPLATE_ROAD26, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D26", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road27( + TEMPLATE_ROAD27, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D27", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road28( + TEMPLATE_ROAD28, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D28", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road29( + TEMPLATE_ROAD29, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D29", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road30( + TEMPLATE_ROAD30, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D30", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road31( + TEMPLATE_ROAD31, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D31", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road32( + TEMPLATE_ROAD32, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D32", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road33( + TEMPLATE_ROAD33, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D33", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road34( + TEMPLATE_ROAD34, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D34", + TXT_ROAD, + LAND_CLEAR, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road35( + TEMPLATE_ROAD35, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D35", + TXT_ROAD, + LAND_CLEAR, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road36( + TEMPLATE_ROAD36, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D36", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road37( + TEMPLATE_ROAD37, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D37", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road38( + TEMPLATE_ROAD38, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D38", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road39( + TEMPLATE_ROAD39, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D39", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road40( + TEMPLATE_ROAD40, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D40", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road41( + TEMPLATE_ROAD41, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D41", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road42( + TEMPLATE_ROAD42, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D42", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Road43( + TEMPLATE_ROAD43, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "D43", + TXT_ROAD, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Water( + TEMPLATE_WATER, + THEATERF_WINTER|THEATERF_TEMPERATE|THEATERF_DESERT, + "W1", + TXT_WATER, + LAND_WATER, + 1,1, + LAND_WATER, + NULL +); +static TemplateTypeClass const Water2( + TEMPLATE_WATER2, + THEATERF_WINTER|THEATERF_TEMPERATE, + "W2", + TXT_WATER, + LAND_WATER, + 2,2, + LAND_WATER, + NULL +); +static TemplateTypeClass const Shore1( + TEMPLATE_SHORE1, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH1", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_BEACH, + (char const *)_slope111111 +); +static TemplateTypeClass const Shore2( + TEMPLATE_SHORE2, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH2", + TXT_WATER, + LAND_ROCK, + 3,3, + LAND_BEACH, + (char const *)_slope111 +); +static TemplateTypeClass const Shore3( + TEMPLATE_SHORE3, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH3", + TXT_WATER, + LAND_ROCK, + 1,1, + LAND_WATER, + NULL +); +static TemplateTypeClass const Shore4( + TEMPLATE_SHORE4, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH4", + TXT_WATER, + LAND_ROCK, + 2,1, + LAND_WATER, + NULL +); +static TemplateTypeClass const Shore5( + TEMPLATE_SHORE5, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH5", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_BEACH, + (char const *)_slope111111 +); +static TemplateTypeClass const Shore6( + TEMPLATE_SHORE6, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH6", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_BEACH, + (char const *)_slope111111 +); +static TemplateTypeClass const Shore7( + TEMPLATE_SHORE7, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH7", + TXT_WATER, + LAND_WATER, + 2,2, + LAND_BEACH, + (char const *)_slope1 +); +static TemplateTypeClass const Shore8( + TEMPLATE_SHORE8, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH8", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_BEACH, + (char const *)_slope11111111 +); +static TemplateTypeClass const Shore9( + TEMPLATE_SHORE9, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH9", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_BEACH, + (char const *)_slope111111011 +); +static TemplateTypeClass const Shore10( + TEMPLATE_SHORE10, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH10", + TXT_WATER, + LAND_WATER, + 2,2, + LAND_BEACH, + (char const *)_slope01 +); +static TemplateTypeClass const Shore11( + TEMPLATE_SHORE11, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH11", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_BEACH, + (char const *)_slope1001 +); +static TemplateTypeClass const Shore12( + TEMPLATE_SHORE12, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH12", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_BEACH, + (char const *)_slope000001001 +); +static TemplateTypeClass const Shore13( + TEMPLATE_SHORE13, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH13", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_BEACH, + (char const *)_slope0000001 +); +static TemplateTypeClass const Shore14( + TEMPLATE_SHORE14, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH14", + TXT_WATER, + LAND_ROCK, + 3,3, + LAND_BEACH, + (char const *)_slope00000011 +); +static TemplateTypeClass const Shore15( + TEMPLATE_SHORE15, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH15", + TXT_WATER, + LAND_ROCK, + 3,3, + LAND_BEACH, + (char const *)_slope000000101 +); +static TemplateTypeClass const Shore16( + TEMPLATE_SHORE16, + THEATERF_WINTER|THEATERF_TEMPERATE, + "SH16", + TXT_WATER, + LAND_ROCK, + 3,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Shore17( + TEMPLATE_SHORE17, + THEATERF_WINTER|THEATERF_TEMPERATE|THEATERF_DESERT, + "SH17", + TXT_WATER, + LAND_WATER, + 2,2, + LAND_WATER, + NULL +); +static TemplateTypeClass const Shore18( + TEMPLATE_SHORE18, + THEATERF_WINTER|THEATERF_TEMPERATE|THEATERF_DESERT, + "SH18", + TXT_WATER, + LAND_WATER, + 2,2, + LAND_WATER, + NULL +); +static TemplateTypeClass const Shore19( + TEMPLATE_SHORE19, + THEATERF_DESERT, + "SH19", + TXT_WATER, + LAND_ROCK, + 3,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Shore20( + TEMPLATE_SHORE20, + THEATERF_DESERT, + "SH20", + TXT_WATER, + LAND_ROCK, + 4,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Shore21( + TEMPLATE_SHORE21, + THEATERF_DESERT, + "SH21", + TXT_WATER, + LAND_ROCK, + 3,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Shore22( + TEMPLATE_SHORE22, + THEATERF_DESERT, + "SH22", + TXT_WATER, + LAND_ROCK, + 6,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Shore23( + TEMPLATE_SHORE23, + THEATERF_DESERT, + "SH23", + TXT_WATER, + LAND_ROCK, + 2,2, + LAND_CLEAR, + (char const *)_slope01 +); +static TemplateTypeClass const Shore24( + TEMPLATE_SHORE24, + THEATERF_DESERT, + "SH24", + TXT_WATER, + LAND_ROCK, + 3,3, + LAND_CLEAR, + (char const *)_slope000001 +); +static TemplateTypeClass const Shore25( + TEMPLATE_SHORE25, + THEATERF_DESERT, + "SH25", + TXT_WATER, + LAND_ROCK, + 3,2, + LAND_CLEAR, + (char const *)_slope0001 +); +static TemplateTypeClass const Shore26( + TEMPLATE_SHORE26, + THEATERF_DESERT, + "SH26", + TXT_WATER, + LAND_ROCK, + 3,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore27( + TEMPLATE_SHORE27, + THEATERF_DESERT, + "SH27", + TXT_WATER, + LAND_ROCK, + 4,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore28( + TEMPLATE_SHORE28, + THEATERF_DESERT, + "SH28", + TXT_WATER, + LAND_ROCK, + 3,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore29( + TEMPLATE_SHORE29, + THEATERF_DESERT, + "SH29", + TXT_WATER, + LAND_ROCK, + 6,2, + LAND_CLEAR, + (char const *)_slope00000001 +); +static TemplateTypeClass const Shore30( + TEMPLATE_SHORE30, + THEATERF_DESERT, + "SH30", + TXT_WATER, + LAND_ROCK, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore31( + TEMPLATE_SHORE31, + THEATERF_DESERT, + "SH31", + TXT_WATER, + LAND_ROCK, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore32( + TEMPLATE_SHORE32, + THEATERF_TEMPERATE|THEATERF_WINTER, + "SH32", + TXT_WATER, + LAND_CLEAR, + 3,3, + LAND_WATER, + (char const *)_slope1 +); +static TemplateTypeClass const Shore33( + TEMPLATE_SHORE33, + THEATERF_TEMPERATE|THEATERF_WINTER, + "SH33", + TXT_WATER, + LAND_CLEAR, + 3,3, + LAND_WATER, + (char const *)_slope001 +); +static TemplateTypeClass const Shore34( + TEMPLATE_SHORE34, + THEATERF_TEMPERATE|THEATERF_WINTER, + "SH34", + TXT_WATER, + LAND_CLEAR, + 3,3, + LAND_WATER, + (char const *)_slope001001001 +); +static TemplateTypeClass const Shore35( + TEMPLATE_SHORE35, + THEATERF_TEMPERATE|THEATERF_WINTER, + "SH35", + TXT_WATER, + LAND_CLEAR, + 3,3, + LAND_WATER, + (char const *)_slope1001001 +); +static TemplateTypeClass const Shore36( + TEMPLATE_SHORE36, + THEATERF_DESERT, + "SH36", + TXT_WATER, + LAND_CLEAR, + 1,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore37( + TEMPLATE_SHORE37, + THEATERF_DESERT, + "SH37", + TXT_WATER, + LAND_CLEAR, + 1,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore38( + TEMPLATE_SHORE38, + THEATERF_DESERT, + "SH38", + TXT_WATER, + LAND_CLEAR, + 1,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore39( + TEMPLATE_SHORE39, + THEATERF_DESERT, + "SH39", + TXT_WATER, + LAND_CLEAR, + 1,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore40( + TEMPLATE_SHORE40, + THEATERF_DESERT, + "SH40", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_CLEAR, + (char const *)_slope1 +); +static TemplateTypeClass const Shore41( + TEMPLATE_SHORE41, + THEATERF_DESERT, + "SH41", + TXT_WATER, + LAND_CLEAR, + 3,3, + LAND_WATER, + (char const *)_slope1101101 +); +static TemplateTypeClass const Shore42( + TEMPLATE_SHORE42, + THEATERF_DESERT, + "SH42", + TXT_WATER, + LAND_WATER, + 1,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore43( + TEMPLATE_SHORE43, + THEATERF_DESERT, + "SH43", + TXT_WATER, + LAND_WATER, + 1,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore44( + TEMPLATE_SHORE44, + THEATERF_DESERT, + "SH44", + TXT_WATER, + LAND_WATER, + 1,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore45( + TEMPLATE_SHORE45, + THEATERF_DESERT, + "SH45", + TXT_WATER, + LAND_WATER, + 1,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore46( + TEMPLATE_SHORE46, + THEATERF_DESERT, + "SH46", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_CLEAR, + (char const *)_slope1101 +); +static TemplateTypeClass const Shore47( + TEMPLATE_SHORE47, + THEATERF_DESERT, + "SH47", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore48( + TEMPLATE_SHORE48, + THEATERF_DESERT, + "SH48", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore49( + TEMPLATE_SHORE49, + THEATERF_DESERT, + "SH49", + TXT_WATER, + LAND_WATER, + 3,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore50( + TEMPLATE_SHORE50, + THEATERF_DESERT, + "SH50", + TXT_WATER, + LAND_WATER, + 4,3, + LAND_CLEAR, + (char const *)_slope00000001 +); +static TemplateTypeClass const Shore51( + TEMPLATE_SHORE51, + THEATERF_DESERT, + "SH51", + TXT_WATER, + LAND_WATER, + 4,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore52( + TEMPLATE_SHORE52, + THEATERF_DESERT, + "SH52", + TXT_WATER, + LAND_WATER, + 4,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore53( + TEMPLATE_SHORE53, + THEATERF_DESERT, + "SH53", + TXT_WATER, + LAND_WATER, + 4,3, + LAND_CLEAR, + (char const *)_slope11101 +); +static TemplateTypeClass const Shore54( + TEMPLATE_SHORE54, + THEATERF_DESERT, + "SH54", + TXT_WATER, + LAND_WATER, + 3,2, + LAND_CLEAR, + (char const *)_slope1 +); +static TemplateTypeClass const Shore55( + TEMPLATE_SHORE55, + THEATERF_DESERT, + "SH55", + TXT_WATER, + LAND_WATER, + 3,2, + LAND_CLEAR, + (char const *)_slope001 +); +static TemplateTypeClass const Shore56( + TEMPLATE_SHORE56, + THEATERF_DESERT, + "SH56", + TXT_WATER, + LAND_WATER, + 3,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore57( + TEMPLATE_SHORE57, + THEATERF_DESERT, + "SH57", + TXT_WATER, + LAND_WATER, + 3,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore58( + TEMPLATE_SHORE58, + THEATERF_DESERT, + "SH58", + TXT_WATER, + LAND_WATER, + 2,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore59( + TEMPLATE_SHORE59, + THEATERF_DESERT, + "SH59", + TXT_WATER, + LAND_WATER, + 2,3, + LAND_CLEAR, + (char const *)_slope1 +); +static TemplateTypeClass const Shore60( + TEMPLATE_SHORE60, + THEATERF_DESERT, + "SH60", + TXT_WATER, + LAND_WATER, + 2,3, + LAND_CLEAR, + (char const *)_slope000101 +); +static TemplateTypeClass const Shore61( + TEMPLATE_SHORE61, + THEATERF_DESERT, + "SH61", + TXT_WATER, + LAND_WATER, + 2,3, + LAND_CLEAR, + (char const *)_slope01 +); +static TemplateTypeClass const Shore62( + TEMPLATE_SHORE62, + THEATERF_DESERT, + "SH62", + TXT_WATER, + LAND_WATER, + 6,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Shore63( + TEMPLATE_SHORE63, + THEATERF_DESERT, + "SH63", + TXT_WATER, + LAND_WATER, + 4,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Boulder1( + TEMPLATE_BOULDER1, + THEATERF_WINTER|THEATERF_TEMPERATE|THEATERF_DESERT, + "B1", + TXT_SLOPE, + LAND_ROCK, + 1,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Boulder2( + TEMPLATE_BOULDER2, + THEATERF_WINTER|THEATERF_TEMPERATE|THEATERF_DESERT, + "B2", + TXT_SLOPE, + LAND_ROCK, + 2,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Boulder3( + TEMPLATE_BOULDER3, + THEATERF_WINTER|THEATERF_TEMPERATE, + "B3", + TXT_SLOPE, + LAND_ROCK, + 3,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Boulder4( + TEMPLATE_BOULDER4, + THEATERF_TEMPERATE, + "B4", + TXT_SLOPE, + LAND_ROCK, + 1,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Boulder5( + TEMPLATE_BOULDER5, + THEATERF_TEMPERATE, + "B5", + TXT_SLOPE, + LAND_ROCK, + 1,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Boulder6( + TEMPLATE_BOULDER6, + THEATERF_TEMPERATE, + "B6", + TXT_SLOPE, + LAND_ROCK, + 1,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope1( + TEMPLATE_SLOPE1, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S01", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_CLEAR, + (char const *)_slope001 +); +static TemplateTypeClass const Slope2( + TEMPLATE_SLOPE2, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S02", + TXT_SLOPE, + LAND_ROCK, + 2,3, + LAND_CLEAR, + (char const *)_slope01 +); +static TemplateTypeClass const Slope3( + TEMPLATE_SLOPE3, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S03", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope4( + TEMPLATE_SLOPE4, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S04", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope5( + TEMPLATE_SLOPE5, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S05", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope6( + TEMPLATE_SLOPE6, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S06", + TXT_SLOPE, + LAND_ROCK, + 2,3, + LAND_CLEAR, + (char const *)_slope1 +); +static TemplateTypeClass const Slope7( + TEMPLATE_SLOPE7, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S07", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope8( + TEMPLATE_SLOPE8, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S08", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_CLEAR, + (char const *)_slope01 +); +static TemplateTypeClass const Slope9( + TEMPLATE_SLOPE9, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S09", + TXT_SLOPE, + LAND_ROCK, + 3,2, + LAND_CLEAR, + (char const *)_slope0001 +); +static TemplateTypeClass const Slope10( + TEMPLATE_SLOPE10, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S10", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope11( + TEMPLATE_SLOPE11, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S11", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope12( + TEMPLATE_SLOPE12, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S12", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope13( + TEMPLATE_SLOPE13, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S13", + TXT_SLOPE, + LAND_ROCK, + 3,2, + LAND_CLEAR, + (char const *)_slope000001 +); +static TemplateTypeClass const Slope14( + TEMPLATE_SLOPE14, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S14", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_CLEAR, + (char const *)_slope0111 +); +static TemplateTypeClass const Slope15( + TEMPLATE_SLOPE15, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S15", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_CLEAR, + (char const *)_slope01 +); +static TemplateTypeClass const Slope16( + TEMPLATE_SLOPE16, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S16", + TXT_SLOPE, + LAND_ROCK, + 2,3, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope17( + TEMPLATE_SLOPE17, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S17", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope18( + TEMPLATE_SLOPE18, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S18", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope19( + TEMPLATE_SLOPE19, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S19", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope20( + TEMPLATE_SLOPE20, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S20", + TXT_SLOPE, + LAND_ROCK, + 2,3, + LAND_CLEAR, + (char const *)_slope000001 +); +static TemplateTypeClass const Slope21( + TEMPLATE_SLOPE21, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S21", + TXT_SLOPE, + LAND_ROCK, + 1,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope22( + TEMPLATE_SLOPE22, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S22", + TXT_SLOPE, + LAND_ROCK, + 2,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope23( + TEMPLATE_SLOPE23, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S23", + TXT_SLOPE, + LAND_ROCK, + 3,2, + LAND_CLEAR, + (char const *)_slope000001 +); +static TemplateTypeClass const Slope24( + TEMPLATE_SLOPE24, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S24", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope25( + TEMPLATE_SLOPE25, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S25", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope26( + TEMPLATE_SLOPE26, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S26", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope27( + TEMPLATE_SLOPE27, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S27", + TXT_SLOPE, + LAND_ROCK, + 3,2, + LAND_CLEAR, + (char const *)_slope0011 +); +static TemplateTypeClass const Slope28( + TEMPLATE_SLOPE28, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S28", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope29( + TEMPLATE_SLOPE29, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S29", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope30( + TEMPLATE_SLOPE30, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S30", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope31( + TEMPLATE_SLOPE31, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S31", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope32( + TEMPLATE_SLOPE32, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S32", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope33( + TEMPLATE_SLOPE33, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S33", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope34( + TEMPLATE_SLOPE34, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S34", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope35( + TEMPLATE_SLOPE35, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S35", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope36( + TEMPLATE_SLOPE36, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S36", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope37( + TEMPLATE_SLOPE37, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S37", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Slope38( + TEMPLATE_SLOPE38, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "S38", + TXT_SLOPE, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Brush1( + TEMPLATE_BRUSH1, + THEATERF_DESERT, + "BR1", + TXT_BRUSH, + LAND_ROCK, + 1,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Brush2( + TEMPLATE_BRUSH2, + THEATERF_DESERT, + "BR2", + TXT_BRUSH, + LAND_ROCK, + 1,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Brush3( + TEMPLATE_BRUSH3, + THEATERF_DESERT, + "BR3", + TXT_BRUSH, + LAND_ROCK, + 1,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Brush4( + TEMPLATE_BRUSH4, + THEATERF_DESERT, + "BR4", + TXT_BRUSH, + LAND_ROCK, + 1,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Brush5( + TEMPLATE_BRUSH5, + THEATERF_DESERT, + "BR5", + TXT_BRUSH, + LAND_ROCK, + 1,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Brush6( + TEMPLATE_BRUSH6, + THEATERF_DESERT, + "BR6", + TXT_BRUSH, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Brush7( + TEMPLATE_BRUSH7, + THEATERF_DESERT, + "BR7", + TXT_BRUSH, + LAND_ROCK, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Brush8( + TEMPLATE_BRUSH8, + THEATERF_DESERT, + "BR8", + TXT_BRUSH, + LAND_ROCK, + 3,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Brush9( + TEMPLATE_BRUSH9, + THEATERF_DESERT, + "BR9", + TXT_BRUSH, + LAND_ROCK, + 3,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Brush10( + TEMPLATE_BRUSH10, + THEATERF_DESERT, + "BR10", + TXT_BRUSH, + LAND_ROCK, + 2,1, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Patch1( + TEMPLATE_PATCH1, + THEATERF_TEMPERATE|THEATERF_DESERT, + "P01", + TXT_PATCH, + LAND_CLEAR, + 1,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch2( + TEMPLATE_PATCH2, + THEATERF_TEMPERATE|THEATERF_DESERT, + "P02", + TXT_PATCH, + LAND_ROCK, + 1,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch3( + TEMPLATE_PATCH3, + THEATERF_TEMPERATE|THEATERF_DESERT, + "P03", + TXT_PATCH, + LAND_CLEAR, + 1,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch4( + TEMPLATE_PATCH4, + THEATERF_TEMPERATE|THEATERF_DESERT, + "P04", + TXT_PATCH, + LAND_ROCK, + 1,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch5( + TEMPLATE_PATCH5, + THEATERF_DESERT, + "P05", + TXT_PATCH, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch6( + TEMPLATE_PATCH6, + THEATERF_DESERT, + "P06", + TXT_PATCH, + LAND_CLEAR, + 6,4, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch7( + TEMPLATE_PATCH7, + THEATERF_WINTER|THEATERF_TEMPERATE|THEATERF_DESERT, + "P07", + TXT_PATCH, + LAND_CLEAR, + 4,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch8( + TEMPLATE_PATCH8, + THEATERF_WINTER|THEATERF_TEMPERATE|THEATERF_DESERT, + "P08", + TXT_PATCH, + LAND_CLEAR, + 3,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch13( + TEMPLATE_PATCH13, + THEATERF_WINTER|THEATERF_TEMPERATE, + "P13", + TXT_PATCH, + LAND_CLEAR, + 3,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch14( + TEMPLATE_PATCH14, + THEATERF_WINTER|THEATERF_TEMPERATE, + "P14", + TXT_PATCH, + LAND_CLEAR, + 2,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch15( + TEMPLATE_PATCH15, + THEATERF_WINTER|THEATERF_TEMPERATE, + "P15", + TXT_PATCH, + LAND_CLEAR, + 1,1, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch16( + TEMPLATE_PATCH16, + THEATERF_WINTER, + "P16", + TXT_PATCH, + LAND_CLEAR, + 2,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch17( + TEMPLATE_PATCH17, + THEATERF_WINTER, + "P17", + TXT_PATCH, + LAND_CLEAR, + 4,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch18( + TEMPLATE_PATCH18, + THEATERF_WINTER, + "P18", + TXT_PATCH, + LAND_CLEAR, + 4,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch19( + TEMPLATE_PATCH19, + THEATERF_WINTER, + "P19", + TXT_PATCH, + LAND_CLEAR, + 4,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Patch20( + TEMPLATE_PATCH20, + THEATERF_WINTER, + "P20", + TXT_PATCH, + LAND_CLEAR, + 4,3, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const River1( + TEMPLATE_RIVER1, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV01", + TXT_RIVER, + LAND_WATER, + 5,4, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const River2( + TEMPLATE_RIVER2, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV02", + TXT_RIVER, + LAND_WATER, + 5,3, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River3( + TEMPLATE_RIVER3, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV03", + TXT_RIVER, + LAND_WATER, + 4,4, + LAND_CLEAR, + (char const *)_slope00000001 +); +static TemplateTypeClass const River4( + TEMPLATE_RIVER4, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV04", + TXT_RIVER, + LAND_WATER, + 4,4, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River5( + TEMPLATE_RIVER5, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV05", + TXT_RIVER, + LAND_WATER, + 3,3, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River6( + TEMPLATE_RIVER6, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV06", + TXT_RIVER, + LAND_WATER, + 3,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River7( + TEMPLATE_RIVER7, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV07", + TXT_RIVER, + LAND_WATER, + 3,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River8( + TEMPLATE_RIVER8, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV08", + TXT_RIVER, + LAND_WATER, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River9( + TEMPLATE_RIVER9, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV09", + TXT_RIVER, + LAND_WATER, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River10( + TEMPLATE_RIVER10, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV10", + TXT_RIVER, + LAND_WATER, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River11( + TEMPLATE_RIVER11, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV11", + TXT_RIVER, + LAND_WATER, + 2,2, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River12( + TEMPLATE_RIVER12, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV12", + TXT_RIVER, + LAND_WATER, + 3,4, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River13( + TEMPLATE_RIVER13, + THEATERF_WINTER|THEATERF_TEMPERATE, + "RV13", + TXT_RIVER, + LAND_WATER, + 4,4, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River14( + TEMPLATE_RIVER14, + THEATERF_DESERT, + "RV14", + TXT_RIVER, + LAND_WATER, + 4,3, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River15( + TEMPLATE_RIVER15, + THEATERF_DESERT, + "RV15", + TXT_RIVER, + LAND_WATER, + 4,3, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River16( + TEMPLATE_RIVER16, + THEATERF_DESERT, + "RV16", + TXT_RIVER, + LAND_WATER, + 6,4, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River17( + TEMPLATE_RIVER17, + THEATERF_DESERT, + "RV17", + TXT_RIVER, + LAND_WATER, + 6,5, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River18( + TEMPLATE_RIVER18, + THEATERF_DESERT, + "RV18", + TXT_RIVER, + LAND_WATER, + 4,4, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River19( + TEMPLATE_RIVER19, + THEATERF_DESERT, + "RV19", + TXT_RIVER, + LAND_WATER, + 4,4, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River20( + TEMPLATE_RIVER20, + THEATERF_DESERT, + "RV20", + TXT_RIVER, + LAND_WATER, + 6,8, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River21( + TEMPLATE_RIVER21, + THEATERF_DESERT, + "RV21", + TXT_RIVER, + LAND_WATER, + 5,8, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River22( + TEMPLATE_RIVER22, + THEATERF_DESERT, + "RV22", + TXT_RIVER, + LAND_WATER, + 3,3, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River23( + TEMPLATE_RIVER23, + THEATERF_DESERT, + "RV23", + TXT_RIVER, + LAND_WATER, + 3,3, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River24( + TEMPLATE_RIVER24, + THEATERF_DESERT, + "RV24", + TXT_RIVER, + LAND_WATER, + 3,3, + LAND_ROCK, + NULL +); +static TemplateTypeClass const River25( + TEMPLATE_RIVER25, + THEATERF_DESERT, + "RV25", + TXT_RIVER, + LAND_WATER, + 3,3, + LAND_ROCK, + NULL +); +static TemplateTypeClass const Ford1( + TEMPLATE_FORD1, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "FORD1", + TXT_RIVER, + LAND_WATER, + 3,3, + LAND_CLEAR, + (char const *)_slope001111001 +); +static TemplateTypeClass const Ford2( + TEMPLATE_FORD2, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "FORD2", + TXT_RIVER, + LAND_WATER, + 3,3, + LAND_CLEAR, + (char const *)_slope111010011 +); +static TemplateTypeClass const Falls1( + TEMPLATE_FALLS1, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "FALLS1", + TXT_RIVER, + LAND_WATER, + 3,3, + LAND_CLEAR, + (char const *)_slope1 +); +static TemplateTypeClass const Falls2( + TEMPLATE_FALLS2, + THEATERF_WINTER|THEATERF_DESERT|THEATERF_TEMPERATE, + "FALLS2", + TXT_RIVER, + LAND_WATER, + 3,2, + LAND_CLEAR, + NULL +); +static TemplateTypeClass const Bridge1( + TEMPLATE_BRIDGE1, + THEATERF_WINTER|THEATERF_TEMPERATE, + "BRIDGE1", + TXT_RIVER, + LAND_WATER, + 4,4, + LAND_CLEAR, + (char const *)_slope00110010010011 +); +static TemplateTypeClass const Bridge1d( + TEMPLATE_BRIDGE1D, + THEATERF_WINTER|THEATERF_TEMPERATE, + "BRIDGE1D", + TXT_RIVER, + LAND_WATER, + 4,4, + LAND_CLEAR, + (char const *)_slope00110000000011 +); +static TemplateTypeClass const Bridge2( + TEMPLATE_BRIDGE2, + THEATERF_WINTER|THEATERF_TEMPERATE, + "BRIDGE2", + TXT_RIVER, + LAND_WATER, + 5,5, + LAND_CLEAR, + (char const *)_slope1100001000001000001100011 +); +static TemplateTypeClass const Bridge2d( + TEMPLATE_BRIDGE2D, + THEATERF_WINTER|THEATERF_TEMPERATE, + "BRIDGE2D", + TXT_RIVER, + LAND_WATER, + 5,5, + LAND_CLEAR, + (char const *)_slope1100000000000000001100011 +); +static TemplateTypeClass const Bridge3( + TEMPLATE_BRIDGE3, + THEATERF_DESERT, + "BRIDGE3", + TXT_RIVER, + LAND_WATER, + 6,5, + LAND_CLEAR, + (char const *)_slope00011010010100100001000011 +); +static TemplateTypeClass const Bridge3d( + TEMPLATE_BRIDGE3D, + THEATERF_DESERT, + "BRIDGE3D", + TXT_RIVER, + LAND_WATER, + 6,5, + LAND_CLEAR, + (char const *)_slope00011010000100000001000011 +); +static TemplateTypeClass const Bridge4( + TEMPLATE_BRIDGE4, + THEATERF_DESERT, + "BRIDGE4", + TXT_RIVER, + LAND_WATER, + 6,4, + LAND_CLEAR, + (char const *)_slope01000000100000010000001 +); +static TemplateTypeClass const Bridge4d( + TEMPLATE_BRIDGE4D, + THEATERF_DESERT, + "BRIDGE4D", + TXT_RIVER, + LAND_WATER, + 6,4, + LAND_CLEAR, + (char const *)_slope01000000000000000000001 +); + +TemplateTypeClass const * const TemplateTypeClass::Pointers[TEMPLATE_COUNT] = { + &Clear, // TEMPLATE_CLEAR1 + &Water, // TEMPLATE_WATER + &Water2, // TEMPLATE_WATER2 + &Shore1, // TEMPLATE_SHORE1 + &Shore2, // TEMPLATE_SHORE2 + &Shore3, // TEMPLATE_SHORE3 + &Shore4, // TEMPLATE_SHORE4 + &Shore5, // TEMPLATE_SHORE5 + &Shore11, // TEMPLATE_SHORE11 + &Shore12, // TEMPLATE_SHORE12 + &Shore13, // TEMPLATE_SHORE13 + &Shore14, // TEMPLATE_SHORE14 + &Shore15, // TEMPLATE_SHORE15 + &Slope1, // TEMPLATE_SLOPE1 + &Slope2, // TEMPLATE_SLOPE2 + &Slope3, // TEMPLATE_SLOPE3 + &Slope4, // TEMPLATE_SLOPE4 + &Slope5, // TEMPLATE_SLOPE5 + &Slope6, // TEMPLATE_SLOPE6 + &Slope7, // TEMPLATE_SLOPE7 + &Slope8, // TEMPLATE_SLOPE8 + &Slope9, // TEMPLATE_SLOPE9 + &Slope10, // TEMPLATE_SLOPE10 + &Slope11, // TEMPLATE_SLOPE11 + &Slope12, // TEMPLATE_SLOPE12 + &Slope13, // TEMPLATE_SLOPE13 + &Slope14, // TEMPLATE_SLOPE14 + &Slope15, // TEMPLATE_SLOPE15 + &Slope16, // TEMPLATE_SLOPE16 + &Slope17, // TEMPLATE_SLOPE17 + &Slope18, // TEMPLATE_SLOPE18 + &Slope19, // TEMPLATE_SLOPE19 + &Slope20, // TEMPLATE_SLOPE20 + &Slope21, // TEMPLATE_SLOPE21 + &Slope22, // TEMPLATE_SLOPE22 + &Slope23, // TEMPLATE_SLOPE23 + &Slope24, // TEMPLATE_SLOPE24 + &Slope25, // TEMPLATE_SLOPE25 + &Slope26, // TEMPLATE_SLOPE26 + &Slope27, // TEMPLATE_SLOPE27 + &Slope28, // TEMPLATE_SLOPE28 + &Slope29, // TEMPLATE_SLOPE29 + &Slope30, // TEMPLATE_SLOPE30 + &Slope31, // TEMPLATE_SLOPE31 + &Slope32, // TEMPLATE_SLOPE32 + &Slope33, // TEMPLATE_SLOPE33 + &Slope34, // TEMPLATE_SLOPE34 + &Slope35, // TEMPLATE_SLOPE35 + &Slope36, // TEMPLATE_SLOPE36 + &Slope37, // TEMPLATE_SLOPE37 + &Slope38, // TEMPLATE_SLOPE38 + &Shore32, // TEMPLATE_SHORE32 + &Shore33, // TEMPLATE_SHORE33 + &Shore20, // TEMPLATE_SHORE20 + &Shore21, // TEMPLATE_SHORE21 + &Shore22, // TEMPLATE_SHORE22 + &Shore23, // TEMPLATE_SHORE23 + &Brush1, // TEMPLATE_BRUSH1 + &Brush2, // TEMPLATE_BRUSH2 + &Brush3, // TEMPLATE_BRUSH3 + &Brush4, // TEMPLATE_BRUSH4 + &Brush5, // TEMPLATE_BRUSH5 + &Brush6, // TEMPLATE_BRUSH6 + &Brush7, // TEMPLATE_BRUSH7 + &Brush8, // TEMPLATE_BRUSH8 + &Brush9, // TEMPLATE_BRUSH9 + &Brush10, // TEMPLATE_BRUSH10 + &Patch1, // TEMPLATE_PATCH1 + &Patch2, // TEMPLATE_PATCH2 + &Patch3, // TEMPLATE_PATCH3 + &Patch4, // TEMPLATE_PATCH4 + &Patch5, // TEMPLATE_PATCH5 + &Patch6, // TEMPLATE_PATCH6 + &Patch7, // TEMPLATE_PATCH7 + &Patch8, // TEMPLATE_PATCH8 + &Shore16, // TEMPLATE_SHORE16 + &Shore17, // TEMPLATE_SHORE17 + &Shore18, // TEMPLATE_SHORE18 + &Shore19, // TEMPLATE_SHORE19 + &Patch13, // TEMPLATE_PATCH13 + &Patch14, // TEMPLATE_PATCH14 + &Patch15, // TEMPLATE_PATCH15 + &Boulder1, // TEMPLATE_BOULDER1 + &Boulder2, // TEMPLATE_BOULDER2 + &Boulder3, // TEMPLATE_BOULDER3 + &Boulder4, // TEMPLATE_BOULDER4 + &Boulder5, // TEMPLATE_BOULDER5 + &Boulder6, // TEMPLATE_BOULDER6 + &Shore6, // TEMPLATE_SHORE6 + &Shore7, // TEMPLATE_SHORE7 + &Shore8, // TEMPLATE_SHORE8 + &Shore9, // TEMPLATE_SHORE9 + &Shore10, // TEMPLATE_SHORE10 + + &Road1, // TEMPLATE_ROAD1 + &Road2, // TEMPLATE_ROAD2 + &Road3, // TEMPLATE_ROAD3 + &Road4, // TEMPLATE_ROAD4 + &Road5, // TEMPLATE_ROAD5 + &Road6, // TEMPLATE_ROAD6 + &Road7, // TEMPLATE_ROAD7 + &Road8, // TEMPLATE_ROAD8 + &Road9, // TEMPLATE_ROAD9 + &Road10, // TEMPLATE_ROAD10 + &Road11, // TEMPLATE_ROAD11 + &Road12, // TEMPLATE_ROAD12 + &Road13, // TEMPLATE_ROAD13 + &Road14, // TEMPLATE_ROAD14 + &Road15, // TEMPLATE_ROAD15 + &Road16, // TEMPLATE_ROAD16 + &Road17, // TEMPLATE_ROAD17 + &Road18, // TEMPLATE_ROAD18 + &Road19, // TEMPLATE_ROAD19 + &Road20, // TEMPLATE_ROAD20 + &Road21, // TEMPLATE_ROAD21 + &Road22, // TEMPLATE_ROAD22 + &Road23, // TEMPLATE_ROAD23 + &Road24, // TEMPLATE_ROAD24 + &Road25, // TEMPLATE_ROAD25 + &Road26, // TEMPLATE_ROAD26 + &Road27, // TEMPLATE_ROAD27 + &Road28, // TEMPLATE_ROAD28 + &Road29, // TEMPLATE_ROAD29 + &Road30, // TEMPLATE_ROAD30 + &Road31, // TEMPLATE_ROAD31 + &Road32, // TEMPLATE_ROAD32 + &Road33, // TEMPLATE_ROAD33 + &Road34, // TEMPLATE_ROAD34 + &Road35, // TEMPLATE_ROAD35 + &Road36, // TEMPLATE_ROAD36 + &Road37, // TEMPLATE_ROAD37 + &Road38, // TEMPLATE_ROAD38 + &Road39, // TEMPLATE_ROAD39 + &Road40, // TEMPLATE_ROAD40 + &Road41, // TEMPLATE_ROAD41 + &Road42, // TEMPLATE_ROAD42 + &Road43, // TEMPLATE_ROAD43 + + &River1, // TEMPLATE_RIVER1 + &River2, // TEMPLATE_RIVER2 + &River3, // TEMPLATE_RIVER3 + &River4, // TEMPLATE_RIVER4 + &River5, // TEMPLATE_RIVER5 + &River6, // TEMPLATE_RIVER6 + &River7, // TEMPLATE_RIVER7 + &River8, // TEMPLATE_RIVER8 + &River9, // TEMPLATE_RIVER9 + &River10, // TEMPLATE_RIVER10 + &River11, // TEMPLATE_RIVER11 + &River12, // TEMPLATE_RIVER12 + &River13, // TEMPLATE_RIVER13 + &River14, // TEMPLATE_RIVER14 + &River15, // TEMPLATE_RIVER15 + &River16, // TEMPLATE_RIVER16 + &River17, // TEMPLATE_RIVER17 + &River18, // TEMPLATE_RIVER18 + &River19, // TEMPLATE_RIVER19 + &River20, // TEMPLATE_RIVER20 + &River21, // TEMPLATE_RIVER21 + &River22, // TEMPLATE_RIVER22 + &River23, // TEMPLATE_RIVER23 + &River24, // TEMPLATE_RIVER24 + &River25, // TEMPLATE_RIVER25 + &Ford1, // TEMPLATE_FORD1 + &Ford2, // TEMPLATE_FORD2 + &Falls1, // TEMPLATE_FALLS1 + &Falls2, // TEMPLATE_FALLS2 + &Bridge1, // TEMPLATE_BRIDGE1 + &Bridge1d, // TEMPLATE_BRIDGE1D + &Bridge2, // TEMPLATE_BRIDGE2 + &Bridge2d, // TEMPLATE_BRIDGE2D + &Bridge3, // TEMPLATE_BRIDGE3 + &Bridge3d, // TEMPLATE_BRIDGE3D + &Bridge4, // TEMPLATE_BRIDGE4 + &Bridge4d, // TEMPLATE_BRIDGE4D + + &Shore24, // TEMPLATE_SHORE24 + &Shore25, // TEMPLATE_SHORE25 + &Shore26, // TEMPLATE_SHORE26 + &Shore27, // TEMPLATE_SHORE27 + &Shore28, // TEMPLATE_SHORE28 + &Shore29, // TEMPLATE_SHORE29 + &Shore30, // TEMPLATE_SHORE30 + &Shore31, // TEMPLATE_SHORE31 + + &Patch16, // TEMPLATE_PATCH16 + &Patch17, // TEMPLATE_PATCH17 + &Patch18, // TEMPLATE_PATCH18 + &Patch19, // TEMPLATE_PATCH19 + &Patch20, // TEMPLATE_PATCH20 + + &Shore34, // TEMPLATE_SHORE34 + &Shore35, // TEMPLATE_SHORE35 + &Shore36, // TEMPLATE_SHORE36 + &Shore37, // TEMPLATE_SHORE37 + &Shore38, // TEMPLATE_SHORE38 + &Shore39, // TEMPLATE_SHORE39 + &Shore40, // TEMPLATE_SHORE40 + &Shore41, // TEMPLATE_SHORE41 + &Shore42, // TEMPLATE_SHORE42 + &Shore43, // TEMPLATE_SHORE43 + &Shore44, // TEMPLATE_SHORE44 + &Shore45, // TEMPLATE_SHORE45 + + &Shore46, // TEMPLATE_SHORE46 + &Shore47, // TEMPLATE_SHORE47 + &Shore48, // TEMPLATE_SHORE48 + &Shore49, // TEMPLATE_SHORE49 + &Shore50, // TEMPLATE_SHORE50 + &Shore51, // TEMPLATE_SHORE51 + &Shore52, // TEMPLATE_SHORE52 + &Shore53, // TEMPLATE_SHORE53 + &Shore54, // TEMPLATE_SHORE54 + &Shore55, // TEMPLATE_SHORE55 + &Shore56, // TEMPLATE_SHORE56 + &Shore57, // TEMPLATE_SHORE57 + &Shore58, // TEMPLATE_SHORE58 + &Shore59, // TEMPLATE_SHORE59 + &Shore60, // TEMPLATE_SHORE60 + &Shore61, // TEMPLATE_SHORE61 + + &Shore62, // TEMPLATE_SHORE62 + &Shore63, // TEMPLATE_SHORE63 +}; + + +/*********************************************************************************************** + * TemplateTypeClass::TemplateTypeClass -- Constructor for template type objects. * + * * + * This is the constructor for the template types. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1994 JLB : Created. * + *=============================================================================================*/ +TemplateTypeClass::TemplateTypeClass(TemplateType iconset, int theater, + char const *ininame, int fullname, LandType land, + int width, int height, LandType altland, char const *alticons ) : + ObjectTypeClass(false, false, false, true, false, false, true, true, fullname, ininame, ARMOR_NONE, 0) +{ + Theater = theater; + AltIcons = alticons; + AltLand = altland; + Type = iconset; + Land = land; + Width = width; + Height = height; +} + + +/*********************************************************************************************** + * TemplateTypeClass::From_Name -- Determine template from ASCII name. * + * * + * This routine is used to determine the template number given only * + * an ASCII representation. The scenario loader uses this routine * + * to construct the map from the INI control file. * + * * + * INPUT: name -- Pointer to the ASCII name of the template. * + * * + * OUTPUT: Returns with the template number. If the name had no match, * + * then returns with TEMPLATE_NONE. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +TemplateType TemplateTypeClass::From_Name(char const *name) +{ + if (name) { + for (TemplateType index = TEMPLATE_FIRST; index < TEMPLATE_COUNT; index++) { + if (stricmp(As_Reference(index).IniName, name) == 0) { + return(index); + } + } + } + return(TEMPLATE_NONE); +} + + +/*********************************************************************************************** + * TemplateTypeClass::Occupy_List -- Determines occupation list. * + * * + * This routine is used to examine the template map and build an * + * occupation list. This list is used to render a template cursor as * + * well as placement of icon numbers. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a pointer to the template occupation list. * + * * + * WARNINGS: The return pointer is valid only until the next time that * + * this routine is called. * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +short const * TemplateTypeClass::Occupy_List(bool) const +{ + static short _occupy[13*8+5]; + unsigned char map[13*8]; + short *ptr; + int index; + + Mem_Copy(Get_Icon_Set_Map(Get_Image_Data()), map, Width*Height); + + ptr = &_occupy[0]; + for (index = 0; index < Width*Height; index++) { + if (map[index] != 0xFF) { + *ptr++ = (index % Width) + ((index / Width)*MAP_CELL_W); + } + } + *ptr = REFRESH_EOL; + + return((short const *)&_occupy[0]); +} + + +/*********************************************************************************************** + * TemplateTypeClass::Init -- Loads graphic data for templates. * + * * + * This routine loads the template graphic data for all the template * + * type supported for the specified theater. This routine is called * + * whenever the theater for the scenario is first determined. * + * * + * INPUT: theater -- The theater that the template data is to be * + * loaded for. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine goes to disk! * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + * 06/02/1994 JLB : Only handles iconset loading now (as it should). * + *=============================================================================================*/ +void TemplateTypeClass::Init(TheaterType theater) +{ + //if (theater != LastTheater){ + char fullname[_MAX_FNAME+_MAX_EXT]; // Fully constructed iconset name. + void const * ptr; // Working loaded iconset pointer. + + for (TemplateType index = TEMPLATE_FIRST; index < TEMPLATE_COUNT; index++) { + TemplateTypeClass const & tplate = As_Reference(index); + + ((void const *&)tplate.ImageData) = NULL; + if (tplate.Theater & (1< 3 || h > 3); + if (scale) { + x -= (w/2) * (ICON_PIXEL_W/2); + y -= (h/2) * (ICON_PIXEL_H/2); + } else { + x -= (w/2) * ICON_PIXEL_W; + y -= (h/2) * ICON_PIXEL_H; + } + x += WindowList[window][WINDOWX]<<3; + y += WindowList[window][WINDOWY]; + + Mem_Copy(Get_Icon_Set_Map(Get_Image_Data()), map, Width*Height); + + for (index = 0; index < w*h; index++) { + if (map[index] != 0xFF) { + HidPage.Draw_Stamp(Get_Image_Data(), index, 0, 0, NULL, WINDOW_MAIN); + if (scale) { + + HidPage.Scale((*LogicPage), 0, 0, + x + ((index % w)*(ICON_PIXEL_W/2)), + y + ((index / w)*(ICON_PIXEL_H/2)), + ICON_PIXEL_W, ICON_PIXEL_H, + ICON_PIXEL_W/2, ICON_PIXEL_H/2, (char *)NULL); + + } else { + HidPage.Blit((*LogicPage), 0, 0, x + ((index % w)*(ICON_PIXEL_W)), + y + ((index / w)*(ICON_PIXEL_H)), ICON_PIXEL_W, ICON_PIXEL_H); + } + } + } +} + + +/*********************************************************************************************** + * TemplateTypeClass::Prep_For_Add -- Prepares to add template to scenario. * + * * + * This routine prepares a list of template objects so that the * + * scenario editor can use this list to display a dialog box. The * + * selection of a template object will allow its placement upon the * + * map. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + * 05/28/1994 JLB : Only handles real templates now. * + * 06/04/1994 JLB : Uses map editing interface functions. * + *=============================================================================================*/ +void TemplateTypeClass::Prep_For_Add(void) +{ + for (TemplateType index = TEMPLATE_CLEAR1; index < TEMPLATE_COUNT; index++) { + if (As_Reference(index).Get_Image_Data()) { + Map.Add_To_List(&As_Reference(index)); + } + } +} +#endif + + +/*********************************************************************************************** + * TemplateTypeClass::Create_And_Place -- Creates and places a template object on the map. * + * * + * This support routine is used by the scenario editor to add a template object to the map * + * and to the game. * + * * + * INPUT: cell -- The cell to place the template object. * + * * + * OUTPUT: bool; Was the template object placed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +bool TemplateTypeClass::Create_And_Place(CELL cell, HousesType ) const +{ + if (new TemplateClass(Type, cell)) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TemplateTypeClass::Create_One_Of -- Creates an object of this template type. * + * * + * This routine will create an object of this type. For certain template objects, such * + * as walls, it is actually created as a building. The "building" wall is converted into * + * a template at the moment of placing down on the map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the appropriate object for this template type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * TemplateTypeClass::Create_One_Of(HouseClass *) const +{ + return(new TemplateClass(Type, -1)); +} + + +/*********************************************************************************************** + * TemplateTypeClass::One_Time -- Performs one-time initialization * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void TemplateTypeClass::One_Time(void) +{ +} + + + + diff --git a/CDFILE.CPP b/CDFILE.CPP new file mode 100644 index 0000000..7fdc3f6 --- /dev/null +++ b/CDFILE.CPP @@ -0,0 +1,508 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\cdfile.cpv 2.18 16 Oct 1995 16:48:10 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : CDFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 18, 1994 * + * * + * Last Update : October 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CDFileClass::Clear_Search_Drives -- Removes all record of a search path. * + * CDFileClass::Open -- Opens the file object -- with path search. * + * CDFileClass::Open -- Opens the file wherever it can be found. * + * CDFileClass::Set_Name -- Performs a multiple directory scan to set the filename. * + * CDFileClass::Set_Search_Drives -- Sets a list of search paths for file access. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +//#include "cdfile.h" + +/* +** Pointer to the first search path record. +*/ +CDFileClass::SearchDriveType * CDFileClass::First = 0; + +int CDFileClass::CurrentCDDrive = 0; +int CDFileClass::LastCDDrive = 0; +char CDFileClass::RawPath[512]; + + + +int __cdecl Is_Disk_Inserted(int disk) +{ + struct find_t fb; + char scan[] = "?:\\*.*"; + + scan[0] = 'A' + disk; + return(_dos_findfirst(scan, _A_SUBDIR, &fb) == 0); +} + + +CDFileClass::CDFileClass(char const *filename) : + IsDisabled(false) +{ + Set_Name(filename); + memset (RawPath, 0, sizeof(RawPath)); +} + + +CDFileClass::CDFileClass(void) : + IsDisabled(false) +{ +} + + +/*********************************************************************************************** + * CDFileClass::Open -- Opens the file object -- with path search. * + * * + * This will open the file object, but since the file object could have been constructed * + * with a pathname, this routine will try to find the file first. For files opened for * + * writing, then use the existing filename without performing a path search. * + * * + * INPUT: rights -- The access rights to use when opening the file * + * * + * OUTPUT: bool; Was the open successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int CDFileClass::Open(int rights) +{ + return(RawFileClass::Open(rights)); +} + + +/*********************************************************************************************** + * CDFC::Refresh_Search_Drives -- Updates the search path when a CD changes or is added * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/22/96 9:01AM ST : Created * + *=============================================================================================*/ +void CDFileClass::Refresh_Search_Drives (void) +{ + Clear_Search_Drives(); + Set_Search_Drives(RawPath); +} + + + + + +/*********************************************************************************************** + * CDFileClass::Set_Search_Drives -- Sets a list of search paths for file access. * + * * + * This routine sets up a list of search paths to use when accessing files. The path list * + * is scanned if the file could not be found in the current directory. This is the primary * + * method of supporting CD-ROM drives, but is also useful for scanning network and other * + * directories. The pathlist as passed to this routine is of the same format as the path * + * list used by DOS -- paths are separated by semicolons and need not end in an antivirgule.* + * * + * If a path entry begins with "?:" then the question mark will be replaced with the first * + * CD-ROM drive letter available. If there is no CD-ROM driver detected, then this path * + * entry will be ignored. By using this feature, you can always pass the CD-ROM path * + * specification to this routine and it will not break if the CD-ROM is not loaded (as in * + * the case during development). * + * * + * Here is an example path specification: * + * * + * Set_Search_Drives("DATA;?:\DATA;F:\PROJECT\DATA"); * + * * + * In this example, the current directory will be searched first, followed by a the * + * subdirectory "DATA" located off of the current directory. If not found, then the CD-ROM * + * will be searched in a directory called "\DATA". If not found or the CD-ROM is not * + * present, then it will look to the hard coded path of "F:\PROJECTS\DATA" (maybe a * + * network?). If all of these searches fail, the file system will default to the current * + * directory and let the normal file error system take over. * + * * + * INPUT: pathlist -- Pointer to string of path specifications (separated by semicolons) * + * that will be used to search for files. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + * 05/21/1996 ST : Modified to recognise multiple CD drives * + *=============================================================================================*/ +int CDFileClass::Set_Search_Drives(char * pathlist) +{ + int found = FALSE; + int empty = FALSE; + /* + ** If there is no pathlist to add, then just return. + */ + if (!pathlist) return(0); + + /* + ** Save the path as it was passed in so we can parse it again later. + ** Check for the case where RawPath was passed in. + */ + if (pathlist != RawPath){ + strcat (RawPath, ";"); + strcat (RawPath, pathlist); + } + + char const * ptr = strtok(pathlist, ";"); + while (ptr) { + if (strlen(ptr)){ + + char path[PATH_MAX]; // Working path buffer. + + /* + ** Fixup the path to be legal. Legal is defined as all that is necessary to + ** create a pathname is to append the actual filename submitted to the + ** file system. This means that it must have either a trailing ':' or '\' + ** character. + */ + strcpy(path, ptr); + switch (path[strlen(path)-1]) { + case ':': + case '\\': + break; + + default: + strcat(path, "\\"); + break; + } + + /* + ** If there is a drive letter specified, and this drive letter is '?', then it should + ** be substituted with the CD-ROM drive letter. In the case of no CD-ROM attached, then + ** merely ignore this path entry. + */ + if (strncmp(path, "?:", 2) == 0) { + if (CurrentCDDrive){ + found = TRUE; + /* + ** If the drive has a C&C CD in it then add it to the path + */ + if (Get_CD_Index(CurrentCDDrive, 2*60) >= 0){ + path[0] = CurrentCDDrive + 'A'; + Add_Search_Drive(path); + } + } + /* + ** Find the next path string and resubmit. + */ + ptr = strtok(NULL, ";"); + continue; + } + + found = TRUE; + Add_Search_Drive(path); + } + + /* + ** Find the next path string and resubmit. + */ + ptr = strtok(NULL, ";"); + } + if (!found) return(1); + if (empty) return(2); + return(0); +} + + + +/*********************************************************************************************** + * CDFC::Set_CD_Drive -- sets the current CD drive letter * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/22/96 9:39AM ST : Created * + *=============================================================================================*/ + +void CDFileClass::Set_CD_Drive (int drive) +{ + LastCDDrive = CurrentCDDrive; + CurrentCDDrive = drive; +} + + + +/*********************************************************************************************** + * CDFC::Add_Search_Drive -- Add a new path to the search path list * + * * + * * + * * + * INPUT: path * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/22/96 10:12AM ST : Created * + *=============================================================================================*/ + +void CDFileClass::Add_Search_Drive(char *path) +{ + SearchDriveType *srch; // Working pointer to path object. + /* + ** Allocate a record structure. + */ + srch = new SearchDriveType; + + /* + ** Attach the path to this structure. + */ + srch->Path = strdup(path); + srch->Next = NULL; + + /* + ** Attach this path record to the end of the path chain. + */ + if (!First) { + First = srch; + } else { + SearchDriveType * chain = First; + + while (chain->Next) { + chain = (SearchDriveType *)chain->Next; + } + chain->Next = srch; + } +} + + + + +/*********************************************************************************************** + * CDFileClass::Clear_Search_Drives -- Removes all record of a search path. * + * * + * Use this routine to clear out any previous path(s) set with Set_Search_Drives() * + * function. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +void CDFileClass::Clear_Search_Drives(void) +{ + SearchDriveType * chain; // Working pointer to path chain. + + chain = First; + while (chain) { + SearchDriveType *next; + + next = (SearchDriveType *)chain->Next; + if (chain->Path) { + free((char *)chain->Path); + } + delete chain; + + chain = next; + } + First = 0; +} + + +/*********************************************************************************************** + * CDFileClass::Set_Name -- Performs a multiple directory scan to set the filename. * + * * + * This routine will scan all the directories specified in the path list and if the file * + * was found in one of the directories, it will set the filename to a composite of the * + * correct directory and the filename. It is used to allow path searching when searching * + * for files. Typical use is to support CD-ROM drives. This routine examines the current * + * directory first before scanning through the path list. If after scanning the entire * + * path list, the file still could not be found, then the file object's name is set with * + * just the raw filename as passed to this routine. * + * * + * INPUT: filename -- Pointer to the filename to set as the name of this file object. * + * * + * OUTPUT: Returns a pointer to the final and complete filename of this file object. This * + * may have a path attached to the file. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +char const * CDFileClass::Set_Name(char const *filename) +{ + /* + ** Try to find the file in the current directory first. If it can be found, then + ** just return with the normal file name setting process. Do the same if there is + ** no multi-drive search path. + */ + RawFileClass::Set_Name(filename); + if (IsDisabled || !First || RawFileClass::Is_Available()) { + return(File_Name()); + } + + /* + ** Attempt to find the file first. Check the current directory. If not found there, then + ** search all the path specifications available. If it still can't be found, then just + ** fall into the normal raw file filename setting system. + */ + SearchDriveType * srch = First; + + while (srch) { + char path[_MAX_PATH]; + + /* + ** Build a pathname to search for. + */ + strcpy(path, srch->Path); + strcat(path, filename); + + /* + ** Check to see if the file could be found. The low level Is_Available logic will + ** prompt if necessary when the CD-ROM drive has been removed. In all other cases, + ** it will return false and the search process will continue. + */ + RawFileClass::Set_Name(path); + if (RawFileClass::Is_Available()) { + return(File_Name()); + } + + /* + ** It wasn't found, so try the next path entry. + */ + srch = (SearchDriveType *)srch->Next; + } + + /* + ** At this point, all path searching has failed. Just set the file name to the + ** plain text passed to this routine and be done with it. + */ + RawFileClass::Set_Name(filename); + return(File_Name()); +} + + +/*********************************************************************************************** + * CDFileClass::Open -- Opens the file wherever it can be found. * + * * + * This routine is similar to the RawFileClass open except that if the file is being * + * opened only for READ access, it will search all specified directories looking for the * + * file. If after a complete search the file still couldn't be found, then it is opened * + * using the normal RawFileClass system -- resulting in normal error procedures. * + * * + * INPUT: filename -- Pointer to the override filename to supply for this file object. It * + * would be the base filename (sans any directory specification). * + * * + * rights -- The access rights to use when opening the file. * + * * + * OUTPUT: bool; Was the file opened successfully? If so then the filename may be different * + * than requested. The location of the file can be determined by examining the * + * filename of this file object. The filename will contain the complete * + * pathname used to open the file. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int CDFileClass::Open(char const *filename, int rights) +{ + Close(); + + /* + ** Verify that there is a filename associated with this file object. If not, then this is a + ** big error condition. + */ + if (!filename) { + Error(ENOENT, false); + } + + /* + ** If writing is requested, then multiple drive searching is not performed. + */ + if (IsDisabled || rights == WRITE) { + return(RawFileClass::Open(filename, rights)); + } + + /* + ** Perform normal multiple drive searching for the filename and open + ** using the normal procedure. + */ + Set_Name(filename); + return(RawFileClass::Open(rights)); +} + + +#ifdef NEVER +/* Get the drive letters if the CD's online */ +WORD __cdecl GetCDDrive(VOID) +{ + _ES = FP_SEG(&cdDrive[0]); + _BX = FP_OFF(&cdDrive[0]); + _AX = 0x150d; + geninterrupt(0x2F); + return((WORD)(*cdDrive)); +} +#endif + +int Get_CD_Drive(void) +{ +#ifdef NEVER + for (int index = 0; index < 26; index++) { + union REGS regs; + + regs.w.ax = 0x150B; + regs.w.bx = 0; + regs.w.cx = index; + int386(0x2F, ®s, ®s); + if (regs.w.bx == 0xADAD) { + return(index); + } + } + return(0); +#else + +// GetCDClass temp; +// return(temp.GetCDDrive()); + return (0); +#endif + +} diff --git a/CDFILE.H b/CDFILE.H new file mode 100644 index 0000000..8f17d2d --- /dev/null +++ b/CDFILE.H @@ -0,0 +1,115 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\cdfile.h_v 2.20 16 Oct 1995 16:45:58 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood LIbrary * + * * + * File Name : CDFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 18, 1994 * + * * + * Last Update : October 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CDFILE_H +#define CDFILE_H + +#include +#include "rawfile.h" + +/* +** This class is derived from the raw file class. This class adds the functionality of searching +** across multiple directories or drives. It is designed for the typical case of a CD-ROM game +** were some data exists in the current directory (hard drive) and the rest exists on the CD-ROM. +** Searching for the file occurs by first examining the current directory. If the file does not +** exist there, then all the paths available are examined in turn until the file can be found. +** For opening files to write, only the current directory is examined. The directory search order +** is controlled by the path list as submitted to Set_Search_Drives(). The format of the path +** string is the same as the DOS path string. +*/ +class CDFileClass : public RawFileClass +{ + public: + CDFileClass(char const *filename); + CDFileClass(void); + virtual ~CDFileClass(void) {}; + + virtual char const * Set_Name(char const *filename); + virtual int Open(char const *filename, int rights=READ); + virtual int Open(int rights=READ); + + void Searching(int on) {IsDisabled = !on;}; + + static bool Is_There_Search_Drives(void) {return(First != NULL);}; + static int Set_Search_Drives(char * pathlist); + static void Add_Search_Drive(char *path); + static void Clear_Search_Drives(void); + static void Refresh_Search_Drives(void); + static void Set_CD_Drive(int drive); + static int Get_CD_Drive(void) {return(CurrentCDDrive);}; + static int Get_Last_CD_Drive(void) {return(LastCDDrive);}; + + private: + + /* + ** Is multi-drive searching disabled for this file object? + */ + unsigned IsDisabled:1; + + /* + ** This is the control record for each of the drives specified in the search + ** path. There can be many such search paths available. + */ + typedef struct { + void * Next; // Pointer to next search record. + char const * Path; // Pointer to path string. + } SearchDriveType; + + /* + ** This points to the first path record. + */ + static SearchDriveType * First; + + /* + ** This is a copy of the unparsed search path list + */ + static char RawPath[512]; + + /* + ** The drive letter of the current cd drive + */ + static int CurrentCDDrive; + + /* + ** The drive letter of the last used CD drive + */ + static int LastCDDrive; +}; + + +#endif + diff --git a/CELL.CPP b/CELL.CPP new file mode 100644 index 0000000..60e9e1b --- /dev/null +++ b/CELL.CPP @@ -0,0 +1,2316 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\cell.cpv 2.18 16 Oct 1995 16:49:20 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CELL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : August 17, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CellClass::Adjacent_Cell -- Determines the adjacent cell according to facing. * + * CellClass::Adjust_Threat -- Allows adjustment of threat at cell level * + * CellClass::CellClass -- Constructor for cell objects. * + * CellClass::Cell_Building -- Return with building at specified cell. * + * CellClass::Cell_Color -- Determine what radar color to use for this cell. * + * CellClass::Cell_Coord -- Returns the coordinate of this cell. * + * CellClass::Cell_Find_Object -- Returns ptr to RTTI type occupying cell * + * CellClass::Cell_Infantry -- Returns with pointer of first infantry unit. * + * CellClass::Cell_Object -- Returns with clickable object in cell. * + * CellClass::Cell_Techno -- Return with the unit/building at specified cell. * + * CellClass::Cell_Terrain -- Determines terrain object in cell. * + * CellClass::Cell_Unit -- Returns with pointer to unit occupying cell. * + * CellClass::Clear_Icon -- Calculates what the clear icon number should be. * + * CellClass::Closest_Free_Spot -- returns free spot closest to given coord * + * CellClass::Concrete_Calc -- Calculates the concrete icon to use for the cell. * + * CellClass::Draw_It -- Draws the cell imagery at the location specified. * + * CellClass::Flag_Place -- Places a house flag down on the cell. * + * CellClass::Flag_Remove -- Removes the house flag from the cell. * + * CellClass::Cell_Occupier -- Fetches the occupier for the cell. * + * CellClass::Get_Trigger -- retrieves reference to the cell's trigger * + * CellClass::Goodie_Check -- Performs crate discovery logic. * + * CellClass::Incoming -- Causes objects in cell to "run for cover". * + * CellClass::Is_Generally_Clear -- Determines if cell can be built upon. * + * CellClass::Occupy_Down -- Flag occupation of specified cell. * + * CellClass::Occupy_Unit -- Marks cell as unit occupied. * + * CellClass::Occupy_Up -- Removes occupation flag from the specified cell. * + * CellClass::Overlap_Down -- This routine is used to mark a cell as being spilled over (overla* + * CellClass::Overlap_Unit -- Marks cell as being overlapped by unit. * + * CellClass::Overlap_Up -- Removes overlap flag for the cell. * + * CellClass::Read -- Reads a particular cell value from a save game file. * + * CellClass::Recalc_Attributes -- Recalculates the ground type attributes for the cell. * + * CellClass::Redraw_Objects -- Redraws all objects overlapping this cell. * + * CellClass::Reduce_Tiberium -- Reduces the tiberium in the cell by the amount specified. * + * CellClass::Reduce_Wall -- Damages a wall, if damage is high enough. * + * CellClass::Reserve_Cell -- Marks a cell as being occupied by the specified unit ID. * + * CellClass::Shimmer -- Causes all objects in the cell to shimmer. * + * CellClass::Spot_Index -- returns cell sub-coord index for given COORD * + * CellClass::Tiberium_Adjust -- Adjust the look of the Tiberium for smooth. * + * CellClass::Validate -- validates cell's number * + * CellClass::Wall_Update -- Updates the imagery for wall objects in cell. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#define FIXUP 0 + + +/*********************************************************************************************** + * CellClass::Validate -- validates cell's number * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int CellClass::Validate(void) const +{ + int num; + + num = Cell_Number(); + if (num < 0 || num > 4095) { + Validate_Error("CELL"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * CellClass::CellClass -- Constructor for cell objects. * + * * + * A cell object is constructed into an empty state. It contains no specific objects, * + * templates, or overlays. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/09/1994 JLB : Created. * + *=============================================================================================*/ +CellClass::CellClass(void) +{ + memset(this, 0, sizeof(CellClass)); + Smudge = SMUDGE_NONE; + Overlay = OVERLAY_NONE; + Smudge = SMUDGE_NONE; + TType = TEMPLATE_NONE; + Owner = HOUSE_NONE; + InfType = HOUSE_NONE; +} + + +/*********************************************************************************************** + * CellClass::Cell_Color -- Determine what radar color to use for this cell. * + * * + * Use this routine to determine what radar color to render a radar * + * pixel with. This routine is called many many times to render the * + * radar map, so it must be fast. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the color to display the radar pixel with. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/01/1994 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + * 05/31/1994 JLB : Takes into account any stealth characteristics of object. * + *=============================================================================================*/ +int CellClass::Cell_Color(bool override) const +{ + Validate(); + BuildingClass * object = Cell_Building(); + if (object) { + return(object->House->Class->Color); + } + + if (override) { + return(TBLACK); + } + return(::Ground[Land_Type()].Color); +} + + +/*********************************************************************************************** + * CellClass::Cell_Techno -- Return with the unit/building at specified cell. * + * * + * Returns an object located in the cell. If there is a * + * building present, it returns a pointer to that, otherwise it returns * + * a pointer to one of the units there. If nothing is present in the * + * specified cell, then it returns NULL. * + * * + * INPUT: x,y -- Coordinate offset (from upper left corner) to use as an aid in selecting * + * the desired object within the cell. * + * * + * OUTPUT: Returns a pointer to a building or unit located in cell. If * + * nothing present, just returns NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + *=============================================================================================*/ +TechnoClass * CellClass::Cell_Techno(int x, int y) const +{ + Validate(); + ObjectClass * object; + COORDINATE click; // Coordinate of click relative to cell corner. + TechnoClass * close = NULL; + long distance = 0; // Recorded closest distance. + + /* + ** Create a coordinate value that represent the pixel location within the cell. This is + ** actually the lower significant bits (leptons) of a regular coordinate value. + */ + click = XY_Coord(Pixel_To_Lepton(x), Pixel_To_Lepton(y)); + + if (Cell_Occupier()) { + object = Cell_Occupier(); + while (object) { + if (object->Is_Techno()) { + COORDINATE coord; // Coordinate relative to cell corner. + long dist; + + coord = object->Center_Coord() & 0x00FF00FFL; + dist = Distance(coord, click); + if (!close || dist < distance) { + close = (TechnoClass *)object; + distance = dist; + } + } + object = object->Next; + } + } + return(close); +} + + +/*************************************************************************** + * CellClass::Cell_Find_Object -- Returns ptr to RTTI type occupying cell * + * * + * INPUT: RTTIType the RTTI type we are searching for * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/17/1995 PWG : Created. * + * 06/12/1995 JLB : Returns object class pointer. * + *=========================================================================*/ +ObjectClass * CellClass::Cell_Find_Object(RTTIType rtti) const +{ + Validate(); + ObjectClass * object = Cell_Occupier(); + + while (object) { + if (object->What_Am_I() == rtti) { + return(object); + } + object = object->Next; + } + return(NULL); +} + + +/*********************************************************************************************** + * CellClass::Cell_Building -- Return with building at specified cell. * + * * + * Given a cell, determine if there is a building associated * + * and return with a pointer to this building. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the building associated with the * + * cell. If there is no building associated, then NULL is * + * returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + *=============================================================================================*/ +BuildingClass * CellClass::Cell_Building(void) const +{ + Validate(); + return((BuildingClass *)Cell_Find_Object(RTTI_BUILDING)); +} + + +/*********************************************************************************************** + * CellClass::Cell_Terrain -- Determines terrain object in cell. * + * * + * This routine is used to determine the terrain object (if any) that * + * overlaps this cell. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the terrain object that overlaps * + * this cell. If there is no terrain object present, then NULL * + * is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1994 JLB : Created. * + *=============================================================================================*/ +TerrainClass * CellClass::Cell_Terrain(void) const +{ + Validate(); + return((TerrainClass *)Cell_Find_Object(RTTI_TERRAIN)); +} + + +/*********************************************************************************************** + * CellClass::Cell_Object -- Returns with clickable object in cell. * + * * + * This routine is used to determine which object is to be selected * + * by a player click upon the cell. Not all objects that overlap the * + * cell are selectable by the player. This routine sorts out which * + * is which and returns with the appropriate object pointer. * + * * + * INPUT: x,y -- Coordinate (from upper left corner of cell) to use as a guide when * + * selecting the object within the cell. This plays a role in those cases * + * where several objects (such as infantry) exist within the same cell. * + * * + * OUTPUT: Returns with pointer to the object clickable within the * + * cell. NULL is returned if there is no clickable object * + * present. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/13/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * CellClass::Cell_Object(int x, int y) const +{ + Validate(); + ObjectClass * ptr; + + /* + ** Hack so that aircraft landed on helipads can still be selected if directly + ** clicked on. + */ + ptr = (ObjectClass *)Cell_Find_Object(RTTI_AIRCRAFT); + if (ptr) { + return(ptr); + } + + ptr = Cell_Techno(x, y); + if (ptr) { + return(ptr); + } + ptr = Cell_Terrain(); + if (ptr) return(ptr); + return(ptr); +} + + +/*********************************************************************************************** + * CellClass::Redraw_Objects -- Redraws all objects overlapping this cell. * + * * + * This is a low level routine that marks all objects that overlap this * + * cell to be redrawn. It is necessary to call this routine whenever * + * the underlying icon has to be redrawn. * + * * + * INPUT: forced -- Should this redraw be forced even if flags * + * indicate that it would be redundant? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1994 JLB : Created. * + * 06/20/1994 JLB : Simplified to use object pointers. * + * 12/24/1994 JLB : Only checks if cell is in view and not flagged already. * + *=============================================================================================*/ +void CellClass::Redraw_Objects(bool forced) +{ + Validate(); + CELL cell = Cell_Number(); + + if (Map.In_View(cell) && (forced || !Map.Is_Cell_Flagged(cell))) { + + /* + ** Flag the icon to be redrawn. + */ + Map.Flag_Cell(cell); + + /* + ** Flag the main object in the cell to be redrawn. + */ + if (Cell_Occupier()) { + ObjectClass * optr = Cell_Occupier(); + while (optr) { + optr->Mark(MARK_CHANGE); + optr = optr->Next; + } + } + + /* + ** Flag any overlapping object in this cell to be redrawn. + */ + for (int index = 0; index < sizeof(Overlapper)/sizeof(Overlapper[0]); index++) { + if (Overlapper[index]) { + Overlapper[index]->Mark(MARK_CHANGE); + } + } + } +} + + +/*********************************************************************************************** + * CellClass::Is_Generally_Clear -- Determines if cell can be built upon. * + * * + * This determines if the cell can become a proper foundation for * + * building placement. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can the cell be built upon? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1994 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Is_Generally_Clear(void) const +{ + Validate(); + if (ScenarioInit) return(true); + if (Flag.Composite || IsFlagged || Cell_Occupier()) { + return(false); + } + if (Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference(Smudge).IsBib && Owner != HOUSE_NONE) { + return(false); + } + if (Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Overlay).IsWall) { + return(false); + } + +#ifdef ADVANCED + /* + ** Building certain kinds of terrain is prohibited -- bridges in particular. + */ + switch (TType) { + case TEMPLATE_BRIDGE1: + case TEMPLATE_BRIDGE1D: + case TEMPLATE_BRIDGE2: + case TEMPLATE_BRIDGE2D: + case TEMPLATE_BRIDGE3: + case TEMPLATE_BRIDGE3D: + case TEMPLATE_BRIDGE4: + case TEMPLATE_BRIDGE4D: + case TEMPLATE_FORD1: + case TEMPLATE_FORD2: + return(false); + + default: + break; + } +#endif + + return(::Ground[Land_Type()].Build); +} + + +/*********************************************************************************************** + * CellClass::Recalc_Attributes -- Recalculates the ground type attributes for the cell. * + * * + * This routine recalculates the ground type in the cell. The speeds the find path * + * algorithm and other determinations of the cell type. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/29/1994 JLB : Created. * + * 06/20/1994 JLB : Knows about template pointer in cell object. * + *=============================================================================================*/ +void CellClass::Recalc_Attributes(void) +{ + Validate(); + /* + ** Check for wall effects. + */ + if (Overlay != OVERLAY_NONE) { + Land = OverlayTypeClass::As_Reference(Overlay).Land; + if (Land != LAND_CLEAR) return; + } + + /* + ** If there is a template associated with this cell, then scan + ** through the template exception list checking to see if this cell + ** is one of the exception types. If it is, then return that ground type, + ** otherwise return the template's default type. + */ + if (TType != TEMPLATE_NONE) { + TemplateTypeClass const *ttype = &TemplateTypeClass::As_Reference(TType); + + /* + ** If there is an exception type list for the icons of this template, then + ** find out if the current icon is one of them. If so, apply the exception + ** ground type to the cell. + */ + char const *ptr = ttype->AltIcons; + if (ptr) { + int icon = TIcon; + + while (*ptr != -1) { + if (icon == *ptr++) { + Land = ttype->AltLand; + return; + } + } + } + + /* + ** No exception found, so just return the default ground type for this template. + */ + Land = ttype->Land; + return; + } + + /* + ** No template is the same as clear terrain. + */ + Land = TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1).Land; +} + + +/*********************************************************************************************** + * CellClass::Occupy_Down -- Flag occupation of specified cell. * + * * + * This routine is used to mark the cell as being occupied by the specified object. * + * * + * INPUT: object -- The object that is to occupy the cell * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 11/29/1994 JLB : Simplified. * + *=============================================================================================*/ +void CellClass::Occupy_Down(ObjectClass * object) +{ + Validate(); + ObjectClass * optr; + + /* + ** If the specified object is already part of the occupation list, then don't add + ** it again -- bail instead. + */ + if (Cell_Occupier()) { + optr = Cell_Occupier(); + while (optr) { + if (optr == object) { + return; + } + if (!optr->Next) break; + optr = optr->Next; + } + } + optr = Cell_Occupier(); + object->Next = optr; + + OccupierPtr = object; + Map.Radar_Pixel(Cell_Number()); + + /* + ** If being placed down on a visible square, then flag this + ** techno object as being revealed to the player. + */ + if (IsVisible || GameToPlay != GAME_NORMAL) { + object->Revealed(PlayerPtr); + } + + /* + ** Special occupy bit set. + */ + switch (object->What_Am_I()) { + case RTTI_BUILDING: + Flag.Occupy.Building = true; + break; + + case RTTI_AIRCRAFT: + case RTTI_UNIT: + Flag.Occupy.Vehicle = true; + break; + + case RTTI_TERRAIN: + Flag.Occupy.Monolith = true; + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * CellClass::Occupy_Up -- Removes occupation flag from the specified cell. * + * * + * This routine will lift the object from the cell and free the cell to be occupied by * + * another object. Only if the cell was previously marked with the object specified, will * + * the object be lifted off. This routine is the counterpart to Occupy_Down(). * + * * + * INPUT: object -- The object that is being lifted off. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 11/29/1994 JLB : Fixed to handle next pointer in previous object. * + *=============================================================================================*/ +void CellClass::Occupy_Up(ObjectClass * object) +{ + Validate(); + ObjectClass * optr = NULL; // Working pointer to the objects in the chain. + + if (Cell_Occupier()) { + optr = Cell_Occupier(); + } + if (optr == object) { + OccupierPtr = object->Next; + object->Next = 0; + } else { + while (optr) { + if (optr->Next == object) { + optr->Next = object->Next; + object->Next = 0; + break; + } + if (!optr->Next) break; + optr = optr->Next; + } + } + Map.Radar_Pixel(Cell_Number()); + + /* + ** Special occupy bit clear. + */ + switch (object->What_Am_I()) { + case RTTI_BUILDING: + Flag.Occupy.Building = false; + break; + + case RTTI_AIRCRAFT: + case RTTI_UNIT: + Flag.Occupy.Vehicle = false; +#ifdef NEVER + int x,y; + if (Map.Coord_To_Pixel(Cell_Coord(), x, y)) { + SeenBuff.Put_Pixel(x, y, BLUE); + } +#endif + break; + + case RTTI_TERRAIN: + Flag.Occupy.Monolith = false; + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * CellClass::Overlap_Down -- This routine is used to mark a cell as being spilled over (overla* + * * + * Most game objects can often have their graphic imagery spill into more than one cell * + * even though they are considered to "occupy" only one cell. All cells overlapped are * + * flagged by this routine. Using this information it is possible to keep the tactical map * + * display correct. * + * * + * INPUT: object -- The object to mark as overlapping this cell. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 07/04/1995 JLB : Ensures that buildings are always marked down. * + *=============================================================================================*/ +void CellClass::Overlap_Down(ObjectClass * object) +{ + Validate(); + ObjectClass **ptr = 0; + + if (!object) return; + for (int index = 0; index < sizeof(Overlapper)/sizeof(Overlapper[0]); index++) { + if (Overlapper[index] == object) return; + if (!Overlapper[index]) ptr = &Overlapper[index]; + } + + /* + ** Buildings must ALWAYS succeed in marking the cell as overlapped. Bump somebody + ** else out in this case. + */ + if (!ptr && object->What_Am_I() == RTTI_BUILDING) { + for (index = 0; index < sizeof(Overlapper)/sizeof(Overlapper[0]); index++) { + switch (Overlapper[index]->What_Am_I()) { + case RTTI_BUILDING: + case RTTI_TERRAIN: + break; + + default: + Overlapper[index] = object; + index = sizeof(Overlapper)/sizeof(Overlapper[0]); + break; + } + } + } + if (ptr) *ptr = object; + + /* + ** If being placed down on a visible square, then flag this + ** techno object as being revealed to the player. + */ + if (IsVisible) { + object->Revealed(PlayerPtr); + } +} + + +/*********************************************************************************************** + * CellClass::Overlap_Up -- Removes overlap flag for the cell. * + * * + * This is the counterpart to Overlap_Down and is used to remove the overlap flag for the * + * specified unit on the cell. * + * * + * INPUT: object -- The object to remove the overlap flag for. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +void CellClass::Overlap_Up(ObjectClass *object) +{ + Validate(); + for (int index = 0; index < sizeof(Overlapper)/sizeof(Overlapper[0]); index++) { + if (Overlapper[index] == object) { + Overlapper[index] = 0; + break; + } + } +} + + +/*********************************************************************************************** + * CellClass::Cell_Unit -- Returns with pointer to unit occupying cell. * + * * + * This routine will determine if a unit is occupying the cell and if so, return a pointer * + * to it. If there is no unit occupying the cell, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with pointer to unit occupying cell, else NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +UnitClass * CellClass::Cell_Unit(void) const +{ + Validate(); + return((UnitClass*)Cell_Find_Object(RTTI_UNIT)); +} + + +/*********************************************************************************************** + * CellClass::Cell_Infantry -- Returns with pointer of first infantry unit occupying the cell. * + * * + * This routine examines the cell and returns a pointer to the first infantry unit * + * that occupies it. If there is no infantry unit in the cell, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with pointer to infantry unit occupying the cell or NULL if none are * + * present. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/21/1994 JLB : Created. * + *=============================================================================================*/ +InfantryClass * CellClass::Cell_Infantry(void) const +{ + Validate(); + return((InfantryClass*)Cell_Find_Object(RTTI_INFANTRY)); +} + + +/*********************************************************************************************** + * CellClass::Draw_It -- Draws the cell imagery at the location specified. * + * * + * This is the gruntwork cell rendering code. It draws the cell at the screen location * + * specified. This routine doesn't draw any overlapping or occupying units. It only * + * deals with the ground (cell) layer -- icon level. * + * * + * INPUT: x,y -- The screen coordinates to render the cell imagery at. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 08/21/1994 JLB : Revised for simple template objects. * + * 11/01/1994 BRR : Updated placement cursor; draws actual object * + * 11/14/1994 BRR : Added remapping code to show passable areas * + * 12/02/1994 BRR : Added trigger display * + * 12/11/1994 JLB : Mixes up clear terrain through pseudo-random table. * + * 04/25/1995 JLB : Smudges drawn BELOW overlays. * + *=============================================================================================*/ +void CellClass::Draw_It(int x, int y, int draw_type) const +{ + Validate(); + TemplateTypeClass const *ttype = 0; + int icon; // The icon number to use from the template set. + CELL cell = Cell_Number(); + void * remap = NULL; +#ifdef SCENARIO_EDITOR + TemplateTypeClass * tptr; + TriggerClass * trig; + int i; + char waypt[2]; +#endif + + /* + ** Fetch a pointer to the template type associated with this cell. + */ + if (TType != TEMPLATE_NONE) { + ttype = &TemplateTypeClass::As_Reference(TType); + icon = TIcon; + } else { + ttype = &TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1); + icon = Clear_Icon(); + } + + + /* + ** Draw the stamp of the template. + */ + if (Debug_Icon) { + LogicPage->Fill_Rect(Map.TacPixelX+x, Map.TacPixelY+y, Map.TacPixelX+x+ICON_PIXEL_W-1, Map.TacPixelY+y+ICON_PIXEL_H-1, Sim_Random_Pick(1, 254)); + FontXSpacing -= 2; + Fancy_Text_Print("%d\r%2X%c\r%02X.%02X", Map.TacPixelX+x+(ICON_PIXEL_W>>1), Map.TacPixelY+y, WHITE, TBLACK, TPF_6POINT|TPF_NOSHADOW|TPF_CENTER, cell, Flag.Composite, (Cell_Occupier() ? '*' : ' '), Overlay, OverlayData); + FontXSpacing += 2; + } else { + + + if (!draw_type || draw_type == CELL_BLIT_ONLY){ + + +#ifdef SCENARIO_EDITOR + /* + ** Set up the remap table for this icon. + */ + if (Debug_Map && Debug_Passable) { + if (::Ground[Land].Cost[0] == 0 || (Cell_Occupier() != NULL && + Cell_Occupier()->What_Am_I() != RTTI_INFANTRY)) { // impassable + remap = Map.FadingRed; + } else { + if (::Ground[Land].Cost[0] > 0x70) { // pretty passable + remap = Map.FadingGreen; + } else { + remap = Map.FadingYellow; // moderately passable + } + } + } +#endif + + // ****** maybe this icon shouldn't be drawn if it is known that the cell will be covered + // with shadow. + /* + ** This is the underlying terrain icon. + */ + if (ttype->Get_Image_Data()) { + LogicPage->Draw_Stamp(ttype->Get_Image_Data(), icon, x, y, NULL, WINDOW_TACTICAL); + if (remap) { + LogicPage->Remap(x+Map.TacPixelX, y+Map.TacPixelY, ICON_PIXEL_W, ICON_PIXEL_H, remap); + } + } + + +#ifdef SCENARIO_EDITOR + /* + ** Draw the map editor's "current" cell. This is the cell that can be + ** assigned attributes such as tag labels. + ** This must be draw before the placement cursor, but after drawing the + ** objects in the cell. + */ + if (Debug_Map && CurrentCell == Cell_Number()) { + LogicPage->Draw_Rect(x+Map.TacPixelX, y+Map.TacPixelY, Map.TacPixelX + x + CELL_PIXEL_W - 1, Map.TacPixelY + y + CELL_PIXEL_H - 1, YELLOW); + } +#endif + +#ifdef NEVER + /* + ** Special concrete render. It always renders over the underlying + ** terrain unless this concrete piece will cover the entire terrain + ** piece. + */ + if (Concrete) { + LogicPage->Draw_Stamp(TemplateTypeClass::As_Pointer(TEMPLATE_CONCRETE_GDI)->Get_Image_Data(), Concrete-1, x, y, NULL, WINDOW_TACTICAL); + } +#endif + } + + /* + ** Redraw any smudge. + */ + if (Smudge != SMUDGE_NONE) { +#ifdef NEVER + switch (Smudge) { + case SMUDGE_BIB1: + CC_Draw_Shape(Bib1, SmudgeData, x, y, WINDOW_TACTICAL, SHAPE_WIN_REL); + break; + + case SMUDGE_BIB2: + CC_Draw_Shape(Bib2, SmudgeData, x, y, WINDOW_TACTICAL, SHAPE_WIN_REL); + break; + + case SMUDGE_BIB3: + CC_Draw_Shape(Bib3, SmudgeData, x, y, WINDOW_TACTICAL, SHAPE_WIN_REL); + break; + } +#endif + SmudgeTypeClass::As_Reference(Smudge).Draw_It(x, y, SmudgeData); + } + + + if (!draw_type || draw_type == CELL_DRAW_ONLY){ + + /* + ** Draw the overlay object. + */ + if (Overlay != OVERLAY_NONE) { + OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(Overlay); + IsTheaterShape = (bool)otype.IsTheater; + CC_Draw_Shape(otype.Get_Image_Data(), OverlayData, (x+(CELL_PIXEL_W>>1)), (y+(CELL_PIXEL_H>>1)), WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, NULL, Map.UnitShadow); + IsTheaterShape = false; + } + + #ifdef SCENARIO_EDITOR + if (Debug_Map) { + /* + ** Draw the cell's Trigger mnemonic, if it has a trigger + */ + if (IsTrigger) { + trig = Get_Trigger(); + Fancy_Text_Print(trig->Get_Name(), x+Map.TacPixelX, y+Map.TacPixelY, PINK, TBLACK, TPF_NOSHADOW|TPF_6POINT); + } + + /* + ** Draw the cell's Waypoint designation if there is one. + */ + if (IsWaypoint) { + for (i = 0; i < 26; i++) { + if (Waypoint[i]==Cell_Number()) { + waypt[0] = 'A' + i; + waypt[1] = 0; + Fancy_Text_Print(waypt, Map.TacPixelX + x + CELL_PIXEL_W / 2, + Map.TacPixelY + y + (CELL_PIXEL_H / 2) - 3, YELLOW, TBLACK, + TPF_NOSHADOW | TPF_6POINT | TPF_CENTER); + break; + } + } + if (Waypoint[WAYPT_HOME] == Cell_Number()) { + Fancy_Text_Print("Home", Map.TacPixelX + x, Map.TacPixelY + y + (CELL_PIXEL_H) - 7, + WHITE, TBLACK, TPF_NOSHADOW | TPF_6POINT); + } + if (Waypoint[WAYPT_REINF] == Cell_Number()) { + Fancy_Text_Print("Reinf", Map.TacPixelX + x, Map.TacPixelY + y + (CELL_PIXEL_H) - 7, + WHITE, TBLACK, TPF_NOSHADOW | TPF_6POINT); + } + } + } + #endif + + /* + ** Draw the placement cursor: + ** - First, draw the hash-mark cursor, so it will appear underneath + ** any cursor being drawn + ** - If the PendingObject is a template, overlay, or smudge, draw it + ** - Otherwise, it's up to the Display.Refresh_Map() routine to draw it + */ + if (IsCursorHere) { + + /* + ** Draw the hash-mark cursor: + */ + if (Map.ProximityCheck && Is_Generally_Clear()) { + LogicPage->Draw_Stamp(Map.TransIconset, 0, x, y, NULL, WINDOW_TACTICAL); + } else { + LogicPage->Draw_Stamp(Map.TransIconset, 2, x, y, NULL, WINDOW_TACTICAL); + } + + #ifdef SCENARIO_EDITOR + if (Debug_Map && Map.PendingObject) { + + switch (Map.PendingObject->What_Am_I()) { + + /* + ** Draw a template: + ** - Compute the icon offset of this cell for this template, using + ** ZoneCell+ZoneOffset to get the upper-left corner of the placement + ** cursor + ** - Draw the icon + */ + case RTTI_TEMPLATETYPE: + tptr = (TemplateTypeClass *)Map.PendingObject; + if (tptr->Get_Image_Data()) { + icon = (Cell_X(cell) - Cell_X(Map.ZoneCell + Map.ZoneOffset)) + + (Cell_Y(cell) - Cell_Y(Map.ZoneCell + Map.ZoneOffset)) * + tptr->Width; + LogicPage->Draw_Stamp(tptr->Get_Image_Data(), icon, x, y, NULL, WINDOW_TACTICAL); + } + break; + + /* + ** Draw an overlay; just use the existing 'OverlayData' even though + ** it means nothing. + */ + case RTTI_OVERLAYTYPE: + OverlayTypeClass::As_Reference(((OverlayTypeClass *)Map.PendingObject)->Type).Draw_It(x, y, OverlayData); + break; + + /* + ** Draw a smudge + */ + case RTTI_SMUDGETYPE: + SmudgeTypeClass::As_Reference(((SmudgeTypeClass *)Map.PendingObject)->Type).Draw_It(x, y, 0); + break; + } + } + #endif + } + + /* + ** Draw the flag if there is one located at this cell. + */ + if (IsFlagged) { + void const * remap = HouseClass::As_Pointer(Owner)->Remap_Table(false, false); + CC_Draw_Shape(MixFileClass::Retrieve("FLAGFLY.SHP"), Frame % 14, x+(ICON_PIXEL_W/2), y+(ICON_PIXEL_H/2), WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_GHOST|SHAPE_FADING, remap, Map.UnitShadow); + } + } + } +} + + +/*********************************************************************************************** + * CellClass::Concrete_Calc -- Calculates the concrete icon to use for the cell. * + * * + * This routine examines the cells around the current one and from this, determines what * + * concrete icon shape to use (if any). The cell data is adjusted and the cell is marked * + * for redraw if the icon changed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/01/1994 JLB : Created. * + *=============================================================================================*/ +void CellClass::Concrete_Calc(void) +{ + Validate(); + static FacingType _even[5] = {FACING_N, FACING_S, FACING_SW, FACING_W, FACING_NW}; + static FacingType _odd[5] = {FACING_N, FACING_NE, FACING_E, FACING_SE, FACING_S}; + FacingType *ptr; // Working pointer into adjacent cell list. + int index; // Constructed bit index. + int icon; // Icon number. + bool isodd; // Is this for the odd column? + +#define OF_N 0x01 +#define OF_NE 0x02 +#define OF_E 0x04 +#define OF_SE 0x08 +#define OF_S 0x10 + +#define EF_N 0x01 +#define EF_NW 0x10 +#define EF_W 0x08 +#define EF_SW 0x04 +#define EF_S 0x02 + + /* + ** Determine if the even or odd row logic is necessary. + */ + isodd = ((Cell_Number() & 0x01) != 0); + + /* + ** Fetch correct pointer depending on whether this is for an + ** odd or even row. + */ + ptr = (isodd) ? _odd : _even; + + /* + ** Build an index according to the presence of concrete in the special + ** adjacent cells. This is a short list of adjacent cell flags since + ** only 5 adjacent cells need to be examined. The choice of which 5 + ** depends on whether this is for an even or odd column. + */ + index = 0; + for (int i = 0; i < (sizeof(_even)/sizeof(_even[0])); i++) { + CellClass & cellptr = Adjacent_Cell(*ptr++); + +// if ((cellptr->IsConcrete) || +// cellptr->Concrete == C_UPDOWN_RIGHT || +// cellptr->Concrete == C_UPDOWN_LEFT) { + + if (cellptr.Overlay == OVERLAY_CONCRETE) { + index |= (1< levels) { + OverlayData -= levels; + reducer = levels; + } else { + Overlay = OVERLAY_NONE; + reducer = OverlayData; + OverlayData = 0; + Recalc_Attributes(); + } + } + return(reducer); +} + + +/*********************************************************************************************** + * CellClass::Reduce_Wall -- Damages a wall, if damage is high enough. * + * * + * This routine will change the wall shape used for a wall if it's damaged. * + * * + * * + * INPUT: damage -- The number of damage points the wall was hit with. * + * * + * OUTPUT: bool; Was the wall destroyed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BWG : Created. * + * 03/19/1995 JLB : Updates cell information if wall was destroyed. * + *=============================================================================================*/ +int CellClass::Reduce_Wall(int damage) +{ + Validate(); + if (Overlay != OVERLAY_NONE) { + bool destroyed = false; + OverlayTypeClass const & wall = OverlayTypeClass::As_Reference(Overlay); + + if (wall.IsWall) { + + /* + ** If the damage was great enough to ensure wall destruction, reduce the wall by one + ** level (no more). Otherwise determine wall reduction based on a percentage chance + ** proportional to the damage received and the wall's strength. + */ + if (damage >= wall.DamagePoints) { + destroyed = true; + } else { + destroyed = Random_Pick(0, wall.DamagePoints) < damage; + } + + if (destroyed) { + OverlayData+=16; + if ((OverlayData>>4) >= wall.DamageLevels) { + ObjectClass::Detach_This_From_All(As_Target()); + Owner = HOUSE_NONE; + Overlay = OVERLAY_NONE; + OverlayData = 0; + Recalc_Attributes(); + Redraw_Objects(); + Adjacent_Cell(FACING_N).Wall_Update(); + Adjacent_Cell(FACING_W).Wall_Update(); + Adjacent_Cell(FACING_S).Wall_Update(); + Adjacent_Cell(FACING_E).Wall_Update(); + return(true); + } + } + } + } + return(false); +} + + +/*********************************************************************************************** + * CellClass::Get_Trigger -- retrieves reference to the cell's trigger * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * TriggerClass reference * + * * + * WARNINGS: * + * Never call this function unless the IsTrigger flag is set for the cell! * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=============================================================================================*/ +TriggerClass * CellClass::Get_Trigger(void) const +{ + Validate(); + if (IsTrigger) { + return(CellTriggers[Cell_Number()]); + } + return(NULL); +} + + +/*********************************************************************************************** + * CellClass::Spot_Index -- returns cell sub-coord index for given COORD * + * * + * INPUT: * + * coord COORD to compute index for * + * * + * OUTPUT: * + * index into StoppingCoord that's closest to this coord * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1994 BR : Created. * + * 12/10/1994 JLB : Uses alternate sub-position algorithm. * + *=============================================================================================*/ +int CellClass::Spot_Index(COORDINATE coord) +{ + COORDINATE rel = coord & 0x00FF00FFL; // Sub coordinate value within cell. + + /* + ** If the coordinate is close enough to the center of the cell, then return + ** the center position index. + */ + if (Distance(rel, 0x00800080L) < 60) { + return(0); + } + + /* + ** Since the center cell position has been eliminated, a simple comparison + ** as related to the center of the cell can be used to determine the sub + ** position. Take advantage of the fact that the sub positions are organized + ** from left to right, top to bottom. + */ + int index = 0; + if (Coord_X(rel) > 0x80) index |= 0x01; + if (Coord_Y(rel) > 0x80) index |= 0x02; + return(index+1); +} + + +/*********************************************************************************************** + * CellClass::Closest_Free_Spot -- returns free spot closest to given coord * + * * + * Similar to the CellClass::Free_Spot; this routine finds the spot in * + * the cell closest to the given coordinate, and returns the COORD of * + * that spot if it's available, NULL if it's not. * + * * + * INPUT: * + * coord coordinate to check (only sub cell position examined) * + * * + * any -- If only the closest spot is desired regardless of whether it is free or * + * not, then this parameter will be true. * + * * + * OUTPUT: * + * COORD of free spot, NULL if none. The coordinate return value does not alter the cell * + * coordinate data portions of the coordinate passed in. Only the lower sub-cell * + * data is altered. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/08/1994 BR : Created. * + * 12/10/1994 JLB : Picks best of closest stopping positions. * + * 12/21/1994 JLB : Adds a mix-up factor if center location is occupied. * + *=============================================================================================*/ +COORDINATE CellClass::Closest_Free_Spot(COORDINATE coord, bool any) const +{ + Validate(); + int spot_index = Spot_Index(coord); + + /* + ** This precalculated sequence table records the closest spots to any given spot. Sequential + ** examination of these spots for availability ensures that the closes available one is + ** discovered first. + */ + static unsigned char _sequence[5][4] = { + {1,2,3,4}, + {0,2,3,4}, + {0,1,4,3}, + {0,1,4,2}, + {0,2,3,1} + }; + + /* + ** In the case of the center coordinate being requested, but is occupied, then all other + ** sublocations are equidistant. Instead of picking a static sequence of examination, the + ** order is mixed up by way of this table. + */ + static unsigned char _alternate[4][4] = { + {1,2,3,4}, + {2,3,4,1}, + {3,4,1,2}, + {4,1,2,3}, + }; + coord &= 0xFF00FF00L; + + /* + ** Cells occupied by buildings or vehicles don't have any free spots. + */ + if (!any && (Flag.Occupy.Vehicle || Flag.Occupy.Monolith)) { + return(NULL); + } + + /* + ** If just the nearest position is desired regardless of whether occupied or not, + ** then just return with the stopping coordinate value. + */ + if (any || Is_Spot_Free(spot_index)) { + return(Coord_Add(coord, StoppingCoordAbs[spot_index])); + } + + /* + ** Scan through all available sub-locations in the cell in order to determine + ** the closest one to the coordinate requested. Use precalculated table so that + ** when the first free position is found, bail. + */ + unsigned char *sequence; + if (spot_index == 0) { + sequence = &_alternate[Random_Pick(0,3)][0]; + } else { + sequence = &_sequence[spot_index][0]; + } + for (int index = 0; index < 4; index++) { + int pos = *sequence++; + + if (Is_Spot_Free(pos)) { + return(Coord_Add(coord, StoppingCoordAbs[pos])); + } + } + + /* + ** No free spot could be found so return a NULL coordinate. + */ + return(0x00000000L); +} + + +/*********************************************************************************************** + * CellClass::Clear_Icon -- Calculates what the clear icon number should be. * + * * + * This support routine will determine what the clear icon number would be for the cell. * + * The icon number is determined by converting the cell number into an index into a * + * lookup table. This yields what appears to be a randomized map without the necessity of * + * generating and recording randomized map numbers. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the icon number for clear terrain if it were displayed at the * + * cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + * 06/09/1995 JLB : Uses 16 entry scramble algorithm. * + *=============================================================================================*/ +int CellClass::Clear_Icon(void) const +{ + Validate(); + CELL cell = Cell_Number(); + return((cell & 0x03) | ((cell>>4) & 0x0C)); +} + + +/*********************************************************************************************** + * CellClass::Incoming -- Causes objects in cell to "run for cover". * + * * + * This routine is called whenever a great, but slow moving, threat is presented to the * + * occupants of a cell. The occupants will, in most cases, stop what they are doing and * + * try to get out of the way. * + * * + * INPUT: threat -- The coordinate source of the threat. * + * * + * forced -- If this threat is so major that the occupants should stop what * + * they are doing, then this parameter should be set to true. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/10/1995 JLB : Created. * + *=============================================================================================*/ +void CellClass::Incoming(COORDINATE threat, bool forced) +{ + Validate(); + ObjectClass * object = NULL; + + object = Cell_Occupier(); + while (object) { + + /* + ** Special check to make sure that friendly units never scatter. + */ + if (Special.IsScatter || (object->Is_Techno() && !((TechnoClass *)object)->House->IsHuman)) { + if (object->What_Am_I() == RTTI_INFANTRY) { + object->Scatter(threat, forced); + } else { + object->Scatter(threat, forced); +// object->Scatter(threat, false); + } + } + object = object->Next; + } +} + + +/*********************************************************************************************** + * CellClass::Adjacent_Cell -- Determines the adjacent cell according to facing. * + * * + * Use this routine to return a reference to the adjacent cell in the direction specified. * + * * + * INPUT: face -- The direction to use when determining the adjacent cell. * + * * + * OUTPUT: Returns with a reference to the adjacent cell. * + * * + * WARNINGS: If the facing value is invalid, then a reference to the same cell is returned. * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +CellClass const & CellClass::Adjacent_Cell(FacingType face) const +{ + Validate(); + if ((unsigned)face >= FACING_COUNT) { + return(*this); + } + + CellClass const * ptr = this + AdjacentCell[face]; + if (ptr->Cell_Number() & 0xF000) return(*this); + return(*ptr); +// return(*(this + AdjacentCell[face])); +} + + +/*************************************************************************** + * CellClass::Adjust_Threat -- Allows adjustment of threat at cell level * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/24/1995 PWG : Created. * + *=========================================================================*/ +void CellClass::Adjust_Threat(HousesType house, int threat_value) +{ + Validate(); + int region = Map.Cell_Region(Cell_Number()); + + for (HousesType lp = HOUSE_FIRST; lp < HOUSE_COUNT; lp ++) { + if (lp == house) continue; + + HouseClass *house_ptr = HouseClass::As_Pointer(lp); + if (house_ptr && (!house_ptr->IsHuman || !house_ptr->Is_Ally(house))) { + house_ptr->Adjust_Threat(region, threat_value); + } + } + if (Debug_Threat) { + Map.Flag_To_Redraw(true); + } +} + + +/*********************************************************************************************** + * CellClass::Tiberium_Adjust -- Adjust the look of the Tiberium for smoothing purposes. * + * * + * This routine will adjust the level of the Tiberium in the cell so that it will * + * smoothly blend with the adjacent Tiberium. This routine should only be called for * + * new Tiberium cells. Existing cells that contain Tiberium follow a different growth * + * pattern. * + * * + * INPUT: pregame -- Is this a pregame call? Such a call will mixup the Tiberium overlay * + * used. * + * * + * OUTPUT: Returns with the added Tiberium value that is now available for harvesting. * + * * + * WARNINGS: The return value is only valid for the initial placement. Tiberium growth will * + * increase the net worth of the existing Tiberium. * + * * + * HISTORY: * + * 05/16/1995 JLB : Created. * + *=============================================================================================*/ +long CellClass::Tiberium_Adjust(bool pregame) +{ + Validate(); + if (Overlay != OVERLAY_NONE) { + if (OverlayTypeClass::As_Reference(Overlay).Land == LAND_TIBERIUM) { + static int _adj[9] = {0,1,3,4,6,7,8,10,11}; + int count = 0; + + /* + ** Mixup the Tiberium overlays so that they don't look the same. + */ + if (pregame) { + Overlay = Random_Pick(OVERLAY_TIBERIUM1, OVERLAY_TIBERIUM12); + } + + /* + ** Add up all adjacent cells that contain tiberium. + ** (Skip those cells which aren't on the map) + */ + for (FacingType face = FACING_FIRST; face < FACING_COUNT; face++) { + CELL cell = Cell_Number() + AdjacentCell[face]; + + CellClass & adj = Adjacent_Cell(face); + + if (adj.Overlay != OVERLAY_NONE && + OverlayTypeClass::As_Reference(adj.Overlay).Land == LAND_TIBERIUM) { + count++; + } + } + + OverlayData = _adj[count]; + return((OverlayData+1) * UnitTypeClass::TIBERIUM_STEP); + } + } + return(0); +} + + +/*********************************************************************************************** + * CellClass::Goodie_Check -- Performs crate discovery logic. * + * * + * Call this routine whenever an object enters a cell. It will check for the existance * + * of a crate and generate any "goodie" it might contain. * + * * + * INPUT: object -- Pointer to the object that is triggering this crate. * + * * + * OUTPUT: Can the object continue to enter this cell? A false return value means that the * + * cell is now occupied and must not be entered. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + * 07/08/1995 JLB : Added a bunch of goodies to the crates. * + *=============================================================================================*/ +bool CellClass::Goodie_Check(FootClass * object) +{ + Validate(); + enum { + MONEY, // Cash award. + UNIT, // A free unit. + NUKE, // A nuclear device that explodes. + ION, // Calls forth an ion blast on discoverer. + NUKE_MISSILE, // Gets a one time nuclear missile options. + ION_BLAST, // Gets a one time ion blast option. + AIR_STRIKE, // Gets a one time air strike option. + HEAL_BASE, // Heals the player's entire base. + CLOAK, // Units in region gain cloak ability. + EXPLOSION, // Conventional explosion. + NAPALM, // A napalm explosion. + SQUAD, // A mixed squad of friendly infantry appear. + VISCEROID, // A visceroid appears! + DARKNESS, // Shroud the entire map. + REVEAL, // Reveal the entire map. + TOTAL_CRATES, + }; + static int _what[] = { + DARKNESS,DARKNESS, + REVEAL,REVEAL, + NUKE, +// ION,ION, + NUKE_MISSILE, + ION_BLAST,ION_BLAST, + AIR_STRIKE,AIR_STRIKE,AIR_STRIKE,AIR_STRIKE, + HEAL_BASE,HEAL_BASE, + CLOAK,CLOAK, + EXPLOSION,EXPLOSION,EXPLOSION,EXPLOSION, + NAPALM,NAPALM,NAPALM, + SQUAD,SQUAD,SQUAD,SQUAD,SQUAD, + UNIT,UNIT,UNIT,UNIT,UNIT, + VISCEROID + }; + + + /* + ** Crate types are only defined here so it needs to match my new global crate total ST - 6/4/96 2:16PM + */ +#if (TOTAL_CRATES) == (TOTAL_CRATE_TYPES) + +#else + //Huge_Errrrror..... Oh NO! + +#endif + + + if (object && Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Overlay).IsCrate) { + bool steel = (Overlay == OVERLAY_STEEL_CRATE); + COORDINATE coord; // Temporary working coordinate value. + + /* + ** A triggered crate is automatically destroyed regardless of who or how + ** it was triggered. + */ + Redraw_Objects(); + Overlay = OVERLAY_NONE; + OverlayData = 0; + + if (steel) { + if (object->Owner() == HOUSE_BAD) { + object->House->Add_Nuke_Piece(); + new AnimClass(ANIM_CRATE_EMPULSE, Cell_Coord()); + } + + } else { + + int index; + UnitClass * unit = 0; + unsigned damage = 0; + int what = MONEY; + + if (GameToPlay != GAME_NORMAL && (Random_Pick(1, 2) == 1 || !object->House->BScan)) { + what = -1; + + /* + ** If the player has a construction yeard and practically no money and no legitmate means + ** to make any more money, then give money to build a refinery. + */ + if ((object->House->BScan & (STRUCTF_CONST|STRUCTF_REFINERY)) == STRUCTF_CONST && + object->House->Available_Money() < BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost) { + + what = MONEY; + } + + /* + ** If the player should get an MCV replacement, then give it now (probably). + */ + if (Random_Pick(0, 1) == 0 && + MPlayerBases && + !(object->House->UScan & UNITF_MCV) && + object->House->BScan == 0 && + object->House->Available_Money() > (BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost + BuildingTypeClass::As_Reference(STRUCT_POWER).Cost)) { + + what = UNIT; + } + + while (what == -1) { + what = _what[Random_Pick((unsigned)0, sizeof(_what)/sizeof(_what[0])-1)]; + + if (what == REVEAL && object->House->IsVisionary) what = -1; + if (what == AIR_STRIKE && object->House->AirStrike.Is_Present()) what = -1; + if (what == NUKE_MISSILE && object->House->NukeStrike.Is_Present()) what = -1; + if (what == ION_BLAST && object->House->IonCannon.Is_Present()) what = -1; + if (what == CLOAK && object->IsCloakable) what = -1; + } + } + + /* + ** Keep track of the number of each type of crate found + */ + if (GameToPlay == GAME_INTERNET){ + object->House->TotalCrates->Increment_Unit_Total(what); + } + + /* + ** Update the crate count and when all the crates have been discovered, flag + ** to generate a new one. + */ + CrateCount--; + if (!CrateMaker && CrateCount <= 0 && GameToPlay != GAME_NORMAL) { + CrateMaker = true; + CrateTimer = 1; + } + + /* + ** Create the effect requested. + */ + switch (what) { + default: + + /* + ** Give the player money. + */ + case MONEY: + new AnimClass(ANIM_CRATE_DOLLAR, Cell_Coord()); + if (GameToPlay == GAME_NORMAL) { + HouseClass::As_Pointer(object->Owner())->Refund_Money(2000); + } else { + HouseClass::As_Pointer(object->Owner())->Refund_Money(100 + (Random_Pick(0,19)*100)); + } + break; + + /* + ** Shroud the world in blackness. + */ + case DARKNESS: + new AnimClass(ANIM_CRATE_EMPULSE, Cell_Coord()); + if (object->House == PlayerPtr) { + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + CellClass * cellptr = &Map[cell]; + if (cellptr->IsMapped || cellptr->IsVisible) { + cellptr->Redraw_Objects(); + cellptr->IsMapped = false; + cellptr->IsVisible = false; + } + } + for (int index = 0; index < Map.Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * object = Map.Layer[LAYER_GROUND][index]; + if (object && object->Is_Techno() && ((TechnoClass *)object)->House == PlayerPtr) { + object->Look(); + } + } + Map.Flag_To_Redraw(true); + } + break; + + /* + ** Reveal the entire map. + */ + case REVEAL: + new AnimClass(ANIM_CRATE_EMPULSE, Cell_Coord()); + object->House->IsVisionary = true; + if (object->House == PlayerPtr) { + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + Map.Map_Cell(cell, PlayerPtr); + } + } + break; + + /* + ** Try to create a unit where the crate was. + */ + case UNIT: { + UnitTypeClass const * utp = NULL; + + /* + ** Give the player an MCV if he has no base left but does have more than enough + ** money to rebuild a new base. Of course, if he already has an MCV, then don't + ** give him another one. + */ + if (MPlayerBases && + !(object->House->UScan & UNITF_MCV) && + object->House->BScan == 0 && + object->House->Available_Money() > (BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost + BuildingTypeClass::As_Reference(STRUCT_POWER).Cost)) { + + utp = &UnitTypeClass::As_Reference(UNIT_MCV); + } + + /* + ** If the player has a base and a refinery, but no harvester, then give him + ** a free one. + */ + if (!utp && (object->House->BScan & STRUCTF_REFINERY) && !(object->House->UScan & UNITF_HARVESTER)) { + utp = &UnitTypeClass::As_Reference(UNIT_HARVESTER); + } + + while (!utp) { + UnitType utype = Random_Pick(UNIT_FIRST, (UnitType)(UNIT_COUNT-1)); + if (utype != UNIT_MCV || MPlayerBases) { + utp = &UnitTypeClass::As_Reference(utype); + if (utp->IsCrateGoodie && (utp->Ownable & (1 << object->Owner())) && utp->Level <= BuildLevel+2) break; + utp = NULL; + } + } + + UnitClass * unit = (UnitClass *)utp->Create_One_Of(object->House); + if (unit) { + if (unit->Unlimbo(Cell_Coord())) { + return(false); + } + delete unit; + } + } + break; + + /* + ** Create a squad of miscellanous composition. + */ + case SQUAD: + for (index = 0; index < 5; index++) { + static InfantryType _inf[] = { + INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1, + INFANTRY_E2, + INFANTRY_E3, + INFANTRY_E4, + INFANTRY_E5, + INFANTRY_E7, + INFANTRY_RAMBO + }; + InfantryTypeClass::As_Reference(_inf[Random_Pick((unsigned)0, sizeof(_inf)/sizeof(_inf[0])-1)]).Create_And_Place(Cell_Number(), object->Owner()); + } + return(false); + + /* + ** Sometimes an explosion of great magnitude occurs. + */ + case NUKE: + new AnimClass(ANIM_ATOM_BLAST, Cell_Coord()); + break; + + /* + ** Sometimes an explosion of great magnitude occurs. + */ + case ION: + new AnimClass(ANIM_ION_CANNON, Cell_Coord()); + break; + + /* + ** A nuclear missile was discovered. Add it to the sidebar. + */ + case NUKE_MISSILE: + new AnimClass(ANIM_CRATE_MISSILE, Cell_Coord()); + if (object->House->NukeStrike.Enable(true)) { + if (object->IsOwnedByPlayer) { + Map.Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB); + Map.Column[1].Flag_To_Redraw(); + } + } + break; + + /* + ** A one time ion blast was discovered. Add it to the sidebar. + */ + case ION_BLAST: + new AnimClass(ANIM_CRATE_EARTH, Cell_Coord()); + if (object->House->IonCannon.Enable(true)) { + if (object->IsOwnedByPlayer) { + Map.Add(RTTI_SPECIAL, SPC_ION_CANNON); + Map.Column[1].Flag_To_Redraw(); + } + } + break; + + /* + ** A one time air strike can be called int. Add it to the sidebar. + */ + case AIR_STRIKE: + new AnimClass(ANIM_CRATE_DEVIATOR, Cell_Coord()); + if (object->House->AirStrike.Enable(true)) { + if (object->IsOwnedByPlayer) { + Map.Add(RTTI_SPECIAL, SPC_AIR_STRIKE); + Map.Column[1].Flag_To_Redraw(); + } + } + break; + + /* + ** A group of explosions are triggered around the crate. + */ + case EXPLOSION: + damage = 400; + object->Take_Damage((int&)damage, 0, WARHEAD_HE); + for (index = 0; index < 5; index++) { + COORDINATE coord = Coord_Scatter(Cell_Coord(), Random_Pick(0, 0x0200)); + new AnimClass(ANIM_FBALL1, coord); + damage = 400; + Explosion_Damage(coord, damage, NULL, WARHEAD_HE); + } + break; + + /* + ** A napalm blast is triggered. + */ + case NAPALM: + coord = Coord_Mid(Cell_Coord(), object->Center_Coord()); + new AnimClass(ANIM_NAPALM3, coord); + damage = 600; + Explosion_Damage(coord, damage, NULL, WARHEAD_FIRE); + break; + + /* + ** A visceroid appears and, boy, he's angry! + */ + case VISCEROID: + unit = new UnitClass(UNIT_VICE, HOUSE_JP); + if (unit) { + if (unit->Unlimbo(Cell_Coord())) { + return(false); + } + delete unit; + } + break; + + /* + ** All objects within a certain range will gain the ability to cloak. + */ + case CLOAK: + new AnimClass(ANIM_CRATE_STEALTH, Cell_Coord()); + for (index = 0; index < Map.Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * obj = Map.Layer[LAYER_GROUND][index]; + + if (obj && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < 0x0300) { + ((TechnoClass *)obj)->IsCloakable = true; + } + } + break; + + /* + ** All of the player's objects heal up. + */ + case HEAL_BASE: + new AnimClass(ANIM_CRATE_INVUN, Cell_Coord()); + for (index = 0; index < Logic.Count(); index++) { + ObjectClass * obj = Logic[index]; + + if (obj && object->Is_Techno() && object->House->Class->House == obj->Owner()) { + obj->Strength = obj->Class_Of().MaxStrength; + } + } + break; + + } + } + } + return(true); +} + + +/*********************************************************************************************** + * CellClass::Flag_Place -- Places a house flag down on the cell. * + * * + * This routine will place the house flag at this cell location. * + * * + * INPUT: house -- The house that is having its flag placed here. * + * * + * OUTPUT: Was the flag successfully placed here? * + * * + * WARNINGS: Failure to place means that the cell is impassable for some reason. * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Flag_Place(HousesType house) +{ + Validate(); + if (!IsFlagged && Is_Generally_Clear()) { + IsFlagged = true; + Owner = house; + Redraw_Objects(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * CellClass::Flag_Remove -- Removes the house flag from the cell. * + * * + * This routine will free the cell of any house flag that may be located there. * + * * + * INPUT: none * + * * + * OUTPUT: Was there a flag here that was removed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Flag_Remove(void) +{ + Validate(); + if (IsFlagged) { + IsFlagged = false; + Owner = HOUSE_NONE; + Redraw_Objects(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * CellClass::Shimmer -- Causes all objects in the cell to shimmer. * + * * + * This routine is called when some event would cause a momentary disruption in the * + * cloaking device. All objects that are cloaked in the cell will have their cloaking * + * device shimmer. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void CellClass::Shimmer(void) +{ + Validate(); + ObjectClass * object = Cell_Occupier(); + + while (object) { + object->Do_Shimmer(); + object = object->Next; + } +} + + +/*********************************************************************************************** + * CellClass::Cell_Occupier -- Fetches the occupier for the cell. * + * * + * This routine returns with the first recorded occupier of this cell. A special validity * + * check is performed to ensure that there are no dead objects still marked on the * + * map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the first occupier object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/17/1995 JLB : Created. * + *=============================================================================================*/ +ObjectClass * CellClass::Cell_Occupier(void) const +{ + ObjectClass * ptr = OccupierPtr; + + while (ptr && !ptr->IsActive) { + ptr = ptr->Next; + ((ObjectClass *&)OccupierPtr) = 0; + } + + return(ptr); +} diff --git a/CELL.H b/CELL.H new file mode 100644 index 0000000..c621611 --- /dev/null +++ b/CELL.H @@ -0,0 +1,253 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\cell.h_v 2.20 16 Oct 1995 16:45:36 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CELL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : April 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CELL_H +#define CELL_H + +#include "building.h" +#include "unit.h" +#include "template.h" + + +/**************************************************************************** +** Each cell on the map is controlled by the following structure. +*/ +class CellClass +{ + public: + /* + ** Does this cell need to be updated on the radar map? If something changes in the cell + ** that might change the radar map imagery, then this flag will be set. It gets cleared + ** when the cell graphic is updated to the radar map. + */ + unsigned IsPlot:1; + + /* + ** Does this cell contain the special placement cursor graphic? This graphic is + ** present when selecting a site for building placement. + */ + unsigned IsCursorHere:1; + + /* + ** Is this cell mapped by the player? A mapped cell is visible. An unmapped cell + ** is covered in a dark shroud. In addition to visibility, mapped cells are the only + ** legal place for transports to land. + */ + unsigned IsMapped:1; + + /* + ** If any part of this cell is visible (even just peeking out from under the shadow), + ** this this flag will be true. Mapped cells always have this flag set, but unmapped + ** cells might not -- it depends on where the shadow edge is located. + */ + unsigned IsVisible:1; + + /* + ** Every cell can be assigned a trigger. The same trigger can be assigned to + ** multiple cells. This bitflag indicates whether this cell has a trigger. + ** The trigger pointers for all cells must be stored elsewhere. + */ + unsigned IsTrigger:1; + + /* + ** Every cell can be assigned a waypoint. A waypoint can only be assigned + ** to one cell, and vice-versa. This bit simply indicates whether this + ** cell is assigned a waypoint or not. + */ + unsigned IsWaypoint:1; + + /* + ** Is this cell currently under the radar map cursor? If so then it + ** needs to be updated whenever the map is updated. + */ + unsigned IsRadarCursor:1; + + /* + ** If this cell contains a house flag, then this will be true. The actual house + ** flag it contains is specified by the Owner field. + */ + unsigned IsFlagged:1; + + /* + ** This contains the icon number and set to use for the base + ** of the terrain. All rendering on an icon occurs AFTER the icon + ** specified by this element is rendered. It is the lowest of the low. + */ + TemplateType TType; + unsigned char TIcon; + + /* + ** The second layer of 'terrain' icons is represented by a simple + ** type number and a value byte. This is sufficient for handling + ** concrete and walls. + */ + OverlayType Overlay; + unsigned char OverlayData; + + /* + ** This is used to specify any special 'stain' overlay icon. This + ** typically includes infantry bodies or other temporary marks. + */ + SmudgeType Smudge; + unsigned char SmudgeData; + + /* + ** Smudges and walls need to record ownership values. For walls, this + ** allows adjacent building placement logic to work. For smudges, it + ** allows building over smudges that are no longer attached to buildings + ** in addition to fixing the adjacent placement logic. + */ + HousesType Owner; + + /* + ** This flag tells you what type of infantry currently occupy the + ** cell or are moving into it. + */ + HousesType InfType; + + /* + ** These point to the object(s) that are located in this cell or overlap + ** this cell. + */ + ObjectClass *OccupierPtr; + ObjectClass *Overlapper[3]; + + /* + ** This array of bit flags is used to indicate which sub positions + ** within the cell are either occupied or are soon going to be + ** occupied. For vehicles, the cells that the vehicle is passing over + ** will be flagged with the vehicle bit. For infantry, the the sub + ** position the infantry is stopped at or headed toward will be marked. + ** The sub positions it passes over will NOT be marked. + */ + union { + struct { + unsigned Center:1; + unsigned NW:1; + unsigned NE:1; + unsigned SW:1; + unsigned SE:1; + unsigned Vehicle:1; // Reserved for vehicle occupation. + unsigned Monolith:1; // Some immovable blockage is in cell. + unsigned Building:1; // A building of some time (usually blocks movement). + } Occupy; + unsigned char Composite; + } Flag; + + //---------------------------------------------------------------- + CellClass(void); + ~CellClass(void) {}; + + int operator == (CellClass const & cell) const {return &cell == this;}; + + /* + ** Query functions. + */ + ObjectClass * Cell_Occupier(void) const; + static int Spot_Index(COORDINATE coord); + bool Is_Spot_Free(int spot_index) const {return (! (Flag.Composite & (1 << spot_index)) ); } + COORDINATE Closest_Free_Spot(COORDINATE coord, bool any=false) const; + COORDINATE Free_Spot(void) const {return Closest_Free_Spot(Cell_Coord());}; + bool Is_Generally_Clear(void) const; + TARGET As_Target(void) const {return ::As_Target(Cell_Number());}; + BuildingClass * Cell_Building(void) const; + CellClass const & Adjacent_Cell(FacingType face) const; + CellClass & Adjacent_Cell(FacingType face) {return (CellClass &)((*((CellClass const *)this)).Adjacent_Cell(face));}; + COORDINATE Cell_Coord(void) const; + int Cell_Color(bool override=false) const; + CELL Cell_Number(void) const; + LandType Land_Type(void) const {return Land;}; + ObjectClass * Cell_Find_Object(RTTIType rtti) const; + ObjectClass * Cell_Object(int x=0, int y=0) const; + TechnoClass * Cell_Techno(int x=0, int y=0) const; + TerrainClass * Cell_Terrain(void) const; + UnitClass * Cell_Unit(void) const; + InfantryClass * Cell_Infantry(void) const; + TriggerClass * Get_Trigger(void) const; + int Clear_Icon(void) const; + bool Goodie_Check(FootClass * object); + ObjectClass * Fetch_Occupier(void) const; + + /* + ** Object placement and removal flag operations. + */ + void Occupy_Down(ObjectClass * object); + void Occupy_Up(ObjectClass * object); + void Overlap_Down(ObjectClass * object); + void Overlap_Up(ObjectClass * object); + bool Flag_Place(HousesType house); + bool Flag_Remove(void); + + /* + ** File I/O. + */ + bool Should_Save(void) const; + bool Save(FileClass & file); + bool Load(FileClass & file); + void Code_Pointers(void); + void Decode_Pointers(void); + + /* + ** Display and rendering controls. + */ + void Draw_It(int x, int y, int draw_flags = 0) const; + void Redraw_Objects(bool forced=false); + void Shimmer(void); + + /* + ** Maintenance calculation support. + */ + long Tiberium_Adjust(bool pregame=false); + void Wall_Update(void); + void Concrete_Calc(void); + void Recalc_Attributes(void); + int Reduce_Tiberium(int levels); + int Reduce_Wall(int damage); + void Incoming(COORDINATE threat=0, bool forced = false); + void Adjust_Threat(HousesType house, int threat_value); + + int operator != (CellClass const &) const {return 0;}; + + int Validate(void) const; + + private: + CellClass (CellClass const &) {}; + + LandType Land; // The land type of this cell. +}; + +#endif diff --git a/CHECKBOX.CPP b/CHECKBOX.CPP new file mode 100644 index 0000000..48040db --- /dev/null +++ b/CHECKBOX.CPP @@ -0,0 +1,72 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\checkbox.cpv 1.6 16 Oct 1995 16:49:36 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CHECKBOX.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/26/95 * + * * + * Last Update : July 1, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CheckBoxClass::Draw_Me -- Draws the checkbox imagery. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "checkbox.h" + + +/*********************************************************************************************** + * CheckBoxClass::Draw_Me -- Draws the checkbox imagery. * + * * + * This routine will draw the checkbox either filled or empty as necessary. * + * * + * INPUT: forced -- Should the check box be drawn even if it doesn't think it needs to? * + * * + * OUTPUT: Was the check box rendered? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1995 JLB : Created. * + *=============================================================================================*/ +int CheckBoxClass::Draw_Me(int forced) +{ + if (ToggleClass::Draw_Me(forced)) { + + Hide_Mouse(); + Draw_Box(X, Y, Width, Height, BOXSTYLE_GREEN_DOWN, false); + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, Y+Height-2, DKGREY); + if (IsOn) { + LogicPage->Draw_Line(X+1, Y+1, X+Width-2, Y+Height-2, BLACK); + LogicPage->Draw_Line(X+Width-2, Y+1, X+1, Y+Height-2, BLACK); + } + Show_Mouse(); + return(true); + } + return(false); +} diff --git a/CHECKBOX.H b/CHECKBOX.H new file mode 100644 index 0000000..6b75235 --- /dev/null +++ b/CHECKBOX.H @@ -0,0 +1,55 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\checkbox.h_v 1.6 16 Oct 1995 16:46:00 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CHECKBOX.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/26/95 * + * * + * Last Update : May 26, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CHECKBOX_H +#define CHECKBOX_H + +#include "toggle.h" + +class CheckBoxClass : public ToggleClass +{ + public: + CheckBoxClass(unsigned id, int x, int y) : + ToggleClass(id, x, y, 7, 7) + {}; + + virtual int Draw_Me(int forced=false); + + protected: +}; + +#endif diff --git a/CHEKLIST.CPP b/CHEKLIST.CPP new file mode 100644 index 0000000..7f50505 --- /dev/null +++ b/CHEKLIST.CPP @@ -0,0 +1,168 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\cheklist.cpv 2.18 16 Oct 1995 16:48:36 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CHEKLIST.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : February 16, 1995 * + * * + * Last Update : February 16, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * CheckListClass::Action -- action function for this class * + * CheckListClass::CheckListClass -- constructor * + * CheckListClass::Check_Item -- [un]checks an items * + * CheckListClass::~CheckListClass -- destructor * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * CheckListClass::CheckListClass -- constructor * + * * + * INPUT: * + * id control ID for this list box * + * x x-coord * + * y y-coord * + * w width * + * h height * + * flags mouse event flags * + * up ptr to Up-arrow shape * + * down ptr to Down-arrow shape * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/16/1995 BR : Created. * + *=========================================================================*/ +CheckListClass::CheckListClass(int id, int x, int y, int w, int h, TextPrintType flags, + void const * up, void const * down) : + ListClass (id, x, y, w, h, flags, up, down) +{ + IsReadOnly = false; +} + + +/*************************************************************************** + * CheckListClass::Check_Item -- [un]checks an items * + * * + * INPUT: * + * index index of item to check or uncheck * + * checked 0 = uncheck, non-zero = check * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/16/1995 BR : Created. * + *=========================================================================*/ +void CheckListClass::Check_Item(int index, int checked) +{ + if (List[index]) { + ((char &)List[index][0]) = checked ? CHECK_CHAR : UNCHECK_CHAR; + } +} + + +/*************************************************************************** + * CheckListClass::Is_Checked -- returns checked state of an item * + * * + * INPUT: * + * index index of item to query * + * * + * OUTPUT: * + * 0 = item is unchecked, 1 = item is checked * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/16/1995 BR : Created. * + *=========================================================================*/ +int CheckListClass::Is_Checked(int index) const +{ + if (List[index]) { + return(List[index][0] == CHECK_CHAR); + } + return(false); +} + + +/*************************************************************************** + * CheckListClass::Action -- action function for this class * + * * + * INPUT: * + * flags the reason we're being called * + * key the KN_number that was pressed * + * * + * OUTPUT: * + * true = event was processed, false = event not processed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/16/1995 BR : Created. * + *=========================================================================*/ +int CheckListClass::Action(unsigned flags, KeyNumType &key) +{ + int rc; + + /* + ** If this is a read-only list, it's a display-only device + */ + if (IsReadOnly) { + return(false); + } + + /* + ** Invoke parents Action first, so it can set the SelectedIndex if needed. + */ + rc = ListClass::Action(flags, key); + + /* + ** Now, if this event was a left-press, toggle the checked state of the + ** current item. + */ + if (flags & LEFTPRESS) { + if (Is_Checked(SelectedIndex)) { + Check_Item(SelectedIndex,0); + } else { + Check_Item(SelectedIndex,1); + } + } + + return(rc); +} diff --git a/CHEKLIST.H b/CHEKLIST.H new file mode 100644 index 0000000..e9d637d --- /dev/null +++ b/CHEKLIST.H @@ -0,0 +1,86 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\cheklist.h_v 2.19 16 Oct 1995 16:46:20 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CHEKLIST.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : February 16, 1995 * + * * + * Last Update : February 16, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * This class behaves just like the standard list box, except that if the * + * first character of a list entry is a space, clicking on it toggles the * + * space with a check-mark ('\3'). This makes each entry in the list box * + * "toggle-able". * + *-------------------------------------------------------------------------* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CHEKLIST_H +#define CHEKLIST_H + +#include "list.h" + + +class CheckListClass : public ListClass +{ + public: + /*--------------------------------------------------------------------- + Constructor/Destructor + ---------------------------------------------------------------------*/ + CheckListClass(int id, int x, int y, int w, int h, TextPrintType flags, + void const * up, void const * down); + ~CheckListClass(void) {}; + + /*--------------------------------------------------------------------- + Checkmark utility functions + ---------------------------------------------------------------------*/ + void Check_Item(int index, int checked); // sets checked state of item + int Is_Checked(int index) const; // gets checked state of item + + /*--------------------------------------------------------------------- + This defines the ASCII value of the checkmark character & non-checkmark + character. + ---------------------------------------------------------------------*/ + enum CheckListClassEnum { + CHECK_CHAR = '\3', + UNCHECK_CHAR = ' ', + }; + + void Set_Read_Only(int rdonly) {IsReadOnly = rdonly;} + + protected: + virtual int Action(unsigned flags, KeyNumType &key); + + private: + bool IsReadOnly; + +}; + + + +#endif +/************************** end of cheklist.h ******************************/ diff --git a/CO-WC32.LNT b/CO-WC32.LNT new file mode 100644 index 0000000..e3f749b --- /dev/null +++ b/CO-WC32.LNT @@ -0,0 +1,67 @@ +// Compiler Options for Watcom C, C++ 32 bit + +-cwc + +// This file contains options to allow PC-lint to process source +// files for your compiler. It is used as follows: +// +// lint co-wc32.lnt source-file(s) +// +-d_M_IX86=200 // assume Intel 80286 architecture -- modify to suit +-d__declspec()= // ignore this construct + + // additional reserve words needed ++rw(_loadds,_export) ++rw(__interrupt,__near,__far,__huge,__fortran,__pascal,__cdecl) ++rw(__export,__loadds,__saveregs,__asm,__fastcall,__stdcall) ++rw(_export) + ++fcd // makes cdecl significant -- used for proto generation ++fcu // chars are by default unsigned ++fsu // so are strings +-d__386__ // pre-defined macro for 386 version, not set by -cwc +-d__FLAT__ // not set by -cwc +-si4 // sizeof(int) is 4 +-spN4 // sizeof(near pointer) is 4 +-spF6 // sizeof( far pointer) is 6 +-sld10 // sizeof(long double) is 10. +-function(exit,_exit) // _exit() is like exit() +-emacro(734,putc) // don't complain about items being too large. +-emacro(506,putc) // don't complain about constant Boolean +-emacro(???,va_arg) // the va_arg() macro can yield 415, 416, 661, 662 + // 796 and 797 (out-of-bounds errors). + + // While processing compiler (library) header files ... +-elib(46) // an unsigned short bit field is used as __FILLER__ +-elib(522) // function return value ignored +-elib(537) // repeated include file (ios.h) +-elib(641) // converting enum to int +-elib(652) // suppress message about #define of earlier declared symbols +-elib(655) // ORing enum's +-elib(726) // extraneous comma in enumeration +-elib(760) // suppress message about multiple identical macro defs +-elib(762) // suppress message about multiple identical declarations and +-elib(806) // small bit field is signed +-elib(1053) // prototypes cannot be distinguished +-elib(1511) // member (rdbuf) hides nonvirtual member +-elib(1704) // private copy constructor +-elib(1712) // default constructor missing +-elib(1717) // empty prototypes +-elib(1720) // strange argument to assignment operator +-elib(1721) // unusual operator =() declaration +-elib(1722) // assignment operator does not return ref to class +-elib(1724) // strange argument to copy constructor + +-esym(1702,operator<<,operator>>) + +// The following functions exhibit variable return modes. +// That is, they may equally-usefully be called for a value +// as called just for their effects. Accordingly we inhibit +// Warning 534 for these functions. +// Feel free to add to or subtract from this list. + +-esym(534,close,creat,fclose,fflush,fprintf,fputc) +-esym(534,fputs,fscanf,fseek,fwrite,lseek,memcpy,memmove,memset) +-esym(534,printf,puts,scanf,sprintf,sscanf,strcat,strcpy) +-esym(534,strncat,strncpy,unlink,write) + diff --git a/COLRLIST.CPP b/COLRLIST.CPP new file mode 100644 index 0000000..a48be5a --- /dev/null +++ b/COLRLIST.CPP @@ -0,0 +1,283 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\colrlist.cpv 1.9 16 Oct 1995 16:50:02 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LIST.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : April 19, 1995 [BRR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ColorListClass::Add_Item -- Adds an item to the list * + * ColorListClass::ColorListClass -- Class constructor * + * ColorListClass::Draw_Entry -- Draws one text line * + * ColorListClass::Remove_Item -- Removes an item from the list * + * ColorListClass::Set_Selected_Style -- tells how to draw selected item * + * ColorListClass::~ColorListClass -- Class destructor * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * ColorListClass::ColorListClass -- class constructor * + * * + * INPUT: * + * id button ID * + * x,y upper-left corner, in pixels * + * w,h width, height, in pixels * + * list ptr to array of char strings to list * + * flags flags for mouse, style of listbox * + * up,down pointers to shapes for up/down buttons * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: 01/05/1995 MML : Created. * + *=========================================================================*/ +ColorListClass::ColorListClass (int id, int x, int y, int w, int h, + TextPrintType flags, void const * up, void const * down) : + ListClass (id, x, y, w, h, flags, up, down) +{ + Style = SELECT_HIGHLIGHT; + SelectColor = -1; +} + + +/*************************************************************************** + * ColorListClass::~ColorListClass -- Class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +ColorListClass::~ColorListClass(void) +{ + Colors.Clear(); +} + + +/*************************************************************************** + * ColorListClass::Add_Item -- Adds an item to the list * + * * + * INPUT: * + * text text to add to list * + * color color for item * + * * + * OUTPUT: * + * position of item in the list * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +int ColorListClass::Add_Item(char const * text, char color) +{ + Colors.Add(color); + return(ListClass::Add_Item(text)); +} + + +/*************************************************************************** + * ColorListClass::Add_Item -- Adds an item to the list * + * * + * INPUT: * + * text text to add to list * + * color color for item * + * * + * OUTPUT: * + * position of item in the list * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +int ColorListClass::Add_Item(int text, char color) +{ + Colors.Add(color); + return(ListClass::Add_Item(text)); +} + + +/*************************************************************************** + * ColorListClass::Remove_Item -- Removes an item from the list * + * * + * INPUT: * + * text ptr to item to remove * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +void ColorListClass::Remove_Item(char const * text) +{ + int index = List.ID(text); + if (index != -1) { + Colors.Delete(index); + ListClass::Remove_Item(text); + } +} + + +/*************************************************************************** + * ColorListClass::Set_Selected_Style -- tells how to draw selected item * + * * + * INPUT: * + * style style to draw * + * color color to draw the special style in; -1 = use item's color* + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +void ColorListClass::Set_Selected_Style(SelectStyleType style, int color) +{ + Style = style; + SelectColor = color; +} + + +/*************************************************************************** + * ColorListClass::Draw_Entry -- Draws one text line * + * * + * INPUT: * + * index index into List of item to draw * + * x,y x,y coords to draw at * + * width maximum width allowed for text * + * selected true = this item is selected * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +void ColorListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + int color; + + /* + ** Draw a non-selected item in its color + */ + if (!selected) { + Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags, width, Tabs); + return; + } + + /* + ** For selected items, choose the right color & style: + */ + if (SelectColor==-1) { + color = Colors[index]; + } else { + color = SelectColor; + } + + switch (Style) { + /* + ** NONE: Just print the string in its native color + */ + case SELECT_NONE: + Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags, width, Tabs); + break; + + /* + ** HIGHLIGHT: Draw the string in the highlight color (SelectColor must + ** be set) + */ + case SELECT_HIGHLIGHT: + if (TextFlags & TPF_6PT_GRAD) { + Conquer_Clip_Text_Print(List[index], x, y, color, TBLACK, TextFlags | TPF_BRIGHT_COLOR, width, Tabs); + } else { + Conquer_Clip_Text_Print(List[index], x, y, color, TBLACK, TextFlags, width, Tabs); + } + break; + + /* + ** BOX: Draw a box around the item in the current select color + */ + case SELECT_BOX: + LogicPage->Draw_Rect (x, y, x + width - 2, y + LineHeight - 2, color); + Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags, width, Tabs); + break; + + /* + ** BAR: draw a color bar under the text + */ + case SELECT_BAR: + if (TextFlags & TPF_6PT_GRAD) { + LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, SelectColor); + Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags | TPF_BRIGHT_COLOR, width, Tabs); + } else { + LogicPage->Fill_Rect (x, y, x + width - 2, y + LineHeight - 2, SelectColor); + Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags, width, Tabs); + } + break; + + /* + ** INVERT: Draw text as the background color on foreground color + */ + case SELECT_INVERT: + if (TextFlags & TPF_6PT_GRAD) { + LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, Colors[index]); + Conquer_Clip_Text_Print(List[index], x, y, BLACK, TBLACK, TextFlags, width, Tabs); + } else { + LogicPage->Fill_Rect (x, y, x + width - 2, y + LineHeight - 2, Colors[index]); + Conquer_Clip_Text_Print(List[index], x, y, LTGREY, TBLACK, TextFlags, width, Tabs); + } + break; + + } +} diff --git a/COLRLIST.H b/COLRLIST.H new file mode 100644 index 0000000..5a2b105 --- /dev/null +++ b/COLRLIST.H @@ -0,0 +1,87 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\colrlist.h_v 1.10 16 Oct 1995 16:47:16 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COLRLIST.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef COLORLIST_H +#define COLORLIST_H + +#include "list.h" + + +/*************************************************************************** +** This class adds the ability for every list item to have a different color. +*/ +class ColorListClass : public ListClass +{ + public: + /********************************************************************* + ** These enums are the ways a selected item can be drawn + */ + typedef enum SelectEnum { + SELECT_NONE, // selected items aren't drawn differently + SELECT_HIGHLIGHT, // item is highlighted + SELECT_BOX, // draw a box around the item + SELECT_BAR, // draw a bar behind the item + SELECT_INVERT, // draw the string inverted + } SelectStyleType; + + ColorListClass(int id, int x, int y, int w, int h, TextPrintType flags, + void const * up, void const * down); + virtual ~ColorListClass(void); + + virtual int Add_Item(char const * text, char color = WHITE); + virtual int Add_Item(int text, char color = WHITE); + virtual void Remove_Item(char const * text); + + virtual void Set_Selected_Style(SelectStyleType style, int color = -1); + + /* + ** This is the list of colors for each item. + */ + DynamicVectorClass Colors; + + protected: + virtual void Draw_Entry(int index, int x, int y, int width, int selected); + + /* + ** This tells how to draw the selected item. + */ + SelectStyleType Style; + int SelectColor; + +}; + +#endif diff --git a/COMBAT.CPP b/COMBAT.CPP new file mode 100644 index 0000000..ac34d41 --- /dev/null +++ b/COMBAT.CPP @@ -0,0 +1,235 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\combat.cpv 2.17 16 Oct 1995 16:48:32 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COMBAT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 19, 1994 * + * * + * Last Update : January 1, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Explosion_Damage -- Inflict an explosion damage affect. * + * Modify_Damage -- Adjusts damage to reflect the nature of the target. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +int Modify_Damage(int damage, WarheadType warhead, ArmorType armor); +void Explosion_Damage(COORDINATE coord, unsigned strength, TechnoClass *source, WarheadType warhead); + + +/*********************************************************************************************** + * Modify_Damage -- Adjusts damage to reflect the nature of the target. * + * * + * This routine is the core of combat tactics. It implements the * + * affect various armor types have against various weapon types. By * + * careful exploitation of this table, tactical advantage can be * + * obtained. * + * * + * INPUT: damage -- The damage points to process. * + * * + * warhead -- The source of the damage points. * + * * + * armor -- The type of armor defending against the damage. * + * * + * distance -- The distance (in leptons) from the source of the damage. * + * * + * OUTPUT: Returns with the adjusted damage points to inflict upon the * + * target. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/16/1994 JLB : Created. * + * 04/17/1994 JLB : Always does a minimum of damage. * + * 01/01/1995 JLB : Takes into account distance from damage source. * + *=============================================================================================*/ +int Modify_Damage(int damage, WarheadType warhead, ArmorType armor, int distance) +{ + /* + ** If there is no raw damage value to start with, then + ** there can be no modified damage either. + */ + if (Special.IsInert || !damage || warhead == WARHEAD_NONE) return(0); + + WarheadTypeClass const * whead = &Warheads[warhead]; + + damage = Fixed_To_Cardinal(damage, whead->Modifier[armor]); + + /* + ** Reduce damage according to the distance from the impact point. + */ + if (damage) { +// if (distance < 0x0010) damage *= 2; // Double damage for direct hits. + distance >>= whead->SpreadFactor; + distance = Bound(distance, 0, 16); + damage >>= distance; + } + + /* + ** If damage was indicated, then it should never drop below one damage point regardless + ** of modifiers. This allows a very weak attacker to eventually destroy anything it + ** fires upon, given enough time. + */ + return(damage); +} + + +/*********************************************************************************************** + * Explosion_Damage -- Inflict an explosion damage affect. * + * * + * Processes the collateral damage affects typically caused by an * + * explosion. * + * * + * INPUT: coord -- The coordinate of ground zero. * + * * + * strength -- Raw damage points at ground zero. * + * * + * source -- Source of the explosion (who is responsible). * + * * + * warhead -- The kind of explosion to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine can consume some time and will affect the AI * + * of nearby enemy units (possibly). * + * * + * HISTORY: * + * 08/16/1991 JLB : Created. * + * 11/30/1991 JLB : Uses coordinate system. * + * 12/27/1991 JLB : Radius of explosion damage effect. * + * 04/13/1994 JLB : Streamlined. * + * 04/16/1994 JLB : Warhead damage type modifier. * + * 04/17/1994 JLB : Cleaned up. * + * 06/20/1994 JLB : Uses object pointers to distribute damage. * + * 06/20/1994 JLB : Source is a pointer. * + *=============================================================================================*/ +void Explosion_Damage(COORDINATE coord, unsigned strength, TechnoClass * source, WarheadType warhead) +{ + CELL cell; // Cell number under explosion. + ObjectClass * object; // Working object pointer. + ObjectClass * objects[32]; // Maximum number of objects that can be damaged. + int distance; // Distance to unit. + int range; // Damage effect radius. + int index; + int count; // Number of vehicle IDs in list. + + if (!strength || Special.IsInert || warhead == WARHEAD_NONE) return; + + WarheadTypeClass const * whead = &Warheads[warhead]; + range = ICON_LEPTON_W + (ICON_LEPTON_W >> 1); + cell = Coord_Cell(coord); + if ((unsigned)cell >= MAP_CELL_TOTAL) return; +// if (!Map.In_Radar(cell)) return; + + CellClass * cellptr = &Map[cell]; + ObjectClass * impacto = cellptr->Cell_Occupier(); + + /* + ** Fill the list of unit IDs that will have damage + ** assessed upon them. The units can be lifted from + ** the cell data directly. + */ + count = 0; + for (FacingType i = FACING_NONE; i < FACING_COUNT; i++) { + /* + ** Fetch a pointer to the cell to examine. This is either + ** an adjacent cell or the center cell. Damage never spills + ** further than one cell away. + */ + if (i != FACING_NONE) { + cellptr = &Map[cell].Adjacent_Cell(i); + } + + /* + ** Add all objects in this cell to the list of objects to possibly apply + ** damage to. The list stops building when the object pointer list becomes + ** full. Do not include overlapping objects; selection state can affect + ** the overlappers, and this causes multiplayer games to go out of sync. + */ + object = cellptr->Cell_Occupier(); + while (object) { + if (!object->IsToDamage && object != source) { + object->IsToDamage = true; + objects[count++] = object; + if (count >= (sizeof(objects)/sizeof(objects[0]))) break; + } + object = object->Next; + } + if (count >= (sizeof(objects)/sizeof(objects[0]))) break; + } + + /* + ** Sweep through the units to be damaged and damage them. When damaging + ** buildings, consider a hit on any cell the building occupies as if it + ** were a direct hit on the building's center. + */ + for (index = 0; index < count; index++) { + object = objects[index]; + + object->IsToDamage = false; + if (object->What_Am_I() == RTTI_BUILDING && impacto == object) { + distance = 0; + } else { + distance = Distance(coord, object->Center_Coord()); + } + if (object->IsDown && !object->IsInLimbo && distance < range) { + int damage = strength; + + /* + ** High explosive does double damage against aircraft. + */ + if (warhead == WARHEAD_HE && object->What_Am_I() == RTTI_AIRCRAFT) { + damage *= 2; + } + + /* + ** Apply the damage to the object. + */ + if (damage) { + object->Take_Damage(damage, distance, warhead, source); + } + } + } + + /* + ** If there is a wall present at this location, it may be destroyed. Check to + ** make sure that the warhead is of the kind that can destroy walls. + */ + cellptr = &Map[cell]; + cellptr->Reduce_Tiberium(strength / 10); + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + + if (optr->IsWall) { + if (whead->IsWallDestroyer || (whead->IsWoodDestroyer && optr->IsWooden)) { + Map[cell].Reduce_Wall(strength); + } + } + } +} diff --git a/COMBUF.CPP b/COMBUF.CPP new file mode 100644 index 0000000..a381524 --- /dev/null +++ b/COMBUF.CPP @@ -0,0 +1,1039 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\combuf.cpv 1.4 16 Oct 1995 16:50:16 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COMBUF.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : May 2, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * CommBufferClass::CommBufferClass -- class constructor * + * CommBufferClass::~CommBufferClass -- class destructor * + * CommBufferClass::Init -- initializes this queue * + * CommBufferClass::Queue_Send -- queues a message for sending * + * CommBufferClass::UnQueue_Send -- removes next entry from send queue * + * CommBufferClass::Get_Send -- gets ptr to queue entry * + * CommBufferClass::Queue_Receive -- queues a received message * + * CommBufferClass::UnQueue_Receive -- removes next entry from send queue* + * CommBufferClass::Get_Receive -- gets ptr to queue entry * + * CommBufferClass::Add_Delay -- adds a new delay value for response time* + * CommBufferClass::Avg_Response_Time -- returns average response time * + * CommBufferClass::Max_Response_Time -- returns max response time * + * CommBufferClass::Reset_Response_Time -- resets computations * + * Mono_Debug_Print -- Debug output routine * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * CommBufferClass::CommBufferClass -- class constructor * + * * + * INPUT: * + * numsend # queue entries for sending * + * numreceive # queue entries for receiving * + * maxlen maximum desired packet length, in bytes * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +CommBufferClass::CommBufferClass(int numsend, int numreceive, int maxlen) +{ + int i; + + /* + ----------------------------- Init variables ----------------------------- + */ + MaxSend = numsend; + MaxReceive = numreceive; + MaxPacketSize = maxlen; + + /* + ----------------------- Allocate the queue entries ----------------------- + */ + SendQueue = new SendQueueType[numsend]; + ReceiveQueue = new ReceiveQueueType[numreceive]; + + SendIndex = new int[numsend]; + ReceiveIndex = new int[numreceive]; + + /* + ---------------------- Allocate queue entry buffers ---------------------- + */ + for (i = 0; i < MaxSend; i++) { + SendQueue[i].Buffer = new char[maxlen]; + } + + for (i = 0; i < MaxReceive; i++) { + ReceiveQueue[i].Buffer = new char[maxlen]; + } + + Init(); + +} /* end of CommBufferClass */ + + +/*************************************************************************** + * CommBufferClass::~CommBufferClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +CommBufferClass::~CommBufferClass() +{ + int i; + + /* + ------------------------ Free queue entry buffers ------------------------ + */ + for (i = 0; i < MaxSend; i++) { + delete [] SendQueue[i].Buffer; + } + + for (i = 0; i < MaxReceive; i++) { + delete [] ReceiveQueue[i].Buffer; + } + + delete [] SendQueue; + delete [] ReceiveQueue; + + delete [] SendIndex; + delete [] ReceiveIndex; + +} /* end of ~CommBufferClass */ + + +/*************************************************************************** + * CommBufferClass::Init -- initializes this queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/20/1995 BR : Created. * + *=========================================================================*/ +void CommBufferClass::Init(void) +{ + int i; + + /*------------------------------------------------------------------------ + Init data members + ------------------------------------------------------------------------*/ + SendTotal = 0L; + ReceiveTotal = 0L; + + DelaySum = 0L; + NumDelay = 0L; + MeanDelay = 0L; + MaxDelay = 0L; + + SendCount = 0; + + ReceiveCount = 0; + + /*------------------------------------------------------------------------ + Init the queue entries + ------------------------------------------------------------------------*/ + for (i = 0; i < MaxSend; i++) { + SendQueue[i].IsActive = 0; + SendQueue[i].IsACK = 0; + SendQueue[i].FirstTime = 0L; + SendQueue[i].LastTime = 0L; + SendQueue[i].SendCount = 0L; + SendQueue[i].BufLen = 0; + + SendIndex[i] = 0; + } + + for (i = 0; i < MaxReceive; i++) { + ReceiveQueue[i].IsActive = 0; + ReceiveQueue[i].IsRead = 0; + ReceiveQueue[i].IsACK = 0; + ReceiveQueue[i].BufLen = 0; + + ReceiveIndex[i] = 0; + } + + /*------------------------------------------------------------------------ + Init debug values + ------------------------------------------------------------------------*/ + DebugOffset = 0; + DebugSize = 0; + DebugNames = NULL; + DebugMaxNames = 0; + +} /* end of Init */ + + +void CommBufferClass::Init_Send_Queue(void) +{ + int i; + + /*------------------------------------------------------------------------ + Init data members + ------------------------------------------------------------------------*/ + SendCount = 0; + + /*------------------------------------------------------------------------ + Init the queue entries + ------------------------------------------------------------------------*/ + for (i = 0; i < MaxSend; i++) { + SendQueue[i].IsActive = 0; + SendQueue[i].IsACK = 0; + SendQueue[i].FirstTime = 0L; + SendQueue[i].LastTime = 0L; + SendQueue[i].SendCount = 0L; + + SendIndex[i] = 0; + } + +} /* end of Init_Send_Queue */ + + +/*************************************************************************** + * CommBufferClass::Queue_Send -- queues a message for sending * + * * + * INPUT: * + * buf buffer containing the message * + * buflen length of 'buf' * + * * + * OUTPUT: * + * 1 = OK, 0 = no room in the queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommBufferClass::Queue_Send(void *buf, int buflen) +{ + int i; + int index; + + /* + --------------------- Error if no room in the queue ---------------------- + */ + if (SendCount==MaxSend) + return(0); + + /* + -------------------------- Find an empty slot ---------------------------- + */ + index = -1; + for (i = 0; i < MaxSend; i++) { + if (SendQueue[i].IsActive==0) { + index = i; + break; + } + } + + /* + ---------------------------- Set entry flags ----------------------------- + */ + SendQueue[index].IsActive = 1; // entry is now active + SendQueue[index].IsACK = 0; // entry hasn't been ACK'd + SendQueue[index].FirstTime = 0L; // filled in by Manager when sent + SendQueue[index].LastTime = 0L; // filled in by Manager when sent + SendQueue[index].SendCount = 0L; // filled in by Manager when sent + SendQueue[index].BufLen = buflen; // save buffer size + + /* + ------------------------- Copy the packet data --------------------------- + */ + memcpy(SendQueue[index].Buffer,buf,buflen); + + /* + ----------------------- Save this entry's index -------------------------- + */ + SendIndex[SendCount] = index; + + /* + -------------------- Increment counters & entry ptr ---------------------- + */ + SendCount++; + SendTotal++; + + return(1); + +} /* end of Queue_Send */ + + +/*************************************************************************** + * CommBufferClass::UnQueue_Send -- removes next entry from send queue * + * * + * Frees the given entry; the index given by the caller is the "active" * + * index value (ie the "nth" active entry), not the actual index in the * + * array. * + * * + * INPUT: * + * buf buffer to store entry's data in; if NULL, it's discarded * + * buflen filled in with length of entry retrieved * + * index "index" of entry to un-queue * + * * + * OUTPUT: * + * 1 = OK, 0 = no entry to retreive * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommBufferClass::UnQueue_Send(void *buf, int *buflen, int index) +{ + int i; + + /* + --------------------- Error if no entry to retrieve ---------------------- + */ + if (SendCount==0 || SendQueue[SendIndex[index]].IsActive==0) + return(0); + + /* + ---------------------- Copy the data from the entry ---------------------- + */ + if (buf!=NULL) { + memcpy(buf,SendQueue[SendIndex[index]].Buffer, + SendQueue[SendIndex[index]].BufLen); + (*buflen) = SendQueue[SendIndex[index]].BufLen; + } + + /* + ---------------------------- Set entry flags ----------------------------- + */ + SendQueue[SendIndex[index]].IsActive = 0; + SendQueue[SendIndex[index]].IsACK = 0; + SendQueue[SendIndex[index]].FirstTime = 0L; + SendQueue[SendIndex[index]].LastTime = 0L; + SendQueue[SendIndex[index]].SendCount = 0L; + SendQueue[SendIndex[index]].BufLen = 0; + + /* + ------------------------- Move Indices back one -------------------------- + */ + for (i = index; i < SendCount - 1; i++) { + SendIndex[i] = SendIndex[i + 1]; + } + SendIndex[SendCount - 1] = 0; + SendCount--; + + return(1); + +} /* end of UnQueue_Send */ + + +/*************************************************************************** + * CommBufferClass::Get_Send -- gets ptr to queue entry * + * * + * This routine gets a pointer to the indicated queue entry. The index * + * value is relative to the next-accessable queue entry; 0 = get the * + * next available queue entry, 1 = get the one behind that, etc. * + * * + * INPUT: * + * index index of entry to get (0 = 1st available) * + * * + * OUTPUT: * + * ptr to entry * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/21/1994 BR : Created. * + *=========================================================================*/ +SendQueueType * CommBufferClass::Get_Send(int index) +{ + if (SendQueue[SendIndex[index]].IsActive==0) { + return(NULL); + } else { + return(&SendQueue[SendIndex[index]]); + } + +} /* end of Get_Send */ + + +/*************************************************************************** + * CommBufferClass::Queue_Receive -- queues a received message * + * * + * INPUT: * + * buf buffer containing the message * + * buflen length of 'buf' * + * * + * OUTPUT: * + * 1 = OK, 0 = no room in the queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommBufferClass::Queue_Receive(void *buf, int buflen) +{ + int i; + int index; + +//CCDebugString ("C&C95 - Queueing a receive packet\n"); + + /* + --------------------- Error if no room in the queue ---------------------- + */ + if (ReceiveCount==MaxReceive) { +CCDebugString ("C&C95 - Error - Receive queue full!\n"); + return(0); + } + + /* + -------------------------- Find an empty slot ---------------------------- + */ + index = -1; + for (i = 0; i < MaxReceive; i++) { + if (ReceiveQueue[i].IsActive==0) { + index = i; + break; + } + } + +if (index == -1){ +CCDebugString ("C&C95 - Error - Receive queue full too!\n"); +} + + + /* + ---------------------------- Set entry flags ----------------------------- + */ + ReceiveQueue[index].IsActive = 1; + ReceiveQueue[index].IsRead = 0; + ReceiveQueue[index].IsACK = 0; + ReceiveQueue[index].BufLen = buflen; + + /* + ------------------------- Copy the packet data --------------------------- + */ + memcpy(ReceiveQueue[index].Buffer,buf,buflen); + + /* + ----------------------- Save this entry's index -------------------------- + */ + ReceiveIndex[ReceiveCount] = index; + + /* + -------------------- Increment counters & entry ptr ---------------------- + */ + ReceiveCount++; + ReceiveTotal++; + + return(1); + +} /* end of Queue_Receive */ + + +/*************************************************************************** + * CommBufferClass::UnQueue_Receive -- removes next entry from send queue * + * * + * Frees the given entry; the index given by the caller is the "active" * + * index value (ie the "nth" active entry), not the actual index in the * + * array. * + * * + * INPUT: * + * buf buffer to store entry's data in; if NULL, it's discarded * + * buflen filled in with length of entry retrieved * + * index index of entry to un-queue * + * * + * OUTPUT: * + * 1 = OK, 0 = no entry to retreive * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommBufferClass::UnQueue_Receive(void *buf, int *buflen, int index) +{ + int i; + + /* + --------------------- Error if no entry to retrieve ---------------------- + */ + if (ReceiveCount==0 || ReceiveQueue[ReceiveIndex[index]].IsActive==0) { + return(0); + } + + /* + ---------------------- Copy the data from the entry ---------------------- + */ + if (buf!=NULL) { + memcpy(buf,ReceiveQueue[ReceiveIndex[index]].Buffer, + ReceiveQueue[ReceiveIndex[index]].BufLen); + (*buflen) = ReceiveQueue[ReceiveIndex[index]].BufLen; + } + + /* + ---------------------------- Set entry flags ----------------------------- + */ + ReceiveQueue[ReceiveIndex[index]].IsActive = 0; + ReceiveQueue[ReceiveIndex[index]].IsRead = 0; + ReceiveQueue[ReceiveIndex[index]].IsACK = 0; + ReceiveQueue[ReceiveIndex[index]].BufLen = 0; + + /* + ------------------------- Move Indices back one -------------------------- + */ + for (i = index; i < ReceiveCount - 1; i++) { + ReceiveIndex[i] = ReceiveIndex[i + 1]; + } + ReceiveIndex[ReceiveCount - 1] = 0; + ReceiveCount--; + + return(1); + +} /* end of UnQueue_Receive */ + + +/*************************************************************************** + * CommBufferClass::Get_Receive -- gets ptr to queue entry * + * * + * This routine gets a pointer to the indicated queue entry. The index * + * value is relative to the next-accessable queue entry; 0 = get the * + * next available queue entry, 1 = get the one behind that, etc. * + * * + * INPUT: * + * index index of entry to get (0 = 1st available) * + * * + * OUTPUT: * + * ptr to entry * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/21/1994 BR : Created. * + *=========================================================================*/ +ReceiveQueueType * CommBufferClass::Get_Receive(int index) +{ + if (ReceiveQueue[ReceiveIndex[index]].IsActive==0) { + return(NULL); + } else { + return(&ReceiveQueue[ReceiveIndex[index]]); + } + +} /* end of Get_Receive */ + + +/*************************************************************************** + * CommBufferClass::Add_Delay -- adds a new delay value for response time * + * * + * This routine updates the average response time for this queue. The * + * computation is based on the average of the last 'n' delay values given, * + * It computes a running total of the last n delay values, then divides * + * that by n to compute the average. * + * * + * When the number of values given exceeds the max, the mean is subtracted * + * off the total, then the new value is added in. Thus, any single delay * + * value will have an effect on the total that approaches 0 over time, and * + * the new delay value contributes to 1/n of the mean. * + * * + * INPUT: * + * delay value to add into the response time computation * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +void CommBufferClass::Add_Delay(unsigned long delay) +{ + int roundoff = 0; + + if (NumDelay==256) { + DelaySum -= MeanDelay; + DelaySum += delay; + if ( (DelaySum & 0x00ff) > 127) + roundoff = 1; + MeanDelay = (DelaySum >> 8) + roundoff; + } else { + NumDelay++; + DelaySum += delay; + MeanDelay = DelaySum / NumDelay; + } + + if (delay > MaxDelay) + MaxDelay = delay; + +} /* end of Add_Delay */ + + +/*************************************************************************** + * CommBufferClass::Avg_Response_Time -- returns average response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * latest computed average response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +unsigned long CommBufferClass::Avg_Response_Time(void) +{ + return(MeanDelay); + +} /* end of Avg_Response_Time */ + + +/*************************************************************************** + * CommBufferClass::Max_Response_Time -- returns max response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * latest computed average response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +unsigned long CommBufferClass::Max_Response_Time(void) +{ + return(MaxDelay); + +} /* end of Max_Response_Time */ + + +/*************************************************************************** + * CommBufferClass::Reset_Response_Time -- resets computations * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +void CommBufferClass::Reset_Response_Time(void) +{ + DelaySum = 0L; + NumDelay = 0L; + MeanDelay = 0L; + MaxDelay = 0L; + +} /* end of Reset_Response_Time */ + + +/*************************************************************************** + * CommBufferClass::Configure_Debug -- sets up special debug values * + * * + * Mono_Debug_Print2() can look into a packet to pull out a particular * + * ID, and can print both that ID and a string corresponding to * + * that ID. This routine configures these values so it can find * + * and decode the ID. This ID is used in addition to the normal * + * CommHeaderType values. * + * * + * INPUT: * + * offset ID's byte offset into packet * + * size size of ID, in bytes; 0 if none * + * names ptr to array of names; use ID as an index into this * + * maxnames max # in the names array; 0 if none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Names shouldn't be longer than 12 characters. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void CommBufferClass::Configure_Debug(int offset, int size, char **names, + int maxnames) +{ + DebugOffset = offset; + DebugSize = size; + DebugNames = names; + DebugMaxNames = maxnames; + +} /* end of Configure_Debug */ + + +/*************************************************************************** + * Mono_Debug_Print -- Debug output routine * + * * + * This routine leaves 5 lines at the top for the caller's use. * + * * + * INPUT: * + * refresh 1 = clear screen & completely refresh * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/02/1995 BRR : Created. * + *=========================================================================*/ +void CommBufferClass::Mono_Debug_Print(int refresh) +{ +#ifdef WWLIB32_H + int i; // loop counter + static int send_col[] = {1,14,28}; // coords of send queue columns + static int receive_col[] = {40,54,68}; // coords of recv queue columns + int row,col; // current row,col for printing + int num; // max # items to print + + struct CommHdr { // this mirrors the CommHeaderType + unsigned short MagicNumber; + unsigned char Code; + unsigned long PacketID; + } *hdr; + + /*------------------------------------------------------------------------ + If few enough entries, call the verbose debug version + ------------------------------------------------------------------------*/ + if (MaxSend <= 16) { + Mono_Debug_Print2(refresh); + return; + } + + /*------------------------------------------------------------------------ + Refresh the screen + ------------------------------------------------------------------------*/ + if (refresh) { + Mono_Clear_Screen (); + Mono_Printf("ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n"); + Mono_Printf("³ Send Queue ³ Receive Queue ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ID Ct ACK ID Ct ACK ID Ct ACK³ ID Rd ACK ID Rd ACK ID Rd ACK³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"); + } + + /*------------------------------------------------------------------------ + Print Send Queue items + ------------------------------------------------------------------------*/ + if (MaxSend <= 48) { + num = MaxSend; + } else { + num = 48; + } + col = 0; + row = 0; + for (i = 0; i < MaxSend; i++) { + Mono_Set_Cursor (send_col[col],row + 8); + if (SendQueue[i].IsActive) { + hdr = (CommHdr *)SendQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + Mono_Printf ("%4d %2d %d",hdr->PacketID, SendQueue[i].SendCount, + SendQueue[i].IsACK); + } else { + Mono_Printf ("____ __ _ "); + } + + row++; + if (row > 15) { + row = 0; + col++; + } + } + + /*------------------------------------------------------------------------ + Print Receive Queue items + ------------------------------------------------------------------------*/ + if (MaxReceive <= 48) { + num = MaxSend; + } else { + num = 48; + } + col = 0; + row = 0; + for (i = 0; i < MaxReceive; i++) { + Mono_Set_Cursor (receive_col[col],row + 8); + if (ReceiveQueue[i].IsActive) { + hdr = (CommHdr *)ReceiveQueue[i].Buffer; + Mono_Printf ("%4d %d %d",hdr->PacketID, ReceiveQueue[i].IsRead, + ReceiveQueue[i].IsACK); + } else { + Mono_Printf ("____ _ _ "); + } + + row++; + if (row > 15) { + row = 0; + col++; + } + } + +#else + refresh = refresh; +#endif +} /* end of Mono_Debug_Print */ + + +/*************************************************************************** + * CommBufferClass::Mono_Debug_Print2 -- Debug output; alternate format * + * * + * This routine prints more information than the other version; it's * + * called only if the number of queue entries is small enough to support * + * this format. * + * * + * INPUT: * + * refresh 1 = clear screen & completely refresh * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void CommBufferClass::Mono_Debug_Print2(int refresh) +{ +#ifdef WWLIB32_H + int i; // loop counter + char txt[80]; + int val; + + struct CommHdr { // this mirrors the CommHeaderType + unsigned short MagicNumber; + unsigned char Code; + unsigned long PacketID; + } *hdr; + + /*------------------------------------------------------------------------ + Refresh the screen + ------------------------------------------------------------------------*/ + if (refresh) { + Mono_Clear_Screen (); + Mono_Printf("ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n"); + Mono_Printf("³ Send Queue ³ Receive Queue ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ID Ct Type Data Name ACK ³ ID Rd Type Data Name ACK ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"); + } + + /*------------------------------------------------------------------------ + Print Send Queue items + ------------------------------------------------------------------------*/ + for (i = 0; i < MaxSend; i++) { + Mono_Set_Cursor (1,8 + i); + /*..................................................................... + Print an active entry + .....................................................................*/ + if (SendQueue[i].IsActive) { + /*.................................................................. + Get header info + ..................................................................*/ + hdr = (CommHdr *)SendQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + sprintf(txt,"%4d %2d %-5s ", + hdr->PacketID, + SendQueue[i].SendCount, + ConnectionClass::Command_Name(hdr->Code)); + + /*.................................................................. + Decode app's ID & its name + ..................................................................*/ + if (DebugSize && (DebugOffset + DebugSize) <= SendQueue[i].BufLen) { + if (DebugSize==1) { + val = *(SendQueue[i].Buffer + DebugOffset); + } else { + if (DebugSize==2) { + val = *((short *)(SendQueue[i].Buffer + DebugOffset)); + } else { + if (DebugSize==4) { + val = *((int *)(SendQueue[i].Buffer + DebugOffset)); + } + } + } + sprintf(txt + strlen(txt),"%4d ",val); + + if (DebugMaxNames > 0 && val >= 0 && val < DebugMaxNames) { + sprintf(txt + strlen(txt),"%-12s %x", DebugNames[val], SendQueue[i].IsACK); + } else { + sprintf(txt + strlen(txt)," %x",SendQueue[i].IsACK); + } + } else { + sprintf(txt + strlen(txt)," %x",SendQueue[i].IsACK); + } + + Mono_Printf("%s",txt); + } else { + + /*..................................................................... + Entry isn't active; print blanks + .....................................................................*/ + Mono_Printf("____ __ _"); + } + } + + /*------------------------------------------------------------------------ + Print Receive Queue items + ------------------------------------------------------------------------*/ + for (i = 0; i < MaxReceive; i++) { + Mono_Set_Cursor (40,8 + i); + /*..................................................................... + Print an active entry + .....................................................................*/ + if (ReceiveQueue[i].IsActive) { + /*.................................................................. + Get header info + ..................................................................*/ + hdr = (CommHdr *)ReceiveQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + sprintf(txt,"%4d %2d %-5s ", + hdr->PacketID, + ReceiveQueue[i].IsRead, + ConnectionClass::Command_Name(hdr->Code)); + /*.................................................................. + Decode app's ID & its name + ..................................................................*/ + if (DebugSize && (DebugOffset + DebugSize) <= ReceiveQueue[i].BufLen) { + if (DebugSize==1) { + val = *(ReceiveQueue[i].Buffer + DebugOffset); + } else { + if (DebugSize==2) { + val = *((short *)(ReceiveQueue[i].Buffer + DebugOffset)); + } else { + if (DebugSize==4) { + val = *((int *)(ReceiveQueue[i].Buffer + DebugOffset)); + } + } + } + sprintf(txt + strlen(txt),"%4d ",val); + + if (DebugMaxNames > 0 && val >= 0 && val < DebugMaxNames) { + sprintf(txt + strlen(txt),"%-12s %x", + DebugNames[val], + ReceiveQueue[i].IsACK); + } else { + sprintf(txt + strlen(txt)," %x",ReceiveQueue[i].IsACK); + } + } else { + sprintf(txt + strlen(txt)," %x",ReceiveQueue[i].IsACK); + } + + Mono_Printf("%s",txt); + } else { + + /*..................................................................... + Entry isn't active; print blanks + .....................................................................*/ + Mono_Printf("____ __ _"); + } + } + +#else + refresh = refresh; +#endif +} /* end of Mono_Debug_Print2 */ + + diff --git a/COMBUF.H b/COMBUF.H new file mode 100644 index 0000000..15b710c --- /dev/null +++ b/COMBUF.H @@ -0,0 +1,181 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\combuf.h_v 1.6 16 Oct 1995 16:46:00 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COMBUF.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 1, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This class's job is to store outgoing messages & incoming messages, * + * and serves as a storage area for various flags for ACK & Retry logic. * + * * + * This class stores buffers in a non-sequenced order; it allows freeing * + * any entry, so the buffers can be kept clear, even if packets come in * + * out of order. * + * * + * The class also contains routines to maintain a cumulative response time * + * for this queue. It's up to the caller to call Add_Delay() whenever * + * it detects that an outgoing message has been ACK'd; this class adds * + * that delay into a computed average delay over the last few message * + * delays. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef COMBUF_H +#define COMBUF_H + + +/* +********************************** Defines ********************************** +*/ +/*--------------------------------------------------------------------------- +This is one output queue entry +---------------------------------------------------------------------------*/ +typedef struct { + unsigned int IsActive : 1; // 1 = this entry is ready to be processed + unsigned int IsACK : 1; // 1 = ACK received for this packet + unsigned long FirstTime; // time this packet was first sent + unsigned long LastTime; // time this packet was last sent + unsigned long SendCount; // # of times this packet has been sent + int BufLen; // size of the packet stored in this entry + char *Buffer; // the data packet +} SendQueueType; + +/*--------------------------------------------------------------------------- +This is one input queue entry +---------------------------------------------------------------------------*/ +typedef struct { + unsigned int IsActive : 1; // 1 = this entry is ready to be processed + unsigned int IsRead : 1; // 1 = caller has read this entry + unsigned int IsACK : 1; // 1 = ACK sent for this packet + int BufLen; // size of the packet stored in this entry + char *Buffer; // the data packet +} ReceiveQueueType; + +/* +***************************** Class Declaration ***************************** +*/ +class CommBufferClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /* + ....................... Constructor/Destructor ........................ + */ + CommBufferClass(int numsend, int numrecieve, int maxlen); + virtual ~CommBufferClass(); + void Init(void); + void Init_Send_Queue(void); + + /* + ......................... Send Queue routines ......................... + */ + int Queue_Send(void *buf, int buflen); // add to Send queue + int UnQueue_Send(void *buf, int *buflen, int index); // remove from Send queue + int Num_Send(void) {return (SendCount);} // # entries in queue + int Max_Send(void) { return (MaxSend);} // max # send queue entries + SendQueueType * Get_Send(int index); // random access to queue + unsigned long Send_Total(void) {return (SendTotal);} + + /* + ....................... Receive Queue routines ........................ + */ + int Queue_Receive(void *buf, int buflen); // add to Receive queue + int UnQueue_Receive(void *buf, int *buflen, int index); // remove from Receive queue + int Num_Receive(void) {return (ReceiveCount);} // # entries in queue + int Max_Receive(void) { return (MaxReceive); } // max # recv queue entries + ReceiveQueueType * Get_Receive(int index); // random access to queue + unsigned long Receive_Total(void) {return (ReceiveTotal);} + + /* + ....................... Response time routines ........................ + */ + void Add_Delay(unsigned long delay); // accumulates response time + unsigned long Avg_Response_Time(void); // gets mean response time + unsigned long Max_Response_Time(void); // gets max response time + void Reset_Response_Time(void); // resets computations + + /* + ........................ Debug output routines ........................ + */ + void Configure_Debug(int offset, int size, char **names, int maxnames); + void Mono_Debug_Print(int refresh = 0); + void Mono_Debug_Print2(int refresh = 0); + + /* + --------------------------- Private Interface ---------------------------- + */ + private: + /* + .......................... Limiting variables ......................... + */ + int MaxSend; // max # send queue entries + int MaxReceive; // max # receive queue entries + int MaxPacketSize; // max size of a packet, in bytes + + /* + ....................... Response time variables ....................... + */ + unsigned long DelaySum; // sum of last 4 delay times + unsigned long NumDelay; // current # delay times summed + unsigned long MeanDelay; // current average delay time + unsigned long MaxDelay; // max delay ever for this queue + + /* + ........................ Send Queue variables ......................... + */ + SendQueueType * SendQueue; // incoming packets + int SendCount; // # packets in the queue + unsigned long SendTotal; // total # added to send queue + int *SendIndex; // array of Send entry indices + + /* + ....................... Receive Queue variables ....................... + */ + ReceiveQueueType * ReceiveQueue; // outgoing packets + int ReceiveCount; // # packets in the queue + unsigned long ReceiveTotal; // total # added to receive queue + int *ReceiveIndex; // array of Receive entry indices + + /* + ......................... Debugging Variables ......................... + */ + int DebugOffset; // offset into app's packet for ID + int DebugSize; // size of app's ID + char **DebugNames; // ptr to array of app-specific names + int DebugMaxNames; // max # of names in array +}; + +#endif + +/**************************** end of combuf.h ******************************/ + diff --git a/COMPAT.H b/COMPAT.H new file mode 100644 index 0000000..4057f91 --- /dev/null +++ b/COMPAT.H @@ -0,0 +1,170 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\compat.h_v 2.19 16 Oct 1995 16:46:02 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COMPAT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/02/95 * + * * + * Last Update : March 2, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef COMPAT_H +#define COMPAT_H + + +#include "i86.h" + +#define KeyNumType int +#define KeyASCIIType int + + + +#define BuffType BufferClass +#define movmem(a,b,c) memmove(b,a,c) +#define ShapeBufferSize _ShapeBufferSize +extern "C" { + extern long ShapeBufferSize; + extern char *ShapeBuffer; +} + +/*=========================================================================*/ +/* Define some equates for the different graphic routines we will install */ +/* later. */ +/*=========================================================================*/ +#define HIDBUFF ((void *)(0xA0000)) +//#define Size_Of_Region(a, b) a*b + +/*=========================================================================*/ +/* Define some Graphic Routines which will only be fixed by these defines */ +/*=========================================================================*/ +#define Set_Font_Palette(a) Set_Font_Palette_Range(a, 0, 15) + +/* +** These are the Open_File, Read_File, and Seek_File constants. +*/ +#define READ 1 // Read access. +#define WRITE 2 // Write access. + +#ifndef SEEK_SET +#define SEEK_SET 0 // Seek from start of file. +#define SEEK_CUR 1 // Seek relative from current location. +#define SEEK_END 2 // Seek from end of file. +#endif + +#define ERROR_WINDOW 1 +#define ErrorWindow 1 + + +extern unsigned char *Palette; +extern unsigned char MDisabled; // Is mouse disabled? +extern WORD Hard_Error_Occured; + +/* +** This is the menu control structures. +*/ +typedef enum MenuIndexType { + MENUX, + MENUY, + ITEMWIDTH, + ITEMSHIGH, + MSELECTED, + NORMCOL, + HILITE, + MENUPADDING=0x1000 +} MenuIndexType; + + +#define BITSPERBYTE 8 +#define MAXSHORT 0x7fff +#define HIBITS 0x8000 +//#define MAXLONG 0x7fffffffL +#define HIBITL 0x80000000 + +#define MAXINT MAXLONG +#define HIBITI HIBITL + +#define DMAXEXP 308 +#define FMAXEXP 38 +#define DMINEXP -307 +#define FMINEXP -37 + +#define MAXDOUBLE 1.797693E+308 +#define MAXFLOAT 3.37E+38F +#define MINDOUBLE 2.225074E-308 +#define MINFLOAT 8.43E-37F + +#define DSIGNIF 53 +#define FSIGNIF 24 + +#define DMAXPOWTWO 0x3FF +#define FMAXPOWTWO 0x7F +#define DEXPLEN 11 +#define FEXPLEN 8 +#define EXPBASE 2 +#define IEEE 1 +#define LENBASE 1 +#define HIDDENBIT 1 +#define LN_MAXDOUBLE 7.0978E+2 +#define LN_MINDOUBLE -7.0840E+2 + +/* These defines handle the various names given to the same color. */ +#define DKGREEN GREEN +#define DKBLUE BLUE +#define GRAY GREY +#define DKGREY GREY +#define DKGRAY GREY +#define LTGRAY LTGREY + +#if 0 +typedef struct { + short Width; // Width of icons (pixels). + short Height; // Height of icons (pixels). + short Count; // Number of (logical) icons in this set. + short Allocated; // Was this iconset allocated? + long Size; // Size of entire iconset memory block. + unsigned char * Icons; // Offset from buffer start to icon data. + long Palettes; // Offset from buffer start to palette data. + long Remaps; // Offset from buffer start to remap index data. + long TransFlag; // Offset for transparency flag table. + unsigned char * Map; // Icon map offset (if present). +} IControl_Type; +#endif + + + +void Stuff_Key_Num ( int ); + + +extern "C"{ +extern int MouseQX; +extern int MouseQY; +} + +#endif diff --git a/COMQUEUE.CPP b/COMQUEUE.CPP new file mode 100644 index 0000000..b828394 --- /dev/null +++ b/COMQUEUE.CPP @@ -0,0 +1,1006 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\comqueue.cpv 1.10 16 Oct 1995 16:49:14 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COMQUEUE.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : May 31, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * CommQueueClass::CommQueueClass -- class constructor * + * CommQueueClass::~CommQueueClass -- class destructor * + * CommQueueClass::Init -- initializes this queue * + * CommQueueClass::Queue_Send -- queues a message for sending * + * CommQueueClass::UnQueue_Send -- removes next entry from send queue * + * CommQueueClass::Next_Send -- gets ptr to next entry in send queue * + * CommQueueClass::Get_Send -- gets ptr to queue entry * + * CommQueueClass::Queue_Receive -- queues a received message * + * CommQueueClass::UnQueue_Receive -- removes next entry from send queue * + * CommQueueClass::Next_Receive -- gets ptr to next entry in send queue * + * CommQueueClass::Get_Receive -- gets ptr to queue entry * + * CommQueueClass::Add_Delay -- adds a new delay value for response time * + * CommQueueClass::Avg_Response_Time -- returns average response time * + * CommQueueClass::Max_Response_Time -- returns max response time * + * CommQueueClass::Reset_Response_Time -- resets computations * + * CommQueueClass::Configure_Debug -- sets up special debug values * + * CommQueueClass::Mono_Debug_Print -- Debug output routine * + * CommQueueClass::Mono_Debug_Print2 -- Debug output; alternate format * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifndef DEMO + +/*************************************************************************** + * CommQueueClass::CommQueueClass -- class constructor * + * * + * INPUT: * + * numsend # queue entries for sending * + * numreceive # queue entries for receiving * + * maxlen maximum desired packet length, in bytes * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +CommQueueClass::CommQueueClass(int numsend, int numreceive, int maxlen) +{ + int i; + + /* + ----------------------------- Init variables ----------------------------- + */ + MaxSend = numsend; + MaxReceive = numreceive; + MaxPacketSize = maxlen; + + /* + ----------------------- Allocate the queue entries ----------------------- + */ + SendQueue = new SendQueueType[numsend]; + ReceiveQueue = new ReceiveQueueType[numreceive]; + + /* + ---------------------- Allocate queue entry buffers ---------------------- + */ + for (i = 0; i < MaxSend; i++) { + SendQueue[i].Buffer = new char[maxlen]; + } + + for (i = 0; i < MaxReceive; i++) { + ReceiveQueue[i].Buffer = new char[maxlen]; + } + + Init(); + +} /* end of CommQueueClass */ + + +/*************************************************************************** + * CommQueueClass::~CommQueueClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +CommQueueClass::~CommQueueClass() +{ + int i; + + /* + ------------------------ Free queue entry buffers ------------------------ + */ + for (i = 0; i < MaxSend; i++) { + delete [] SendQueue[i].Buffer; + } + + for (i = 0; i < MaxReceive; i++) { + delete [] ReceiveQueue[i].Buffer; + } + + delete [] SendQueue; + delete [] ReceiveQueue; + +} /* end of ~CommQueueClass */ + + +/*************************************************************************** + * CommQueueClass::Init -- initializes this queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/20/1995 BR : Created. * + *=========================================================================*/ +void CommQueueClass::Init(void) +{ + int i; + + /*------------------------------------------------------------------------ + Init data members + ------------------------------------------------------------------------*/ + SendTotal = 0L; + ReceiveTotal = 0L; + + DelaySum = 0L; + NumDelay = 0L; + MeanDelay = 0L; + MaxDelay = 0L; + + SendCount = 0; + SendNext = 0; + SendEmpty = 0; + + ReceiveCount = 0; + ReceiveNext = 0; + ReceiveEmpty = 0; + + /*------------------------------------------------------------------------ + Init the queue entries + ------------------------------------------------------------------------*/ + for (i = 0; i < MaxSend; i++) { + SendQueue[i].IsActive = 0; + SendQueue[i].IsACK = 0; + SendQueue[i].FirstTime = 0L; + SendQueue[i].LastTime = 0L; + SendQueue[i].SendCount = 0L; + ReceiveQueue[i].BufLen = 0; + } + for (i = 0; i < MaxReceive; i++) { + ReceiveQueue[i].IsActive = 0; + ReceiveQueue[i].IsRead = 0; + ReceiveQueue[i].IsACK = 0; + ReceiveQueue[i].BufLen = 0; + } + + /*------------------------------------------------------------------------ + Init debug values + ------------------------------------------------------------------------*/ + DebugOffset = 0; + DebugSize = 0; + DebugNames = NULL; + DebugMaxNames = 0; + +} /* end of Init */ + + +/*************************************************************************** + * CommQueueClass::Queue_Send -- queues a message for sending * + * * + * INPUT: * + * buf buffer containing the message * + * buflen length of 'buf' * + * * + * OUTPUT: * + * 1 = OK, 0 = no room in the queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommQueueClass::Queue_Send(void *buf, int buflen) +{ + /* + --------------------- Error if no room in the queue ---------------------- + */ + if (SendCount==MaxSend || SendQueue[SendEmpty].IsActive!=0) + return(0); + + /* + ---------------------------- Set entry flags ----------------------------- + */ + SendQueue[SendEmpty].IsActive = 1; // entry is now active + SendQueue[SendEmpty].IsACK = 0; // entry hasn't been ACK'd + SendQueue[SendEmpty].FirstTime = 0L; // filled in by Manager when sent + SendQueue[SendEmpty].LastTime = 0L; // filled in by Manager when sent + SendQueue[SendEmpty].SendCount = 0L; // filled in by Manager when sent + SendQueue[SendEmpty].BufLen = buflen; // save buffer size + + /* + ------------------------- Copy the packet data --------------------------- + */ + memcpy(SendQueue[SendEmpty].Buffer,buf,buflen); + + /* + -------------------- Increment counters & entry ptr ---------------------- + */ + SendCount++; + SendEmpty++; + if (SendEmpty==MaxSend) + SendEmpty = 0; + + SendTotal++; + + return(1); + +} /* end of Queue_Send */ + + +/*************************************************************************** + * CommQueueClass::UnQueue_Send -- removes next entry from send queue * + * * + * INPUT: * + * buf buffer to store entry's data in; if NULL, it's discarded * + * buflen filled in with length of entry retrieved * + * * + * OUTPUT: * + * 1 = OK, 0 = no entry to retreive * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommQueueClass::UnQueue_Send(void *buf, int *buflen) +{ + /* + --------------------- Error if no entry to retrieve ---------------------- + */ + if (SendCount==0 || SendQueue[SendNext].IsActive==0) + return(0); + + /* + ---------------------- Copy the data from the entry ---------------------- + */ + if (buf!=NULL) { + memcpy(buf,SendQueue[SendNext].Buffer,SendQueue[SendNext].BufLen); + (*buflen) = SendQueue[SendNext].BufLen; + } + + /* + ---------------------------- Set entry flags ----------------------------- + */ + SendQueue[SendNext].IsActive = 0; + SendQueue[SendNext].IsACK = 0; + SendQueue[SendNext].FirstTime = 0L; + SendQueue[SendNext].LastTime = 0L; + SendQueue[SendNext].SendCount = 0L; + SendQueue[SendNext].BufLen = 0; + SendCount--; + SendNext++; + if (SendNext==MaxSend) + SendNext = 0; + + return(1); + +} /* end of UnQueue_Send */ + + +/*************************************************************************** + * CommQueueClass::Next_Send -- gets ptr to next entry in send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * ptr to entry, NULL if there is none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +SendQueueType * CommQueueClass::Next_Send(void) +{ + if (SendCount==0) { + return(NULL); + } else { + return(&SendQueue[SendNext]); + } +} + + +/*************************************************************************** + * CommQueueClass::Get_Send -- gets ptr to queue entry * + * * + * This routine gets a pointer to the indicated queue entry. The index * + * value is relative to the next-accessable queue entry; 0 = get the * + * next available queue entry, 1 = get the one behind that, etc. * + * * + * INPUT: * + * index index of entry to get (0 = 1st available) * + * * + * OUTPUT: * + * ptr to entry * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/21/1994 BR : Created. * + *=========================================================================*/ +SendQueueType * CommQueueClass::Get_Send(int index) +{ + int i; + + i = SendNext + index; + if (i >= MaxSend) + i -= MaxSend; + + if (SendQueue[i].IsActive==0) { + return(NULL); + } else { + return(&SendQueue[i]); + } +} + + +/*************************************************************************** + * CommQueueClass::Queue_Receive -- queues a received message * + * * + * INPUT: * + * buf buffer containing the message * + * buflen length of 'buf' * + * * + * OUTPUT: * + * 1 = OK, 0 = no room in the queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommQueueClass::Queue_Receive(void *buf, int buflen) +{ + /* + --------------------- Error if no room in the queue ---------------------- + */ + if (ReceiveCount==MaxReceive || ReceiveQueue[ReceiveEmpty].IsActive!=0) { + return(0); + } + + /* + ---------------------------- Set entry flags ----------------------------- + */ + ReceiveQueue[ReceiveEmpty].IsActive = 1; + ReceiveQueue[ReceiveEmpty].IsRead = 0; + ReceiveQueue[ReceiveEmpty].IsACK = 0; + ReceiveQueue[ReceiveEmpty].BufLen = buflen; + + /* + ------------------------- Copy the packet data --------------------------- + */ + if (buflen > MaxPacketLen){ + CCDebugString ("C&C95 - Error. Incoming packet too large"); + } + memcpy(ReceiveQueue[ReceiveEmpty].Buffer,buf,buflen); + + /* + -------------------- Increment counters & entry ptr ---------------------- + */ + ReceiveCount++; + ReceiveEmpty++; + if (ReceiveEmpty==MaxReceive) + ReceiveEmpty = 0; + + ReceiveTotal++; + + return(1); + +} /* end of Queue_Receive */ + + +/*************************************************************************** + * CommQueueClass::UnQueue_Receive -- removes next entry from send queue * + * * + * INPUT: * + * buf buffer to store entry's data in; if NULL, it's discarded * + * buflen filled in with length of entry retrieved * + * * + * OUTPUT: * + * 1 = OK, 0 = no entry to retreive * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommQueueClass::UnQueue_Receive(void *buf, int *buflen) +{ + /* + --------------------- Error if no entry to retrieve ---------------------- + */ + if (ReceiveCount==0 || ReceiveQueue[ReceiveNext].IsActive==0) { + return(0); + } + + /* + ---------------------- Copy the data from the entry ---------------------- + */ + if (buf!=NULL) { + memcpy(buf,ReceiveQueue[ReceiveNext].Buffer, + ReceiveQueue[ReceiveNext].BufLen); + (*buflen) = ReceiveQueue[ReceiveNext].BufLen; + } + + /* + ---------------------------- Set entry flags ----------------------------- + */ + ReceiveQueue[ReceiveNext].IsActive = 0; + ReceiveQueue[ReceiveNext].IsRead = 0; + ReceiveQueue[ReceiveNext].IsACK = 0; + ReceiveQueue[ReceiveNext].BufLen = 0; + ReceiveCount--; + ReceiveNext++; + if (ReceiveNext==MaxReceive) + ReceiveNext = 0; + + return(1); + +} /* end of UnQueue_Receive */ + + +/*************************************************************************** + * CommQueueClass::Next_Receive -- gets ptr to next entry in send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * ptr to entry, NULL if there is none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +ReceiveQueueType * CommQueueClass::Next_Receive(void) +{ + if (ReceiveCount==0) { + return(NULL); + } else { + return(&ReceiveQueue[ReceiveNext]); + } +} + + +/*************************************************************************** + * CommQueueClass::Get_Receive -- gets ptr to queue entry * + * * + * This routine gets a pointer to the indicated queue entry. The index * + * value is relative to the next-accessable queue entry; 0 = get the * + * next available queue entry, 1 = get the one behind that, etc. * + * * + * INPUT: * + * index index of entry to get (0 = 1st available) * + * * + * OUTPUT: * + * ptr to entry * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/21/1994 BR : Created. * + *=========================================================================*/ +ReceiveQueueType * CommQueueClass::Get_Receive(int index) +{ + int i; + + i = ReceiveNext + index; + if (i >= MaxReceive) + i -= MaxReceive; + + if (ReceiveQueue[i].IsActive==0) { + return(NULL); + } else { + return(&ReceiveQueue[i]); + } +} + + +/*************************************************************************** + * CommQueueClass::Add_Delay -- adds a new delay value for response time * + * * + * This routine updates the average response time for this queue. The * + * computation is based on the average of the last 'n' delay values given, * + * It computes a running total of the last n delay values, then divides * + * that by n to compute the average. * + * * + * When the number of values given exceeds the max, the mean is subtracted * + * off the total, then the new value is added in. Thus, any single delay * + * value will have an effect on the total that approaches 0 over time, and * + * the new delay value contributes to 1/n of the mean. * + * * + * INPUT: * + * delay value to add into the response time computation * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +void CommQueueClass::Add_Delay(unsigned long delay) +{ + int roundoff = 0; + + if (NumDelay==256) { + DelaySum -= MeanDelay; + DelaySum += delay; + if ( (DelaySum & 0x00ff) > 127) + roundoff = 1; + MeanDelay = (DelaySum >> 8) + roundoff; + } else { + NumDelay++; + DelaySum += delay; + MeanDelay = DelaySum / NumDelay; + } + + if (delay > MaxDelay) { + MaxDelay = delay; + } + +} /* end of Add_Delay */ + + +/*************************************************************************** + * CommQueueClass::Avg_Response_Time -- returns average response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * latest computed average response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +unsigned long CommQueueClass::Avg_Response_Time(void) +{ + return(MeanDelay); + +} /* end of Avg_Response_Time */ + + +/*************************************************************************** + * CommQueueClass::Max_Response_Time -- returns max response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * latest computed average response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +unsigned long CommQueueClass::Max_Response_Time(void) +{ + return(MaxDelay); + +} /* end of Max_Response_Time */ + + +/*************************************************************************** + * CommQueueClass::Reset_Response_Time -- resets computations * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +void CommQueueClass::Reset_Response_Time(void) +{ + DelaySum = 0L; + NumDelay = 0L; + MeanDelay = 0L; + MaxDelay = 0L; + +} /* end of Reset_Response_Time */ + + +/*************************************************************************** + * CommQueueClass::Configure_Debug -- sets up special debug values * + * * + * Mono_Debug_Print2() can look into a packet to pull out a particular * + * ID, and can print both that ID and a string corresponding to * + * that ID. This routine configures these values so it can find * + * and decode the ID. This ID is used in addition to the normal * + * CommHeaderType values. * + * * + * INPUT: * + * offset ID's byte offset into packet * + * size size of ID, in bytes; 0 if none * + * names ptr to array of names; use ID as an index into this * + * maxnames max # in the names array; 0 if none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Names shouldn't be longer than 12 characters. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void CommQueueClass::Configure_Debug(int offset, int size, char **names, + int maxnames) +{ + DebugOffset = offset; + DebugSize = size; + DebugNames = names; + DebugMaxNames = maxnames; + +} /* end of Configure_Debug */ + + +/*************************************************************************** + * Mono_Debug_Print -- Debug output routine * + * * + * This routine leaves 5 lines at the top for the caller's use. * + * * + * INPUT: * + * refresh 1 = clear screen & completely refresh * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/02/1995 BRR : Created. * + *=========================================================================*/ +void CommQueueClass::Mono_Debug_Print(int refresh) +{ +#ifdef WWLIB32_H + int i; // loop counter + static int send_col[] = {1,14,28}; // coords of send queue columns + static int receive_col[] = {40,54,68}; // coords of recv queue columns + int row,col; // current row,col for printing + int num; // max # items to print + + struct CommHdr { // this mirrors the CommHeaderType + unsigned short MagicNumber; + unsigned char Code; + unsigned long PacketID; + } *hdr; + + /*------------------------------------------------------------------------ + If few enough entries, call the verbose debug version + ------------------------------------------------------------------------*/ + if (MaxSend <= 16) { + Mono_Debug_Print2(refresh); + return; + } + + /*------------------------------------------------------------------------ + Refresh the screen + ------------------------------------------------------------------------*/ + if (refresh) { + Mono_Clear_Screen (); + Mono_Printf("ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n"); + Mono_Printf("³ Send Queue ³ Receive Queue ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ID Ct ACK ID Ct ACK ID Ct ACK³ ID Rd ACK ID Rd ACK ID Rd ACK³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"); + } + + /*------------------------------------------------------------------------ + Print Send Queue items + ------------------------------------------------------------------------*/ + if (MaxSend <= 48) { + num = MaxSend; + } else { + num = 48; + } + col = 0; + row = 0; + for (i = 0; i < MaxSend; i++) { + Mono_Set_Cursor (send_col[col],row + 8); + if (SendQueue[i].IsActive) { + hdr = (CommHdr *)SendQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + Mono_Printf ("%4d %2d %d",hdr->PacketID, SendQueue[i].SendCount, + SendQueue[i].IsACK); + } else { + Mono_Printf ("____ __ _ "); + } + + row++; + if (row > 15) { + row = 0; + col++; + } + } + + /*------------------------------------------------------------------------ + Print Receive Queue items + ------------------------------------------------------------------------*/ + if (MaxReceive <= 48) { + num = MaxSend; + } else { + num = 48; + } + col = 0; + row = 0; + for (i = 0; i < MaxReceive; i++) { + Mono_Set_Cursor (receive_col[col],row + 8); + if (ReceiveQueue[i].IsActive) { + hdr = (CommHdr *)ReceiveQueue[i].Buffer; + Mono_Printf ("%4d %d %d",hdr->PacketID, ReceiveQueue[i].IsRead, + ReceiveQueue[i].IsACK); + } else { + Mono_Printf ("____ _ _ "); + } + + row++; + if (row > 15) { + row = 0; + col++; + } + } + +#else + refresh = refresh; +#endif +} /* end of Mono_Debug_Print */ + + +/*************************************************************************** + * CommQueueClass::Mono_Debug_Print2 -- Debug output; alternate format * + * * + * This routine prints more information than the other version; it's * + * called only if the number of queue entries is small enough to support * + * this format. * + * * + * INPUT: * + * refresh 1 = clear screen & completely refresh * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void CommQueueClass::Mono_Debug_Print2(int refresh) +{ +#ifdef WWLIB32_H + int i; // loop counter + char txt[80]; + int val; + + struct CommHdr { // this mirrors the CommHeaderType + unsigned short MagicNumber; + unsigned char Code; + unsigned long PacketID; + } *hdr; + + /*------------------------------------------------------------------------ + Refresh the screen + ------------------------------------------------------------------------*/ + if (refresh) { + Mono_Clear_Screen (); + Mono_Printf("ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n"); + Mono_Printf("³ Send Queue ³ Receive Queue ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ID Ct Type Data Name ACK ³ ID Rd Type Data Name ACK ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"); + } + + /*------------------------------------------------------------------------ + Print Send Queue items + ------------------------------------------------------------------------*/ + for (i = 0; i < MaxSend; i++) { + Mono_Set_Cursor (1,8 + i); + /*..................................................................... + Print an active entry + .....................................................................*/ + if (SendQueue[i].IsActive) { + /*.................................................................. + Get header info + ..................................................................*/ + hdr = (CommHdr *)SendQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + sprintf(txt,"%4d %2d %-5s ", + hdr->PacketID, + SendQueue[i].SendCount, + ConnectionClass::Command_Name(hdr->Code)); + + /*.................................................................. + Decode app's ID & its name + ..................................................................*/ + if (DebugSize && (DebugOffset + DebugSize) <= SendQueue[i].BufLen) { + if (DebugSize==1) { + val = *(SendQueue[i].Buffer + DebugOffset); + } else { + if (DebugSize==2) { + val = *((short *)(SendQueue[i].Buffer + DebugOffset)); + } else { + if (DebugSize==4) { + val = *((int *)(SendQueue[i].Buffer + DebugOffset)); + } + } + } + sprintf(txt + strlen(txt),"%4d ",val); + + if (DebugMaxNames && val > 0 && val < DebugMaxNames) { + sprintf(txt + strlen(txt),"%-12s %x", + DebugNames[val], + SendQueue[i].IsACK); + } else { + sprintf(txt + strlen(txt)," %x",SendQueue[i].IsACK); + } + } + } else { + + /*..................................................................... + Entry isn't active; print blanks + .....................................................................*/ + Mono_Printf("____ __ _"); + } + } + + /*------------------------------------------------------------------------ + Print Receive Queue items + ------------------------------------------------------------------------*/ + for (i = 0; i < MaxReceive; i++) { + Mono_Set_Cursor (40,8 + i); + /*..................................................................... + Print an active entry + .....................................................................*/ + if (ReceiveQueue[i].IsActive) { + /*.................................................................. + Get header info + ..................................................................*/ + hdr = (CommHdr *)ReceiveQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + sprintf(txt,"%4d %2d %-5s ", + hdr->PacketID, + ReceiveQueue[i].IsRead, + ConnectionClass::Command_Name(hdr->Code)); + /*.................................................................. + Decode app's ID & its name + ..................................................................*/ + if (DebugSize && (DebugOffset + DebugSize) <= SendQueue[i].BufLen) { + if (DebugSize==1) { + val = *(ReceiveQueue[i].Buffer + DebugOffset); + } else { + if (DebugSize==2) { + val = *((short *)(ReceiveQueue[i].Buffer + DebugOffset)); + } else { + if (DebugSize==4) { + val = *((int *)(ReceiveQueue[i].Buffer + DebugOffset)); + } + } + } + sprintf(txt + strlen(txt),"%4d ",val); + + if (DebugMaxNames && val > 0 && val < DebugMaxNames) { + sprintf(txt + strlen(txt),"%-12s %x", DebugNames[val], ReceiveQueue[i].IsACK); + } else { + sprintf(txt + strlen(txt)," %x",ReceiveQueue[i].IsACK); + } + } + } else { + + /*..................................................................... + Entry isn't active; print blanks + .....................................................................*/ + Mono_Printf("____ __ _"); + } + } + +#else + refresh = refresh; +#endif +} /* end of Mono_Debug_Print2 */ + + +#endif diff --git a/COMQUEUE.H b/COMQUEUE.H new file mode 100644 index 0000000..7d406b4 --- /dev/null +++ b/COMQUEUE.H @@ -0,0 +1,193 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\comqueue.h_v 1.12 16 Oct 1995 16:45:08 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COMQUEUE.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 1, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This class's job is to queue up outgoing messages & incoming messages, * + * and serves as a storage area for various flags for ACK & Retry logic. * + * It allows the application to keep track of how many messages have * + * passed through this queue, in & out, so packets can use this as a * + * unique ID. (If packets have a unique ID, the application can use this * + * to detect re-sends.) * + * * + * The queues act as FIFO buffers (First-In, First-Out). The first entry * + * placed in a queue is the first one read from it, and so on. The * + * controlling application must ensure it places the entries on the queue * + * in the order it wants to access them. * + * * + * The queue is implemented as an array of Queue Entries. Index 0 is the * + * first element placed on the queue, and is the first retrieved. Index * + * 1 is the next, and so on. When Index 0 is retrieved, the next-available* + * entry becomes Index 1. The array is circular; when the end is reached, * + * the indices wrap around to the beginning. * + * * + * The class also contains routines to maintain a cumulative response time * + * for this queue. It's up to the caller to call Add_Delay() whenever * + * it detects that an outgoing message has been ACK'd; this class adds * + * that delay into a computed average delay over the last few message * + * delays. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef COMQUEUE_H +#define COMQUEUE_H + + + +/*--------------------------------------------------------------------------- +This is one output queue entry +---------------------------------------------------------------------------*/ +typedef struct { + unsigned int IsActive : 1; // 1 = this entry is ready to be processed + unsigned int IsACK : 1; // 1 = ACK received for this packet + unsigned long FirstTime; // time this packet was first sent + unsigned long LastTime; // time this packet was last sent + unsigned long SendCount; // # of times this packet has been sent + int BufLen; // size of the packet stored in this entry + char *Buffer; // the data packet +} SendQueueType; + +/*--------------------------------------------------------------------------- +This is one input queue entry +---------------------------------------------------------------------------*/ +typedef struct { + unsigned int IsActive : 1; // 1 = this entry is ready to be processed + unsigned int IsRead : 1; // 1 = caller has read this entry + unsigned int IsACK : 1; // 1 = ACK sent for this packet + int BufLen; // size of the packet stored in this entry + char *Buffer; // the data packet +} ReceiveQueueType; + +/* +***************************** Class Declaration ***************************** +*/ +class CommQueueClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /* + ....................... Constructor/Destructor ........................ + */ + CommQueueClass(int numsend, int numrecieve, int maxlen); + virtual ~CommQueueClass(); + void Init(void); + + /* + ......................... Send Queue routines ......................... + */ + int Queue_Send(void *buf, int buflen); // add to Send queue + int UnQueue_Send(void *buf, int *buflen); // remove from Send queue + SendQueueType * Next_Send(void); // ptr to next avail entry + int Num_Send(void) {return (SendCount);} // # entries in queue + int Max_Send(void) { return (MaxSend);} // max # send queue entries + SendQueueType * Get_Send(int index); // random access to queue + unsigned long Send_Total(void) {return (SendTotal);} + + /* + ....................... Receive Queue routines ........................ + */ + int Queue_Receive(void *buf, int buflen); // add to Receive queue + int UnQueue_Receive(void *buf, int *buflen); // remove from Receive queue + ReceiveQueueType * Next_Receive(void); // ptr to next avail entry + int Num_Receive(void) {return (ReceiveCount);} // # entries in queue + int Max_Receive(void) { return (MaxReceive); } // max # recv queue entries + ReceiveQueueType * Get_Receive(int index); // random access to queue + unsigned long Receive_Total(void) {return (ReceiveTotal);} + + /* + ....................... Response time routines ........................ + */ + void Add_Delay(unsigned long delay); // accumulates response time + unsigned long Avg_Response_Time(void); // gets mean response time + unsigned long Max_Response_Time(void); // gets max response time + void Reset_Response_Time(void); // resets computations + + /* + ........................ Debug output routines ........................ + */ + void Configure_Debug(int offset, int size, char **names, int maxnames); + void Mono_Debug_Print(int refresh = 0); + void Mono_Debug_Print2(int refresh = 0); + + /* + --------------------------- Private Interface ---------------------------- + */ + private: + /* + .......................... Limiting variables ......................... + */ + int MaxSend; // max # send queue entries + int MaxReceive; // max # receive queue entries + int MaxPacketSize; // max size of a packet, in bytes + + /* + ....................... Response time variables ....................... + */ + unsigned long DelaySum; // sum of last 4 delay times + unsigned long NumDelay; // current # delay times summed + unsigned long MeanDelay; // current average delay time + unsigned long MaxDelay; // max delay ever for this queue + + /* + ........................ Send Queue variables ......................... + */ + SendQueueType * SendQueue; // incoming packets + int SendCount; // # packets in the queue + int SendNext; // next entry read from queue + int SendEmpty; // next empty spot in queue + unsigned long SendTotal; // total # added to send queue + + /* + ....................... Receive Queue variables ....................... + */ + ReceiveQueueType * ReceiveQueue; // outgoing packets + int ReceiveCount; // # packets in the queue + int ReceiveNext; // next entry read from queue + int ReceiveEmpty; // next empty spot in queue + unsigned long ReceiveTotal; // total # added to receive queue + + /* + ......................... Debugging Variables ......................... + */ + int DebugOffset; // offset into app's packet for ID + int DebugSize; // size of app's ID + char **DebugNames; // ptr to array of app-specific names + int DebugMaxNames; // max # of names in array +}; + +#endif + +/*************************** end of comqueue.h *****************************/ + diff --git a/CONFDLG.CPP b/CONFDLG.CPP new file mode 100644 index 0000000..9bdb6c1 --- /dev/null +++ b/CONFDLG.CPP @@ -0,0 +1,250 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\confdlg.cpv 2.17 16 Oct 1995 16:49:52 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONFDLG.CPP * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 30, 1995 * + * * + * Last Update : Jan 30, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ConfirmationClass::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "confdlg.h" + + +bool ConfirmationClass::Process(int text) +{ + return(Process(Text_String(text))); +} + + +/*********************************************************************************************** + * ConfirmationClass::Process -- Handles all the options graphic interface. * + * * + * This dialog uses an edit box to confirm a deletion. * + * * + * INPUT: char *string - display in edit box. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 12/31/1994 MML : Created. * + *=============================================================================================*/ +bool ConfirmationClass::Process(char const * string) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + enum { + NUM_OF_BUTTONS = 2 + }; + + char buffer[80*3]; + int result = true; + int width; + int bwidth, bheight; // button width and height + int height; + int selection; + bool pressed; + int curbutton; + TextButtonClass *buttons[NUM_OF_BUTTONS]; + + /* + ** Set up the window. Window x-coords are in bytes not pixels. + */ + strcpy(buffer, string); + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(buffer, 200*factor, width, height); + width += 60*factor; + height += 60*factor; + int x = (320*factor - width) / 2; + int y = (200*factor - height) / 2; + + Set_Logic_Page(SeenBuff); + + /* + ** Create Buttons. Button coords are in pixels, but are window-relative. + */ + + bheight = FontHeight + FontYSpacing + 2; + bwidth = MAX( (String_Pixel_Width( Text_String( TXT_YES ) ) + 8), 30); + + TextButtonClass yesbtn(BUTTON_YES, TXT_YES, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + 10*factor, y + height - (bheight + 5*factor), bwidth ); + + TextButtonClass nobtn(BUTTON_NO, TXT_NO, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + width - (bwidth + 10*factor), + y + height - (bheight + 5*factor), bwidth ); + + nobtn.Add_Tail(yesbtn); + + curbutton = 1; + buttons[0] = &yesbtn; + buttons[1] = &nobtn; + buttons[curbutton]->Turn_On(); + + /* + ** This causes left mouse button clicking within the confines of the dialog to + ** be ignored if it wasn't recognized by any other button or slider. + */ + GadgetClass dialog(x, y, width, height, GadgetClass::LEFTPRESS); + dialog.Add_Tail(yesbtn); + + /* + ** This causes a right click anywhere or a left click outside the dialog region + ** to be equivalent to clicking on the return to options dialog. + */ + ControlClass background(BUTTON_NO, 0, 0, SeenBuff.Get_Width(), SeenBuff.Get_Height(), GadgetClass::LEFTPRESS|GadgetClass::RIGHTPRESS); + background.Add_Tail(yesbtn); + + /* + ** Main Processing Loop. + */ + bool display = true; + bool process = true; + pressed = false; + while (process) { + + /* + ** Invoke game callback. + */ + if (GameToPlay == GAME_NORMAL) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + result = false; + } + } + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=TRUE; + } + + /* + ** Refresh display if needed. + */ + if (display) { + Hide_Mouse(); + + /* + ** Draw the background. + */ + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_CONFIRMATION, x, y, width); + Fancy_Text_Print(buffer, x+20*factor, y+30*factor, CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + /* + ** Draw the titles. + */ + yesbtn.Draw_All(); + Show_Mouse(); + display = false; + } + + /* + ** Get user input. + */ + KeyNumType input = yesbtn.Input(); + + /* + ** Process Input. + */ + switch (input) { + case (BUTTON_YES | KN_BUTTON): + selection = BUTTON_YES; + pressed = true; + break; + + case (KN_ESC): + case (BUTTON_NO | KN_BUTTON): + selection = BUTTON_NO; + pressed = true; + break; + + case (KN_LEFT): + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton--; + if (curbutton < 0) { + curbutton = NUM_OF_BUTTONS - 1; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case (KN_RIGHT): + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton++; + if (curbutton > (NUM_OF_BUTTONS - 1) ) { + curbutton = 0; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_YES; + pressed = true; + break; + + default: + break; + } + + if (pressed) { + switch (selection) { + case (BUTTON_YES): + result = true; + process = false; + break; + + case (BUTTON_NO): + result = false; + process = false; + break; + } + + pressed = false; + } + } + return(result); +} diff --git a/CONFDLG.H b/CONFDLG.H new file mode 100644 index 0000000..e66c9ba --- /dev/null +++ b/CONFDLG.H @@ -0,0 +1,56 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\confdlg.h_v 2.18 16 Oct 1995 16:46:06 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONFDLG.H * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 30, 1995 * + * * + * Last Update : Jan 30, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------*/ + +#ifndef CONFDLG_H +#define CONFDLG_H + +#include "gadget.h" + +class ConfirmationClass +{ + private: + enum ConfirmationClassEnum { + BUTTON_YES=1, // Button number for "Options menu" + BUTTON_NO, // Button number for "Options menu" + }; + + public: + ConfirmationClass(void) { }; + bool Process(char const * string); + bool Process(int text); +}; + +#endif diff --git a/CONNECT.CPP b/CONNECT.CPP new file mode 100644 index 0000000..fc70f20 --- /dev/null +++ b/CONNECT.CPP @@ -0,0 +1,247 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\connect.cpv 1.9 16 Oct 1995 16:48:56 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONNECT.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : May 31, 1995 [BRR] * + *-------------------------------------------------------------------------* + * Functions: * + * ConnectionClass::ConnectionClass -- class constructor * + * ConnectionClass::~ConnectionClass -- class destructor * + * ConnectionClass::Service -- main polling routine; services packets * + * ConnectionClass::Time -- gets current time * + * ConnectionClass::Command_Name -- returns name for a packet command * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef WWLIB32_H +#else +#include +#endif + +/* +********************************* Globals *********************************** +*/ +static char *ConnectionClass::Commands[PACKET_COUNT] = { + "ADATA", + "NDATA", + "ACK" +}; + + +/*************************************************************************** + * ConnectionClass::ConnectionClass -- class constructor * + * * + * If either max_retries or timeout is -1, that parameter is ignored in * + * timeout computations. If both are -1, the connection will just keep * + * retrying forever. * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the recieve queue * + * maxlen max length of an application packet * + * magicnum the packet "magic number" for this connection * + * retry_delta the time to wait between sends * + * max_retries the max # of retries allowed for a packet * + * (-1 means retry forever, based on this parameter) * + * timeout the max amount of time before we give up on a packet * + * (-1 means retry forever, based on this parameter) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +ConnectionClass::ConnectionClass (int maxlen, unsigned short magicnum, + unsigned long retry_delta, unsigned long max_retries, unsigned long timeout) +{ + /*------------------------------------------------------------------------ + Compute our maximum packet length + ------------------------------------------------------------------------*/ + MaxPacketLen = maxlen + sizeof(CommHeaderType); + + /*------------------------------------------------------------------------ + Assign the magic number + ------------------------------------------------------------------------*/ + MagicNum = magicnum; + + /*------------------------------------------------------------------------ + Initialize the retry time. This is the time that t2 - t1 must be greater + than before a retry will occur. + ------------------------------------------------------------------------*/ + RetryDelta = retry_delta; + + /*------------------------------------------------------------------------ + Set the maximum allowable retries. + ------------------------------------------------------------------------*/ + MaxRetries = max_retries; + + /*------------------------------------------------------------------------ + Set the timeout for this connection. + ------------------------------------------------------------------------*/ + Timeout = timeout; + + /*------------------------------------------------------------------------ + Allocate the packet staging buffer. This will be used to + ------------------------------------------------------------------------*/ + PacketBuf = new char[ MaxPacketLen ]; + +} /* end of ConnectionClass */ + + +/*************************************************************************** + * ConnectionClass::~ConnectionClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +ConnectionClass::~ConnectionClass () +{ + /*------------------------------------------------------------------------ + Free memory. + ------------------------------------------------------------------------*/ + delete [] PacketBuf; + +} /* end of ~ConnectionClass */ + + +/*************************************************************************** + * ConnectionClass::Service -- main polling routine; services packets * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error (connection is broken!) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Service (void) +{ + /*------------------------------------------------------------------------ + Service the Send Queue. This [re]sends packets in the Send Queue which + haven't been ACK'd yet, and if their retry timeout has expired, and + updates the FirstTime, LastTime & SendCount values in the Queue entry. + Entries that have been ACK'd should be removed. + ------------------------------------------------------------------------*/ +// if (!Service_Send_Queue()) +// return(0); + + /*------------------------------------------------------------------------ + Service the Receive Queue. This sends ACKs for packets that haven't + been ACK'd yet. Entries that the app has read, and have been ACK'd, + should be removed. + ------------------------------------------------------------------------*/ +// if (!Service_Receive_Queue()) +// return(0); + +// return(1); + + if ( Service_Send_Queue() && Service_Receive_Queue() ) { + return(1); + } else { + return(0); + } + +} /* end of Service */ + + +/*************************************************************************** + * ConnectionClass::Time -- gets current time * + * * + * INPUT: * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +unsigned long ConnectionClass::Time (void) +{ +#ifdef WWLIB32_H + return(TickCount.Time()); // Westwood Library time +#else + static struct timeb mytime; // DOS time + unsigned long msec; + + ftime(&mytime); + msec = (unsigned long)mytime.time * 1000L + (unsigned long)mytime.millitm; + return((msec / 100) * 6); +#endif + +} /* end of Time */ + + +/*************************************************************************** + * ConnectionClass::Command_Name -- returns name for given packet command * + * * + * INPUT: * + * command packet Command value to get name for * + * * + * OUTPUT: * + * ptr to command name, NULL if invalid * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +char *ConnectionClass::Command_Name(int command) +{ + if (command >= 0 && command < PACKET_COUNT) { + return(Commands[command]); + } else { + return(NULL); + } +} + + diff --git a/CONNECT.H b/CONNECT.H new file mode 100644 index 0000000..ae9e217 --- /dev/null +++ b/CONNECT.H @@ -0,0 +1,343 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\connect.h_v 1.12 16 Oct 1995 16:46:04 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONNECT.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 1, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * DESCRIPTION: * + * This class represents a single "connection" with another system. It's * + * a pure virtual base class that acts as a framework for other classes. * + * * + * This class contains a CommQueueClass member, which stores received * + * & transmitted packets. The ConnectionClass has virtual functions to * + * handle adding packets to the queue, reading them from the queue, * + * a Send routine for actually sending data, and a Receive_Packet function * + * which is used to tell the connection that a new packet has come in. * + * * + * The virtual Service routine will handle all ACK & Retry logic for * + * communicating between this system & another. Thus, any class derived * + * from this class must provide the basic ACK/Retry logic. * + * * + * THE HEADER: * + * The Connection Classes prefix every packet sent with a header that's * + * local to this class. The header contains a "Magic Number" which should * + * be unique for each product, and Packet "Code", which will tell the * + * receiving end if this is DATA, or an ACK packet, and a packet ID, which * + * is a unique numerical ID for this packet (useful for detecting resends).* + * The header is stored with each packet in the send & receive Queues; * + * it's removed before it's passed back to the application, via * + * Get_Packet() * + * * + * THE CONNECTION MANAGER: * + * It is assumed that there will be a "Connection Manager" class which * + * will handle parsing incoming packets; it will then tell the connection * + * that new packets have come in, and the connection will process them in * + * whatever way it needs to for its protocol (check for resends, handle * + * ACK packets, etc). The job of the connection manager is to parse * + * incoming packets & distribute them to the connections that need to * + * store them (for multi-connection protocols). * + * * + * NOTES ON ACK/RETRY: * + * The packet's ID is used to check for re-sends. The ID is set to the * + * Queue's total Send Count; if the receiving system checks this value, * + * and it's less than that system's Receive Count, this is a resend. * + * (ie packet 0 will be the 1st packet sent, since the Send Queue's count * + * is 0 when it's sent; as soon as it's received, the Receive Count goes * + * up to 1, and an ID of 0 then means a resend.) This scheme keeps the * + * application from seeing the same packet twice. All the Connection * + * Manager has to do is mark the resent packet as non-ACK'd. Of course, * + * the Manager doesn't have to use this value at all. * + * * + * Both DATA_ACK packets and DATA_NOACK packets must go through the Send * + * Queue when "sent", so that the SendTotal value for this system * + * will still match the ReceiveTotal value for the other system; this is * + * why a NOACK packet can't just be sent immediately; it must go through * + * the queue. * + * * + * If the protocol being used already guarantees delivery of packets, * + * no ACK is required for the packets. In this case, the connection * + * class for this protocol can overload the Service routine to avoid * + * sending ACK packets, or the Connection Manager can just mark the * + * packet as ACK'd when it adds it to the Receive Queue for the connection.* + * * + * Derived classes must provide: * + * - Init a version of Init that gives the connection * + * access to any hardware-specific values it needs * + * Must chain to the parent's Init routine. * + * - Send_Packet adds the CommHeaderType header, adds the packet * + * to the out-going queue * + * - Receive_Packet processes incoming ACK packets, detects resends,* + * adds new packets to the in-coming queue * + * - Get_Packet reads the next-available packet from the * + * receive queue * + * - Send the hardware-dependent data-sending routine * + * - Service_Send_Queue services the send queue; handles re-sends, * + * detects when outgoing packets have been ACK'd; * + * cleans out the queue of old packets * + * - Service_Receive_Queue services the receive queue; handles sending * + * ACK's for new or re-sent packets; cleans out * + * the queue of old packets * + * * + * Any other routines can be overloaded as the derived class needs. * + * * + * CLASS HIERARCHY: * + * ConnectionClass * + * | * + * | * + * -------------------------------------- * + * | | * + * | | * + * SequencedConnClass NonSequencedConnClass * + * | | * + * | | * + * IPXConnClass ------------------------ * + * | | | * + * | | | * + * IPXGlobalConnClass NullModemConnClass ModemConnClass * + * * + * * + * ConnectionClass: * + * Abstract base class. * + * Provides: Queue for sent/recv'd packets * + * PacketBuf for preparing packets * + * Timeout variables * + * Service() routine * + * * + * SequencedConnClass: * + * Abstract base class * + * Provides: * "Sequenced" ACK/Retry logic, in Service_Send_Queue() & * + * Service_Receive_Queue() routines * + * * Send_Packet(): adds header to packet, adds it to Queue * + * * Receive_Packet(): adds incoming packet to receive Queue, * + * handles incoming ACK's & resends * + * * Get_Packet(): gets packet from the receive queue * + * * + * NonSequencedConnClass: * + * Abstract base class * + * Provides: * "Non-Sequenced" ACK/Retry logic, in Service_Send_Queue() * + * & Service_Receive_Queue() routines * + * * Send_Packet(): adds header to packet, adds it to Queue * + * * Receive_Packet(): adds incoming packet to receive Queue, * + * handles incoming ACK's & resends * + * * Get_Packet(): gets packet from the receive queue * + * * + * IPXConnClass: * + * Provides: * Hardware-dependent IPX interface routines, which allow * + * Service_Send_Queue() & Service_Receive_Queue() to do * + * their job * + * * Ability to associate an IPX Address, a numerical ID, and * + * a character-string Name with a connection * + * Inherits: * Sequenced ACK/Retry logic, Service routines, Queue & * + * PacketBuf, timeout variables * + * * + * IPXGlobalConnClass: * + * Special type of IPX Connection; supports receiving packets from * + * multiple systems at once, and sending packets via Broadcast or * + * to a specific address. * + * Provides: * Specialized Receive_Packet() routine, which handles * + * receiving packets from multiple systems * + * * Specialized Send_Packet() & Get_Packet() routines, * + * which pass IPX address of destination through to * + * the application, giving the application control over * + * whether the packet will be Broadcast or sent to a * + * specific destination (embeds destination address within * + * the packet itself) * + * * Specialized Send routine, which extracts the destination * + * address from the packet * + * Inherits: * Sequenced ACK/Retry logic, Service routines, Queue & * + * PacketBuf, timeout variables, IPX-specific routines * + * * + * NullModemConnClass: * + * Provides: * Hardware-dependent Serial-communication routines, which * + * allow Service_Send_Queue() & Service_Receive_Queue() to * + * do their job * + * Inherits: * Non-Sequenced ACK/Retry logic, Service routines, Queue & * + * PacketBuf, timeout variables * + * * + * ModemConnClass: * + * Provides: * Hardware-dependent Modem-communication routines, which * + * allow Service_Send_Queue() & Service_Receive_Queue() to * + * do their job * + * Inherits: * Non-Sequenced ACK/Retry logic, Service routines, Queue & * + * PacketBuf, timeout variables * + * * + * So, do ya think this header is long enough, or what? * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CONNECTION_H +#define CONNECTION_H + +#define CONN_DEBUG 0 + +/* +********************************** Defines ********************************** +*/ +/*--------------------------------------------------------------------------- +This structure is the header prefixed to any packet sent by the application. +MagicNumber: This is a number unique to the application; it's up to the + Receive_Packet routine to check this value, to be sure we're + not getting data from some other product. This value should + be unique for each application. +Code: This will be one of the below-defined codes. +PacketID: This is a unique numerical ID for this packet. The Connection + sets this ID on all packets sent out. +---------------------------------------------------------------------------*/ +typedef struct { + unsigned short MagicNumber; + unsigned char Code; + unsigned long PacketID; +} CommHeaderType; + + +/* +***************************** Class Declaration ***************************** +*/ +class ConnectionClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + These are the possible values for the Code field of the CommHeaderType: + .....................................................................*/ + enum ConnectionEnum { + PACKET_DATA_ACK, // this is a data packet requiring an ACK + PACKET_DATA_NOACK, // this is a data packet not requiring an ACK + PACKET_ACK, // this is an ACK for a packet + PACKET_COUNT, // for computational purposes + }; + + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + ConnectionClass (int maxlen, unsigned short magicnum, + unsigned long retry_delta, unsigned long max_retries, + unsigned long timeout); + virtual ~ConnectionClass (); + + /*..................................................................... + Initialization. + .....................................................................*/ + virtual void Init (void) {}; + + /*..................................................................... + Send/Receive routines. + .....................................................................*/ + virtual int Send_Packet (void * buf, int buflen, int ack_req) = 0; + virtual int Receive_Packet (void * buf, int buflen) = 0; + virtual int Get_Packet (void * buf, int * buflen) = 0; + + /*..................................................................... + The main polling routine for the connection. Should be called as often + as possible. + .....................................................................*/ + virtual int Service (void); + + /*..................................................................... + This routine is used by the retry logic; returns the current time in + 60ths of a second. + .....................................................................*/ + static unsigned long Time (void); + + /*..................................................................... + Utility routines. + .....................................................................*/ + unsigned short Magic_Num (void) { return (MagicNum); } + unsigned long Retry_Delta (void) { return (RetryDelta); } + void Set_Retry_Delta (unsigned long delta) { RetryDelta = delta;} + unsigned long Max_Retries (void) { return (MaxRetries); } + void Set_Max_Retries (unsigned long retries) { MaxRetries = retries;} + unsigned long Time_Out (void) { return (Timeout); } + void Set_TimeOut (unsigned long t) { Timeout = t;} + unsigned long Max_Packet_Len (void) { return (MaxPacketLen); } + static char * Command_Name(int command); + + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + /*..................................................................... + Routines to service the Send & Receive queues. + .....................................................................*/ + virtual int Service_Send_Queue(void) = 0; + virtual int Service_Receive_Queue(void) = 0; + + /*..................................................................... + This routine actually performs a hardware-dependent data send. It's + pure virtual, so it >must< be defined by a derived class. + .....................................................................*/ + virtual int Send(char *buf, int buflen) = 0; + + /*..................................................................... + This is the maximum packet length, including our own internal header. + .....................................................................*/ + int MaxPacketLen; + + /*..................................................................... + Packet staging area; this is where the CommHeaderType gets tacked onto + the application's packet before it's sent. + .....................................................................*/ + char *PacketBuf; + + /*..................................................................... + This is the magic number assigned to this connection. It is the first + few bytes of any transmission. + .....................................................................*/ + unsigned short MagicNum; + + /*..................................................................... + This value determines the time delay before a packet is re-sent. + .....................................................................*/ + unsigned long RetryDelta; + + /*..................................................................... + This is the maximum number of retries allowed for a packet; if this + value is exceeded, the connection is probably broken. + .....................................................................*/ + unsigned long MaxRetries; + + /*..................................................................... + This is the total timeout for this connection; if this time is exceeded + on a packet, the connection is probably broken. + .....................................................................*/ + unsigned long Timeout; + + /*..................................................................... + Names of all packet commands + .....................................................................*/ + static char *ConnectionClass::Commands[PACKET_COUNT]; +}; + +#endif + diff --git a/CONNMGR.H b/CONNMGR.H new file mode 100644 index 0000000..a913916 --- /dev/null +++ b/CONNMGR.H @@ -0,0 +1,151 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\connmgr.h_v 1.25 02 Jan 1996 11:14:54 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONNMGR.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 3, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This is the Connection Manager base class. This is an abstract base * + * class that's just a shell for more functional derived classes. * + * The main job of the Connection Manager classes is to parse a "pool" of * + * incoming packets, which may be from different computers, and distribute * + * those packets to Connection Classes via their Receive_Packet function. * + * * + * This class should be the only access to the network/modem for the * + * application, so if the app needs any functions to access the * + * connections or the queue's, the derived versions of this class should * + * provide them. * + * * + * It's up to the derived class to define: * + * - Service: polling routine; should Service each connection * + * - Init: initialization; should perform hardware-dependent * + * initialization, then Init each connection; this function * + * isn't defined in this class, since the parameters will * + * be highly protocol-dependent) * + * - Send_Message:sends a packet across the connection (this function * + * isn't defined in this class, since the parameters will * + * be highly protocol-dependent) * + * - Get_Message: gets a message from the connection (this function * + * isn't defined in this class, since the parameters will * + * be highly protocol-dependent) * + * * + * If the derived class supports multiple connections, it should provide * + * functions for creating the connections, associating them with a name * + * or ID or both, destroying them, and sending data through all or any * + * connection. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CONNMGR_H +#define CONNMGR_H + + +/* +***************************** Class Declaration ***************************** +*/ +class ConnManClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Various useful enums: + .....................................................................*/ + enum IPXConnTag { + CONNECTION_NONE = -1, // value of an invalid connection ID + }; + + /*..................................................................... + Constructor/Destructor. These currently do nothing. + .....................................................................*/ + ConnManClass (void) {}; + virtual ~ConnManClass () {}; + + /*..................................................................... + The Service routine: + - Parses incoming packets, and adds them to the Receive Queue for the + Connection Class(s) for this protocol + - Invokes each connection's Service routine; returns an error if the + connection's Service routine indicates an error. + .....................................................................*/ + virtual int Service (void) = 0; + + /*..................................................................... + Sending & receiving data + .....................................................................*/ + virtual int Send_Private_Message (void *buf, int buflen, + int ack_req = 1, int conn_id = CONNECTION_NONE) = 0; + virtual int Get_Private_Message (void *buf, int *buflen, + int *conn_id) = 0; + + /*..................................................................... + Connection management + .....................................................................*/ + virtual int Num_Connections(void) = 0; + virtual int Connection_ID(int index) = 0; + virtual int Connection_Index(int id) = 0; + + /*..................................................................... + Queue utility routines + .....................................................................*/ + virtual int Global_Num_Send(void) = 0; + virtual int Global_Num_Receive(void) = 0; + virtual int Private_Num_Send(int id = CONNECTION_NONE) = 0; + virtual int Private_Num_Receive(int id = CONNECTION_NONE) = 0; + + /*..................................................................... + Timing management + .....................................................................*/ + virtual void Reset_Response_Time(void) = 0; + virtual unsigned long Response_Time(void) = 0; + virtual void Set_Timing (unsigned long retrydelta, + unsigned long maxretries, unsigned long timeout) = 0; + + /*..................................................................... + Debugging + .....................................................................*/ + virtual void Configure_Debug(int index, int type_offset, int type_size, + char **names, int maxnames) = 0; + virtual void Mono_Debug_Print(int index, int refresh) = 0; + /* + --------------------------- Private Interface ---------------------------- + */ + private: + /*..................................................................... + This abstract class contains no data members; but a derived class + will contain: + - An instance of one or more derived Connection Classes + - A buffer to store incoming packets + .....................................................................*/ +}; + +#endif diff --git a/CONQUER.CPP b/CONQUER.CPP new file mode 100644 index 0000000..d259f77 --- /dev/null +++ b/CONQUER.CPP @@ -0,0 +1,4013 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\conquer.cpv 2.18 16 Oct 1995 16:50:24 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONQUER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 3, 1991 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CC_Draw_Shape -- Custom draw shape handler. * + * Call_Back -- Main game maintenance callback routine. * + * Color_Cycle -- Handle the general palette color cycling. * + * Disk_Space_Available -- returns bytes of free disk space * + * Do_Record_Playback -- handles saving/loading map pos & current object * + * Fading_Table_Name -- Builds a theater specific fading table name. * + * Fetch_Techno_Type -- Convert type and ID into TechnoTypeClass pointer. * + * Force_CD_Available -- Ensures that specified CD is available. * + * Get_Radar_Icon -- Builds and alloc a radar icon from a shape file * + * Handle_Team -- Processes team selection command. * + * Handle_View -- Either records or restores the tactical view. * + * KN_To_Facing -- Converts a keyboard input number into a facing value. * + * Keyboard_Process -- Processes the tactical map input codes. * + * Language_Name -- Build filename for current language. * + * Main_Game -- Main game startup routine. * + * Main_Loop -- This is the main game loop (as a single loop). * + * Map_Edit_Loop -- a mini-main loop for map edit mode only * + * Message_Input -- allows inter-player message input processing * + * MixFileHandler -- Handles VQ file access. * + * Name_From_Source -- retrieves the name for the given SourceType * + * Play_Movie -- Plays a VQ movie. * + * Source_From_Name -- Converts ASCII name into SourceType. * + * Sync_Delay -- Forces the game into a 15 FPS rate. * + * Theater_From_Name -- Converts ASCII name into a theater number. * + * Trap_Object -- gets a ptr to object of given type & coord * + * Unselect_All -- Causes all selected objects to become unselected. * + * VQ_Call_Back -- Maintenance callback used for VQ movies. * + * Validate_Error -- prints an error message when an object fails validation * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "tcpip.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ccdde.h" + +#define SHAPE_TRANS 0x40 + +void *Get_Shape_Header_Data(void *ptr); + +/**************************************** +** Function prototypes for this module ** +*****************************************/ +bool Main_Loop(); +void Keyboard_Process(KeyNumType & input); +#ifndef DEMO +static void Message_Input(KeyNumType &input); +#endif +static bool Color_Cycle(void); +bool Map_Edit_Loop(void); +void Trap_Object(void); + +#ifdef CHEAT_KEYS +void Heap_Dump_Check( char *string ); +void Dump_Heap_Pointers( void ); +void Error_In_Heap_Pointers( char *string ); +#endif +static void Do_Record_Playback(void); +extern void Register_Game_Start_Time(void); +extern void Register_Game_End_Time(void); +extern void Send_Statistics_Packet(void); +extern "C" { + extern char *__nheapbeg; +} +bool InMainLoop = false; + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) +#endif + +/*********************************************************************************************** + * Main_Game -- Main game startup routine. * + * * + * This is the first official routine of the game. It handles game initialization and * + * the main game loop control. * + * * + * Initialization: * + * - Init_Game handles one-time-only inits * + * - Select_Game is responsible for initializations required for each new game played * + * (these may be different depending on whether a multiplayer game is selected, and * + * other parameters) * + * - This routine performs any un-inits required, both for each game played, and one-time * + * * + * INPUT: argc -- Number of command line arguments (including program name itself). * + * * + * argv -- Array of command line argument pointers. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/01/1994 JLB : Created. * + *=============================================================================================*/ +extern int TotalLocks; +extern bool Spawn_WChat(bool can_launch); +extern bool SpawnedFromWChat; +void Main_Game(int argc, char *argv[]) +{ + bool fade = false; // don't fade title screen the first time through + + /* + ** Perform one-time-only initializations + */ + if (!Init_Game(argc, argv)) { + return; + } + + CCDebugString ("C&C95 - Game initialisation complete.\n"); + /* + ** Game processing loop: + ** 1) Select which game to play, or whether to exit (don't fade the palette + ** on the first game selection, but fade it in on subsequent calls) + ** 2) Invoke either the main-loop routine, or the editor-loop routine, + ** until they indicate that the user wants to exit the scenario. + */ + while (Select_Game(fade)) { + ScenarioInit = 0; // Kludge. +// Theme.Queue_Song(THEME_PICK_ANOTHER); + + fade = true; + + /* + ** Make the game screen visible, clear the keyboard buffer of spurious + ** values, and then show the mouse. This PRESUMES that Select_Game() has + ** told the map to draw itself. + */ + Fade_Palette_To(GamePalette, FADE_PALETTE_MEDIUM, NULL); + Keyboard::Clear(); + + /* + ** Only show the mouse if we're not playing back a recording. + */ + if (PlaybackGame) { + Hide_Mouse(); + } else { + Show_Mouse(); + } + + SpecialDialog = SDLG_NONE; + //Start_Profiler(); + if (GameToPlay == GAME_INTERNET){ + Register_Game_Start_Time(); + GameStatisticsPacketSent = false; + PacketLater = NULL; + ConnectionLost = false; + }else{ + DDEServer.Disable(); + } + + InMainLoop = true; + +#ifdef SCENARIO_EDITOR + /* + ** Scenario-editor version of main-loop processing + */ + for (;;) { + + /* + ** Non-scenario-editor-mode: call the game's main loop + */ + if (!Debug_Map) { + TotalLocks=0; + if (Main_Loop()) { + break; + } + + if (SpecialDialog != SDLG_NONE) { + //Stop_Profiler(); + switch (SpecialDialog) { + case SDLG_SPECIAL: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + Special_Dialog(); + Map.Revert_Mouse_Shape(); + SpecialDialog = SDLG_NONE; + break; + + case SDLG_OPTIONS: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + Options.Process(); + Map.Revert_Mouse_Shape(); + SpecialDialog = SDLG_NONE; + break; + + case SDLG_SURRENDER: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + if (Surrender_Dialog()) { + OutList.Add(EventClass(EventClass::DESTRUCT)); + } + SpecialDialog = SDLG_NONE; + Map.Revert_Mouse_Shape(); + break; + + default: + break; + } + } + } else { + + /* + ** Scenario-editor-mode: call the editor's main loop + */ + if (Map_Edit_Loop()) { + break; + } + } + } +#else + /* + ** Non-editor version of main-loop processing + */ + for (;;) { + + /* + ** Call the game's main loop + */ + TotalLocks=0; + if (Main_Loop()) { + break; + } + + /* + ** If the SpecialDialog flag is set, invoke the given special dialog. + ** This must be done outside the main loop, since the dialog will call + ** Main_Loop(), allowing the game to run in the background. + */ + if (SpecialDialog != SDLG_NONE) { + //Stop_Profiler(); + switch (SpecialDialog) { + case SDLG_SPECIAL: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + Special_Dialog(); + Map.Revert_Mouse_Shape(); + SpecialDialog = SDLG_NONE; + break; + + case SDLG_OPTIONS: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + Options.Process(); + Map.Revert_Mouse_Shape(); + SpecialDialog = SDLG_NONE; + break; + + case SDLG_SURRENDER: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + if (Surrender_Dialog()) { + OutList.Add(EventClass(EventClass::DESTRUCT)); + } + SpecialDialog = SDLG_NONE; + Map.Revert_Mouse_Shape(); + break; + + default: + break; + } + } + } +#endif + //Stop_Profiler(); + InMainLoop = false; + + if (!GameStatisticsPacketSent && PacketLater){ + Send_Statistics_Packet(); + } + + /* + ** Scenario is done; fade palette to black + */ + Fade_Palette_To(BlackPalette, FADE_PALETTE_SLOW, NULL); + VisiblePage.Clear(); + +#ifndef DEMO + /* + ** Un-initialize whatever needs it, for each game played. + ** + ** Shut down either the modem or network; they'll get re-initialized if + ** the user selections those options again in Select_Game(). This + ** "re-boots" the modem & network code, which I currently feel is safer + ** than just letting it hang around. + ** (Skip this step if we're in playback mode; the modem or net won't have + ** been initialized in that case.) + */ + if ( (RecordGame && !SuperRecord) || PlaybackGame) { + RecordFile.Close(); + } + + if (!PlaybackGame){ + + switch (GameToPlay){ + case GAME_NULL_MODEM: + case GAME_MODEM: + Modem_Signoff(); + break; + + case GAME_IPX: + Shutdown_Network(); + break; + + case GAME_INTERNET: + //Winsock.Close(); + break; + } + } + + + /* + ** If we're playing back, the mouse will be hidden; show it. + ** Also, set all variables back to normal, to return to the main menu. + */ + if (PlaybackGame) { + Show_Mouse(); + GameToPlay = GAME_NORMAL; + PlaybackGame = 0; + } + + + /* + ** If we were spawned from WChat then dont go back to the main menu - just quit + ** + ** New: If spawned from WChat then maximise WChat and go back to the main menu after all + */ +#ifdef FORCE_WINSOCK + if (Special.IsFromWChat){ + Shutdown_Network(); // Clear up the pseudo IPX stuff + Winsock.Close(); + Special.IsFromWChat = false; + SpawnedFromWChat = false; + DDEServer.Delete_MPlayer_Game_Info(); //Make sure we dont use the same start packet twice + GameToPlay = GAME_NORMAL; //Have to do this or we will got straight to the multiplayer menu + Spawn_WChat(false); //Will switch back to Wchat. It must be there because its been poking us + //break; + } +#endif //FORCE_WINSOCK + +#endif //DEMO + + } + +#ifdef DEMO + Hide_Mouse(); + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, NULL); + Load_Title_Screen("DEMOPIC.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Fade_Palette_To(Palette, FADE_PALETTE_MEDIUM, NULL); + Clear_KeyBuffer(); + Get_Key(); + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, NULL); +// Show_Mouse(); +#else + + /* + ** Free the scenario description buffers + */ + Free_Scenario_Descriptions(); +#endif + +#ifndef NOMEMCHECK + Uninit_Game(); +#endif + +} + + +/*********************************************************************************************** + * Keyboard_Process -- Processes the tactical map input codes. * + * * + * This routine is used to process the input codes while the player * + * has the tactical map displayed. It handles all the keys that * + * are appropriate to that mode. * + * * + * INPUT: input -- Input code as returned from Input_Num(). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1992 JLB : Created. * + * 07/04/1995 JLB : Handles team and map control hotkeys. * + *=============================================================================================*/ +extern int DebugColour; +void Keyboard_Process(KeyNumType &input) +{ + ObjectClass * obj; + int index; + + /* + ** Don't do anything if there is not keyboard event. + */ + if (input == KN_NONE) { + return; + } + +#ifndef DEMO + /* + ** For network & modem, process user input for inter-player messages. + */ + Message_Input(input); +#endif + /* + ** Use WWKEY values because KN values have WWKEY_VK_BIT or'd in with them + ** and we need WWKEY_VK_BIT to still be set if it is. + */ + KeyNumType plain = input & ~(WWKEY_SHIFT_BIT|WWKEY_ALT_BIT|WWKEY_CTRL_BIT); + +#ifdef CHEAT_KEYS + + if (Debug_Flag) { + switch (input) { + case (int)KN_M|(int)KN_SHIFT_BIT: + case (int)KN_M|(int)KN_ALT_BIT: + case (int)KN_M|(int)KN_CTRL_BIT: + PlayerPtr->Credits += 10000; + break; + + default: + break; + } + } +#endif + +#ifdef VIRGIN_CHEAT_KEYS + if (Debug_Playtest && input == (KN_W|KN_ALT_BIT)) { + PlayerPtr->Blockage = false; + PlayerPtr->Flag_To_Win(); + } +#endif + +//#ifdef CHEAT_KEYS + if (/*Debug_Playtest && */input == (KN_W|KN_ALT_BIT)) { + PlayerPtr->Blockage = false; + PlayerPtr->Flag_To_Win(); + } + + if (Debug_Flag && input == KN_SLASH) { + if (GameToPlay != GAME_NORMAL) { + SpecialDialog = SDLG_SPECIAL; + input = KN_NONE; + } else { + Special_Dialog(); + } + } +//#endif + + /* + ** If the options key(s) were pressed, then bring up the options screen. + */ + if (input == KN_SPACE || input == KN_ESC) { + Map.Help_Text(TXT_NONE); // Turns off help text. + Queue_Options(); + input = KN_NONE; + //DebugColour++; + //DebugColour &=7; + } + + /* + ** Process prerecorded team selection. This will be an addative select + ** if the SHIFT key is held down. It will create the team if the + ** CTRL or ALT key is held down. + */ + int action = 0; + if (input & WWKEY_SHIFT_BIT) action = 1; + if (input & WWKEY_ALT_BIT) action = 3; + if (input & WWKEY_CTRL_BIT) action = 2; + + switch (KN_To_VK(plain)) { + + /* + ** Center the map around the currently selected objects. If no + ** objects are selected, then fall into the home case. + */ + case VK_HOME: + if (CurrentObject.Count()) { + Map.Center_Map(); + Map.Flag_To_Redraw(true); + break; + } + // Fall into next case. + + /* + ** Center the map about the construction yard or construction vehicle + ** if one is present. + */ + case VK_H: + for (index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + + if (unit && !unit->IsInLimbo && unit->House == PlayerPtr && *unit == UNIT_MCV) { + Unselect_All(); + unit->Select(); + break; + } + } + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + if (building && !building->IsInLimbo && building->House == PlayerPtr && *building == STRUCT_CONST) { + Unselect_All(); + building->Select(); + break; + } + } + Map.Center_Map(); + Map.Flag_To_Redraw(true); + break; + +#ifdef CHEAT_KEYS + /* + ** Toggle free scrolling mode. + */ + case VK_F: + Options.IsFreeScroll = (Options.IsFreeScroll == false); + break; +#endif + + /* + ** If the "N" key is pressed, then select the next object. + */ + case VK_N: + if (action) { + obj = Map.Prev_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + } else { + obj = Map.Next_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + } + if (obj) { + Unselect_All(); + obj->Select(); + Map.Center_Map(); + Map.Flag_To_Redraw(true); + } + break; + + /* + ** For multiplayer, 'R' pops up the surrender dialog. + */ + case VK_R: + if (/*GameToPlay != GAME_NORMAL &&*/ !PlayerPtr->IsDefeated) { + SpecialDialog = SDLG_SURRENDER; + input = KN_NONE; + } + break; + + /* + ** Handle making and breaking alliances. + */ + case VK_A: + if (GameToPlay != GAME_NORMAL || Debug_Flag) { + if (CurrentObject.Count() && !PlayerPtr->IsDefeated) { + if (CurrentObject[0]->Owner() != PlayerPtr->Class->House) { + OutList.Add(EventClass(EventClass::ALLY, CurrentObject[0]->Owner())); + } + } + } + break; + + /* + ** Control the remembered tactical location. + */ + case VK_F7: + case VK_F8: + case VK_F9: + case VK_F10: + if (!Debug_Map) { + Handle_View(KN_To_VK(plain) - VK_F7, action); + } + break; +#if (0) + case VK_F11: + Winsock.Set_Protocol_UDP(FALSE); + break; + + case VK_F12: + Winsock.Set_Protocol_UDP(TRUE); + break; +#endif //(0) + + + /* + ** Control the custom team select state. + */ + case VK_1: + case VK_2: + case VK_3: + case VK_4: + case VK_5: + case VK_6: + case VK_7: + case VK_8: + case VK_9: + case VK_0: + Handle_Team(KN_To_VK(plain) - VK_1, action); + break; + + /* + ** All selected units will go into idle mode. + */ + case VK_S: + if (CurrentObject.Count()) { + for (int index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech && (tech->Can_Player_Move() || (tech->Can_Player_Fire() && + tech->What_Am_I() != RTTI_BUILDING))) { + OutList.Add(EventClass(EventClass::IDLE, tech->As_Target())); + } + } + } + break; + + /* + ** All selected units will attempt to scatter. + */ + case VK_X: + if (CurrentObject.Count()) { + for (int index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech && tech->Can_Player_Move()) { + OutList.Add(EventClass(EventClass::SCATTER, tech->As_Target())); + } + } + } + break; + + /* + ** All selected units will attempt to go into guard area mode. + */ + case VK_G: + if (CurrentObject.Count()) { + for (int index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech && tech->Can_Player_Move() && tech->Can_Player_Fire()) { + OutList.Add(EventClass(tech->As_Target(), MISSION_GUARD_AREA)); + } + } + } + break; + + default: + break; + } + +#ifdef NEVER + FacingType facing = KN_To_Facing(input); + + /* + ** Scroll the map according to the cursor key pressed. + */ + if (facing != FACING_NONE) { + Map.Scroll_Map(facing); + input = 0; + facing = FACING_NONE; + } +#endif + +#ifdef NEVER + /* + ** If the key is pressed, then select the next object. + */ + if (input == KN_TAB) { + ObjectClass * obj = Map.Next_Object(CurrentObject); + if (obj) { + if (CurrentObject) { + CurrentObject->Unselect(); + } + obj->Select(); + } + } +#endif + +#ifdef CHEAT_KEYS + if (Debug_Flag && input && (input & KN_RLSE_BIT) == 0) { + Debug_Key(input); + } +#endif +} + + +#ifndef DEMO +/*********************************************************************************************** + * Message_Input -- allows inter-player message input processing * + * * + * INPUT: * + * input key value * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * MAX_MESSAGE_LENGTH has increased over the DOS version. COMPAT_MESSAGE_LENGTH reflects * * + * the length of the DOS message and also the length of the message in the packet header. * + * To allow transmission of longer messages I split the message into COMPAT_MESSAGE_LENGTH-4 * + * sized chunks and use the extra space after the zero terminator to specify which segment * + * of the whole message this is and also to supply a crc for the string. * + * This allows message segments to arrive out of order and still be displayed correctly. * + * * + * HISTORY: * + * 05/22/1995 BRR : Created. * + * 03/26/1995 ST : Modified to break up longer messages into multiple packets * + *=============================================================================================*/ +static void Message_Input(KeyNumType &input) +{ + int rc; + char txt[MAX_MESSAGE_LENGTH+12]; + int id; + SerialPacketType *serial_packet; + int i; + int message_length; + int sent_so_far; + unsigned short magic_number; + unsigned short crc; + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + + /* + ** Check keyboard input for a request to send a message. + ** The 'to' argument for Add_Edit is prefixed to the message buffer; the + ** message buffer is big enough for the 'to' field plus MAX_MESSAGE_LENGTH. + ** To send the message, calling Get_Edit_Buf retrieves the buffer minus the + ** 'to' portion. At the other end, the buffer allocated to display the + ** message must be MAX_MESSAGE_LENGTH plus the size of "From: xxx (house)". + */ + if (input >= KN_F1 && input < (KN_F1 + MPlayerMax) && + Messages.Get_Edit_Buf()==NULL) { + memset (txt, 0, 40); + + /* + ** For a serial game, send a message on F1 or F4; set 'txt' to the + ** "Message:" string & add an editable message to the list. + */ + if (GameToPlay==GAME_NULL_MODEM + || GameToPlay==GAME_MODEM){ + //|| GameToPlay == GAME_INTERNET) { + if (input==KN_F1 || input==(KN_F1 + MPlayerMax - 1)) { + + strcpy(txt,Text_String(TXT_MESSAGE)); // "Message:" + + Messages.Add_Edit (MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 180*factor); + + Map.Flag_To_Redraw(false); + } + } else { + + /* + ** For a network game: + ** F1-F3 = "To (house):" (only allowed if we're not in ObiWan mode) + ** F4 = "To All:" + */ + if (GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) { + if (input==(KN_F1 + MPlayerMax - 1) && Messages.Get_Edit_Buf()==NULL) { + + MessageAddress = IPXAddressClass(); // set to broadcast + strcpy(txt,Text_String(TXT_TO_ALL)); // "To All:" + + Messages.Add_Edit(MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 180*factor); + + Map.Flag_To_Redraw(false); + } else { + if (Messages.Get_Edit_Buf()==NULL) { + if ((input - KN_F1) < Ipx.Num_Connections() && !MPlayerObiWan) { + + id = Ipx.Connection_ID(input - KN_F1); + MessageAddress = (*(Ipx.Connection_Address (id))); + sprintf(txt,Text_String(TXT_TO),Ipx.Connection_Name(id)); + + Messages.Add_Edit(MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 180*factor); + + Map.Flag_To_Redraw(false); + } + } + } + } + } + } + + /* + ** Function key input is meaningless beyond this point + */ + if (input >= KN_F1 && input <= KN_F10) return; + if (input >= KN_F11 && input <= KN_F12) return; + + /* + ** Process message-system input; send the message out if RETURN is hit. + */ + rc = Messages.Input(input); + + /* + ** If a single character has been added to an edit buffer, update the display. + */ + if (rc == 1) { + Map.Flag_To_Redraw(false); + } + + /* + ** If backspace was hit, redraw the map. This assumes the map is going to + ** completely refresh all cells covered by the messages. Set DisplayClass's + ** IsToRedraw to true to tell it to re-compute the cells that it needs to + ** redraw. + */ + if (rc==2) { + Map.Flag_To_Redraw(false); + Map.DisplayClass::IsToRedraw = true; + } + + + /* + ** Send a message + */ + if (rc==3) { + /*..................................................................... + Store this message in our LastMessage buffer; the computer may send + us a version of it later. + .....................................................................*/ + if (strlen(Messages.Get_Edit_Buf())) { + strcpy(LastMessage,Messages.Get_Edit_Buf()); + } + + message_length = strlen(Messages.Get_Edit_Buf()); + + long actual_message_size; + char *the_string; + + /* + ** Serial game: fill in a SerialPacketType & send it. + ** (Note: The size of the SerialPacketType.Command must be the same as + ** the EventClass.Type!) + */ + if (GameToPlay==GAME_NULL_MODEM + || GameToPlay==GAME_MODEM){ + //|| GameToPlay==GAME_INTERNET) { + + sent_so_far = 0; + magic_number = MESSAGE_HEAD_MAGIC_NUMBER; + crc = (unsigned short) (Calculate_CRC(Messages.Get_Edit_Buf(), message_length) & 0xffff); + + while (sent_so_far < message_length){ + + serial_packet = (SerialPacketType *)NullModem.BuildBuf; + + serial_packet->Command = SERIAL_MESSAGE; + strcpy (serial_packet->Name, MPlayerName); + memcpy (serial_packet->Message, Messages.Get_Edit_Buf()+sent_so_far, COMPAT_MESSAGE_LENGTH-5); + + /* + ** Steve I's stuff for splitting message on word boundries + */ + actual_message_size = COMPAT_MESSAGE_LENGTH - 5; + + /* Start at the end of the message and find a space with 10 chars. */ + the_string = serial_packet->Message; + while ( (COMPAT_MESSAGE_LENGTH -5) -actual_message_size < 10 && + the_string[actual_message_size] != ' '){ + --actual_message_size; + } + if ( the_string[actual_message_size] == ' ' ){ + + /* Now delete the extra characters after the space (they musnt print) */ + for ( int i=0 ; i< (COMPAT_MESSAGE_LENGTH-5) - actual_message_size; i++ ){ + the_string[i + actual_message_size] = 0xff; + } + }else{ + actual_message_size = COMPAT_MESSAGE_LENGTH - 5; + } + + + *(serial_packet->Message + COMPAT_MESSAGE_LENGTH-5) = 0; + /* + ** Flag this message segment as either a message head or a message tail. + */ + *((unsigned short*)(serial_packet->Message + COMPAT_MESSAGE_LENGTH-4)) = magic_number; + *((unsigned short*)(serial_packet->Message + COMPAT_MESSAGE_LENGTH-2)) = crc; + serial_packet->ID = MPlayerLocalID; + + NullModem.Send_Message(NullModem.BuildBuf, sizeof(SerialPacketType), 1); + + magic_number++; + sent_so_far += actual_message_size; //COMPAT_MESSAGE_LENGTH-5; + } + + } else { + + /* + ** Network game: fill in a GlobalPacketType & send it. + */ + if (GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) { + + sent_so_far = 0; + magic_number = MESSAGE_HEAD_MAGIC_NUMBER; + crc = (unsigned short) (Calculate_CRC(Messages.Get_Edit_Buf(), message_length) & 0xffff); + + while (sent_so_far < message_length){ + + GPacket.Command = NET_MESSAGE; + strcpy (GPacket.Name, MPlayerName); + memcpy (GPacket.Message.Buf, Messages.Get_Edit_Buf()+sent_so_far, COMPAT_MESSAGE_LENGTH-5); + + /* + ** Steve I's stuff for splitting message on word boundries + */ + actual_message_size = COMPAT_MESSAGE_LENGTH - 5; + + /* Start at the end of the message and find a space with 10 chars. */ + the_string = GPacket.Message.Buf; + while ( (COMPAT_MESSAGE_LENGTH -5) -actual_message_size < 10 && + the_string[actual_message_size] != ' '){ + --actual_message_size; + } + if ( the_string[actual_message_size] == ' ' ){ + + /* Now delete the extra characters after the space (they musnt print) */ + for ( int i=0 ; i< (COMPAT_MESSAGE_LENGTH-5) - actual_message_size; i++ ){ + the_string[i + actual_message_size] = 0xff; + } + }else{ + actual_message_size = COMPAT_MESSAGE_LENGTH - 5; + } + + *(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-5) = 0; + /* + ** Flag this message segment as either a message head or a message tail. + */ + *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-4)) = magic_number; + *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-2)) = crc; + + GPacket.Message.ID = MPlayerLocalID; + GPacket.Message.NameCRC = Compute_Name_CRC(MPlayerGameName); + + /* + ** If 'F4' was hit, MessageAddress will be a broadcast address; send + ** the message to every player we have a connection with. + */ + if (MessageAddress.Is_Broadcast()) { + for (i = 0; i < Ipx.Num_Connections(); i++) { + Ipx.Send_Global_Message(&GPacket, sizeof(GlobalPacketType), 1, + Ipx.Connection_Address(Ipx.Connection_ID(i))); + Ipx.Service(); + } + } else { + + /* + ** Otherwise, MessageAddress contains the exact address to send to. + ** Send to that address only. + */ + Ipx.Send_Global_Message(&GPacket, sizeof(GlobalPacketType), 1, + &MessageAddress); + Ipx.Service(); + } + + magic_number++; + sent_so_far += actual_message_size; //COMPAT_MESSAGE_LENGTH-5; + } + } + + } + + /* + ** Tell the map to completely update itself, since a message is now missing. + */ + Map.Flag_To_Redraw(true); + } +} +#endif + + +/*********************************************************************************************** + * Color_Cycle -- Handle the general palette color cycling. * + * * + * This is a maintenance routine that handles the color cycling. It should be called as * + * often as necessary to achieve smooth color cycling effects -- at least 8 times a second. * + * * + * INPUT: none * + * * + * OUTPUT: true if palette changed * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 06/10/1994 JLB : Uses new cycle color values. * + * 12/21/1994 JLB : Handles text fade color. * + *=============================================================================================*/ +bool Color_Cycle(void) +{ + static CountDownTimerClass _timer(BT_SYSTEM,0L); + static CountDownTimerClass _ftimer(BT_SYSTEM,0L); + static bool _up = false; + bool changed = false; + + /* + ** Process the fading white color. It is used for the radar box and other glowing + ** game interface elements. + */ + if (!_ftimer.Time()) { + _ftimer.Set(TIMER_SECOND/8); + + /* + ** Pulse the pulsing text color. + */ + #define STEP_RATE 5 + if (_up) { + GamePalette[767] += STEP_RATE; + GamePalette[766] += STEP_RATE; + GamePalette[765] += STEP_RATE; + if (GamePalette[767] > MAX_CYCLE_COLOR) { + GamePalette[767] = MAX_CYCLE_COLOR; + GamePalette[766] = MAX_CYCLE_COLOR; + GamePalette[765] = MAX_CYCLE_COLOR; + _up = false; + } + } else { + GamePalette[767] -= STEP_RATE; + GamePalette[766] -= STEP_RATE; + GamePalette[765] -= STEP_RATE; + if ((unsigned)GamePalette[767] < MIN_CYCLE_COLOR) { + GamePalette[767] = MIN_CYCLE_COLOR; + GamePalette[766] = MIN_CYCLE_COLOR; + GamePalette[765] = MIN_CYCLE_COLOR; + _up = true; + } + } + changed = true; + } + + /* + ** Process the color cycling effects -- water. + */ + if (!_timer.Time()) { + unsigned char colors[3]; + + _timer.Set(TIMER_SECOND/4); + + memmove(colors, &GamePalette[(CYCLE_COLOR_START+CYCLE_COLOR_COUNT-1)*3], sizeof(colors)); + memmove(&GamePalette[(CYCLE_COLOR_START+1)*3], &GamePalette[CYCLE_COLOR_START*3], (CYCLE_COLOR_COUNT-1)*3); + memmove(&GamePalette[CYCLE_COLOR_START*3], colors, sizeof(colors)); + changed = true; + } + + /* + ** If any of the processing functions changed the palette, then this palette must be + ** passed to the system. + */ + if (changed) { + Wait_Vert_Blank(); + Set_Palette(GamePalette); + return (true); + } + return (false); +} + + +/*********************************************************************************************** + * Call_Back -- Main game maintenance callback routine. * + * * + * This routine handles all the "real time" processing that needs to * + * occur. This includes palette fading and sound updating. It needs * + * to be called as often as possible. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +void Call_Back(void) +{ +#ifndef DEMO + int i; + int id; + int color; + unsigned short magic_number; + unsigned short crc; +#endif + + /* + ** Score maintenance + */ + if (SampleType) { + Theme.AI(); + Speak_AI(); + } + +#ifndef DEMO + /* + ** Network maintenance + */ + if (GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) { + + Ipx.Service(); + + /* + ** Read packets only if the game is "closed", so we don't steal global + ** messages from the connection dialogs. + */ + if (!NetOpen) { + if (Ipx.Get_Global_Message (&GPacket, &GPacketlen, &GAddress, &GProductID)) { + if (GProductID == IPXGlobalConnClass::COMMAND_AND_CONQUER) { + + /* + ** If this is another player signing off, remove the connection & + ** mark that player's house as non-human, so the computer will take + ** it over. + */ + if (GPacket.Command == NET_SIGN_OFF) { + for (i = 0; i < Ipx.Num_Connections(); i++) { + + id = Ipx.Connection_ID(i); + + if (!strcmp (GPacket.Name, Ipx.Connection_Name(id) ) && + GAddress == (*Ipx.Connection_Address(id))) { + + CCDebugString ("C&C95 = Destroying connection due to sign off\n"); + Destroy_Connection (id,0); + } + } + } else { + + /* + ** Process a message from another user. + */ + if (GPacket.Command == NET_MESSAGE) { + bool msg_ok = false; + char txt[80]; + + /* + ** If NetProtect is set, make sure this message came from within + ** this game. + */ + if (!NetProtect) { + msg_ok = true; + } else { + if (GPacket.Message.NameCRC == Compute_Name_CRC(MPlayerGameName)) { + msg_ok = true; + } else { + msg_ok = false; + } + } + + if (msg_ok) { + sprintf(txt,Text_String (TXT_FROM), GPacket.Name, GPacket.Message.Buf); + magic_number = *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-4)); + crc = *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-2)); + color = MPlayerID_To_ColorIndex(GPacket.Message.ID); + Messages.Add_Message(txt, MPlayerTColors[color], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, magic_number, crc); + + /* + ** Tell the map to do a partial update (just to force the messages + ** to redraw). + */ + Map.Flag_To_Redraw(false); + + /* + ** Save this message in our last-message buffer + */ + if (strlen(GPacket.Message.Buf)) { + strcpy(LastMessage, GPacket.Message.Buf); + } + } + } else { + Process_Global_Packet(&GPacket, &GAddress); + } + } + } + } + } + } + + /* + ** Modem and Null Modem maintenance + */ + if (GameToPlay == GAME_NULL_MODEM + || ((GameToPlay == GAME_MODEM) && ModemService)){ + //|| GameToPlay == GAME_INTERNET) { + NullModem.Service(); + } +#endif +} + + +/*********************************************************************************************** + * Language_Name -- Build filename for current language. * + * * + * This routine attaches a language specific suffix to the base * + * filename provided. Typical use of this is when loading language * + * specific files at game initialization time. * + * * + * INPUT: basename -- Base name to append language specific * + * extension to. * + * * + * OUTPUT: Returns with pointer to completed filename. * + * * + * WARNINGS: The return pointer value is valid only until the next time * + * this routine is called. * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +char const *Language_Name(char const *basename) +{ + static char _fullname[_MAX_FNAME+_MAX_EXT]; + + if (!basename) return(NULL); + + sprintf(_fullname, "%s.ENG", basename); + return(_fullname); +} + + +/*********************************************************************************************** + * Source_From_Name -- Converts ASCII name into SourceType. * + * * + * This routine is used to convert an ASCII name representing a * + * SourceType into the actual SourceType value. Typically, this is * + * used when processing the scenario INI file. * + * * + * INPUT: name -- The ASCII source name to process. * + * * + * OUTPUT: Returns with the SourceType represented by the name * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/17/1994 JLB : Created. * + *=============================================================================================*/ +SourceType Source_From_Name(char const *name) +{ + if (name) { + for (SourceType source = SOURCE_FIRST; source < SOURCE_COUNT; source++) { + if (stricmp(SourceName[source], name) == 0) { + return(source); + } + } + } + return(SOURCE_NONE); +} + + +/*********************************************************************************************** + * Name_From_Source -- retrieves the name for the given SourceType * + * * + * INPUT: * + * source SourceType to get the name for * + * * + * OUTPUT: * + * name of SourceType * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/15/1994 BR : Created. * + *=============================================================================================*/ +char const *Name_From_Source(SourceType source) +{ + if ((unsigned)source < SOURCE_COUNT) { + return(SourceName[source]); + } + return("None"); +} + + +/*********************************************************************************************** + * Theater_From_Name -- Converts ASCII name into a theater number. * + * * + * This routine converts an ASCII representation of a theater and converts it into a * + * matching theater number. If no match was found, then THEATER_NONE is returned. * + * * + * INPUT: name -- Pointer to ASCII name to convert. * + * * + * OUTPUT: Returns with the name converted into a theater number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/01/1994 JLB : Created. * + *=============================================================================================*/ +TheaterType Theater_From_Name(char const *name) +{ + TheaterType index; + + if (name) { + for (index = THEATER_FIRST; index < THEATER_COUNT; index++) { + if (stricmp(name, Theaters[index].Name) == 0) { + return(index); + } + } + } + return(THEATER_NONE); +} + + +/*********************************************************************************************** + * KN_To_Facing -- Converts a keyboard input number into a facing value. * + * * + * This routine determine which compass direction is represented by the keyboard value * + * provided. It is used for map scrolling and other directional control operations from * + * the keyboard. * + * * + * INPUT: input -- The KN number to convert. * + * * + * OUTPUT: Returns with the facing type that the keyboard number represents. If it could * + * not be translated, then FACING_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +FacingType KN_To_Facing(int input) +{ + input &= ~(KN_ALT_BIT|KN_SHIFT_BIT|KN_CTRL_BIT); + switch (input) { + case KN_LEFT: + return(FACING_W); + + case KN_RIGHT: + return(FACING_E); + + case KN_UP: + return(FACING_N); + + case KN_DOWN: + return(FACING_S); + + case KN_UPLEFT: + return(FACING_NW); + + case KN_UPRIGHT: + return(FACING_NE); + + case KN_DOWNLEFT: + return(FACING_SW); + + case KN_DOWNRIGHT: + return(FACING_SE); + } + return(FACING_NONE); +} + + +/*********************************************************************************************** + * Sync_Delay -- Forces the game into a 15 FPS rate. * + * * + * This routine will wait until the timer for the current frame has expired before * + * returning. It is called at the end of every game loop in order to force the game loop * + * to run at a fixed rate. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Will delay for up to 1/15 of a second. * + * * + * HISTORY: * + * 01/04/1995 JLB : Created. * + * 03/06/1995 JLB : Fixed. * + *=============================================================================================*/ +static void Sync_Delay(void) +{ + /* + ** Delay one tick and keep a record that one tick was "wasted" here. + ** This accumulates into a running histogram of performance. + */ + SpareTicks += FrameTimer.Time(); + while (FrameTimer.Time()) { + Color_Cycle(); + Call_Back(); + + if (SpecialDialog == SDLG_NONE) { + WWMouse->Erase_Mouse(&HidPage, TRUE); + KeyNumType input = KN_NONE; + int x, y; + WWMouse->Erase_Mouse(&HidPage, TRUE); + Map.Input(input, x, y); + if (input) { + Keyboard_Process(input); + } + Map.Render(); + } + } + Color_Cycle(); + Call_Back(); +} + + +/*********************************************************************************************** + * Main_Loop -- This is the main game loop (as a single loop). * + * * + * This function will perform one game loop. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Should the game end? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/01/1994 JLB : Created. * + *=============================================================================================*/ +extern void Check_For_Focus_Loss(void); +void Reallocate_Big_Shape_Buffer(void); + + +bool Main_Loop() +{ + KeyNumType input; // Player input. + int x; + int y; + int framedelay; + +// InMainLoop = true; + + /* + ** I think I'm gonna cry if this makes it work + */ + if (Get_Mouse_State())Show_Mouse(); + + /* + ** Call the focus loss handler + */ + Check_For_Focus_Loss(); + + /* + ** Allocate extra memory for uncompressed shapes as needed + */ + Reallocate_Big_Shape_Buffer(); + + /* + ** Sync-bug trapping code + */ + if (Frame >= TrapFrame) { + Trap_Object(); + } + + // + // Initialize our AI processing timer + // + ProcessTimer.Set(0, true); + + +#if 1 + if (TrapCheckHeap) { + Debug_Trap_Check_Heap = true; + } +#endif + +#ifdef CHEAT_KEYS + Heap_Dump_Check( "After Trap" ); + + /* + ** Update the running status debug display. + */ + Self_Regulate(); +#endif + + /* + ** If there is no theme playing, but it looks like one is required, then start one + ** playing. This is usually the symptom of there being no transition score. + */ + if (SampleType && Theme.What_Is_Playing() == THEME_NONE) { + Theme.Queue_Song(THEME_PICK_ANOTHER); + } + + /* + ** Setup the timer so that the Main_Loop function processes at the correct rate. + */ + if (GameToPlay != GAME_NORMAL && CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + framedelay = 60 / DesiredFrameRate; + FrameTimer.Set(framedelay); + } else { + FrameTimer.Set(Options.GameSpeed); + } + + /* + ** Update the display, unless we're inside a dialog. + */ + if (!PlaybackGame) { + if (SpecialDialog == SDLG_NONE && GameInFocus) { + + WWMouse->Erase_Mouse(&HidPage, TRUE); + Map.Input(input, x, y); + if (input) { + Keyboard_Process(input); + } +// HidPage.Lock(); + Map.Render(); +// HidPage.Unlock(); + } + } + + /* + ** Save map's position & selected objects, if we're recording the game. + */ + if (RecordGame || PlaybackGame) { + Do_Record_Playback(); + } + + /* + ** Sort the map's ground layer by y-coordinate value. This is done + ** outside the IsToRedraw check, for the purposes of game sync'ing + ** between machines; this way, all machines will sort the Map's + ** layer in the same way, and any processing done that's based on + ** the order of this layer will sync on different machines. + */ + Map.Layer[LAYER_GROUND].Sort(); + +// Heap_Dump_Check( "Before Logic.AI" ); + + /* + ** AI logic operations are performed here. + */ + Logic.AI(); + +// Heap_Dump_Check( "After Logic.AI" ); + + /* + ** Manage the inter-player message list. If Manage() returns true, it means + ** a message has expired & been removed, and the entire map must be updated. + */ + if (Messages.Manage()) { + HiddenPage.Clear(); + Map.Flag_To_Redraw(true); + } + + // + // Measure how long it took to process the AI + // + ProcessTicks += ProcessTimer.Time(); + ProcessFrames++; + +// Heap_Dump_Check( "Before Queue_AI" ); + + /* + ** Process all commands that are ready to be processed. + */ + Queue_AI(); + +// Heap_Dump_Check( "After Queue_AI" ); + + /* + ** Keep track of elapsed time in the game. + */ + Score.ElapsedTime += TIMER_SECOND / TICKS_PER_SECOND; + + Call_Back(); + +// Heap_Dump_Check( "After Call_Back" ); + + /* + ** Perform any win/lose code as indicated by the global control flags. + */ + if (EndCountDown) EndCountDown--; + + /* + ** Check for player wins or loses according to global event flag. + */ + + + + if (PlayerWins) { + + if (GameToPlay == GAME_INTERNET && !GameStatisticsPacketSent){ + Register_Game_End_Time(); + Send_Statistics_Packet(); + } + + WWMouse->Erase_Mouse(&HidPage, TRUE); + PlayerLoses = false; + PlayerWins = false; + PlayerRestarts = false; + Map.Help_Text(TXT_NONE); + Do_Win(); + } + if (PlayerLoses) { + + if (GameToPlay == GAME_INTERNET && !GameStatisticsPacketSent){ + Register_Game_End_Time(); + Send_Statistics_Packet(); + } + + WWMouse->Erase_Mouse(&HidPage, TRUE); + PlayerWins = false; + PlayerLoses = false; + PlayerRestarts = false; + Map.Help_Text(TXT_NONE); + Do_Lose(); + } + if (PlayerRestarts) { + WWMouse->Erase_Mouse(&HidPage, TRUE); + PlayerWins = false; + PlayerLoses = false; + PlayerRestarts = false; + Map.Help_Text(TXT_NONE); + Do_Restart(); + } + + /* + ** The frame logic has been completed. Increment the frame + ** counter. + */ + Frame++; + + /* + ** Very rarely, the human players will get a message from the computer. + */ + if (GameToPlay != GAME_NORMAL && MPlayerGhosts && IRandom(0,10000) == 1) { + Computer_Message(); + } + + /* + ** Is there a memory trasher altering the map?? + */ + if (Debug_Check_Map) { + if (!Map.Validate()) { +#ifdef GERMAN + if (CCMessageBox().Process ("Kartenfehler!","Halt","Weiter")==0) +#else +#ifdef FRENCH + if (CCMessageBox().Process ("Erreur de carte!","Stop","Continuer")==0) +#else + if (CCMessageBox().Process ("Map Error!","Stop","Continue")==0) +#endif +#endif + GameActive = false; + Map.Validate(); // give debugger a chance to catch it + } + } + + Sync_Delay(); +// InMainLoop = false; + return(!GameActive); +} + + +#ifdef SCENARIO_EDITOR +/*************************************************************************** + * Map_Edit_Loop -- a mini-main loop for map edit mode only * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/19/1994 BR : Created. * + *=========================================================================*/ +bool Map_Edit_Loop(void) +{ + /* + ** Redraw the map. + */ + Map.Render(); + + /* + ** Get user input (keys, mouse clicks). + */ + KeyNumType input; + + int x; + int y; + Map.Input(input, x, y); + + /* + ** Process keypress. + */ + if (input) { + Keyboard_Process(input); + } + + Call_Back(); // maintains Theme.AI() for music + Color_Cycle(); + + return(!GameActive); +} + + +/*************************************************************************** + * Go_Editor -- Enables/disables the map editor * + * * + * INPUT: * + * flag true = go into editor mode; false = go into game mode * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/19/1994 BR : Created. * + *=========================================================================*/ +void Go_Editor(bool flag) +{ + /* + ** Go into Scenario Editor mode + */ + if (flag) { + Debug_Map = true; + Debug_Unshroud = true; + + /* + ** Un-select any selected objects + */ + Unselect_All(); + + /* + ** Turn off the sidebar if it's on + */ + Map.Activate(0); + + /* + ** Reset the map's Button list for the new mode + */ + Map.Init_IO(); + + /* + ** Force a complete redraw of the screen + */ + HiddenPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + + } else { + + /* + ** Go into normal game mode + */ + Debug_Map = false; + Debug_Unshroud = false; + + /* + ** Un-select any selected objects + */ + Unselect_All(); + + /* + ** Reset the map's Button list for the new mode + */ + Map.Init_IO(); + + /* + ** Force a complete redraw of the screen + */ + HiddenPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + } +} + +#endif + +#if (0) +#define VQ_THREAD_BUFFER_SIZE 1024*1024 +#define VQ_THREAD_BUFFER_CHUNK VQ_THREAD_BUFFER_SIZE/4 +unsigned char *VQThreadBuffer = NULL; +volatile bool ThreadReading = false; +unsigned long VQThreadBlockHead; +unsigned long VQThreadBlockTail; +unsigned long VQBytesLeft; +unsigned long VQBytesRead; + + +void Init_VQ_Threading(CCFileClass *file) +{ + if (!VQThreadBuffer){ + VQThreadBuffer = new unsigned char [VQ_THREAD_BUFFER_SIZE]; + } + Force_VM_Page_In(VQThreadBuffer, VQ_THREAD_BUFFER_SIZE); + VQThreadBlockHead = 0; + VQThreadBlockTail = 0; + VQBytesRead = 0; + VQBytesLeft = file->Size(); +} + + +void Cleanup_VQ_Threading(void) +{ + while (ThreadReading){} + if (VQThreadBuffer){ + delete VQThreadBuffer; + VQThreadBuffer = NULL; + } +} + +unsigned long __stdcall Thread_Read(void *file) +{ + int bytes_to_read; + int left_to_read; + int read_this_time; + unsigned long head; + int sleep_time; + + CCFileClass *ccfile = (CCFileClass*)file; + + bytes_to_read = MIN (VQBytesLeft, VQ_THREAD_BUFFER_CHUNK); + + if (!bytes_to_read){ + ThreadReading = false; + return(0); + } + + left_to_read = bytes_to_read; + + while (left_to_read){ + read_this_time = MIN(8*1024, left_to_read); + //if (read_this_time & 3){ + ccfile->Read(VQThreadBuffer+VQThreadBlockHead, read_this_time); + //}else{ + // ccfile->Read(VQThreadBuffer+VQThreadBlockHead, read_this_time/4); + // ccfile->Read(VQThreadBuffer+VQThreadBlockHead+read_this_time/4, read_this_time/4); + // ccfile->Read(VQThreadBuffer+VQThreadBlockHead+(read_this_time/4)*2, read_this_time/4); + // ccfile->Read(VQThreadBuffer+VQThreadBlockHead+(read_this_time/4)*3, read_this_time/4); + //} + VQThreadBlockHead += read_this_time; + left_to_read -= read_this_time; + + head = VQThreadBlockHead; + if (head VQThreadBlockTail){ + bytes_to_read = MIN(bytes, VQThreadBlockHead-VQThreadBlockTail); + + }else{ + bytes_to_read = MIN(bytes, VQThreadBlockHead+VQ_THREAD_BUFFER_SIZE - VQThreadBlockTail); + } + + }while(ThreadReading && bytes_to_read VQ_THREAD_BUFFER_SIZE){ + + int first_chunk = VQ_THREAD_BUFFER_SIZE - VQThreadBlockTail; + int second_chunk = bytes_to_read - first_chunk; + + memcpy (buffer, VQThreadBuffer + VQThreadBlockTail, first_chunk); + memcpy ((unsigned char*)buffer + first_chunk, VQThreadBuffer, second_chunk); + }else{ + memcpy (buffer, VQThreadBuffer + VQThreadBlockTail, bytes_to_read); + } + + VQThreadBlockTail += bytes_to_read; + VQThreadBlockTail &= VQ_THREAD_BUFFER_SIZE - 1; + + unsigned long head = VQThreadBlockHead; + if (headVQAio; + + /* + ** Perform the action specified by the stream command. + */ + switch (action) { + + /* + ** VQACMD_READ means read NBytes from the stream and place it in the + ** memory pointed to by Buffer. + ** + ** Any error code returned will be remapped by VQA library into + ** VQAERR_READ. + */ + case VQACMD_READ: + error = VQ_Thread_Read (file, buffer, nbytes); + //error = (file->Read(buffer, (unsigned short)nbytes) != (unsigned short)nbytes); + if (error == nbytes) error = 0; + break; + + /* + ** VQACMD_WRITE is analogous to VQACMD_READ. + ** + ** Writing is not allowed to the VQA file, VQA library will remap the + ** error into VQAERR_WRITE. + */ + case VQACMD_WRITE: + error = 1; + break; + + /* + ** VQACMD_SEEK asks that you perform a seek relative to the current + ** position. NBytes is a signed number, indicating seek direction + ** (positive for forward, negative for backward). Buffer has no meaning + ** here. + ** + ** Any error code returned will be remapped by VQA library into + ** VQAERR_SEEK. + */ + case VQACMD_SEEK: + //error = (file->Seek(nbytes, SEEK_CUR) == -1); + VQ_Thread_Seek(nbytes); + error = 0; + break; + + /* + ** VQACMD_OPEN asks that you open your stream for access. + */ + case VQACMD_OPEN: + file = new CCFileClass((char *)buffer); + + if (file != NULL && file->Is_Available()) { + error = file->Open((char *)buffer, READ); + + if (error != -1) { + vqa->VQAio = (unsigned long)file; + error = 0; + //file->Set_Buffer_Size(8*1024); + } else { + delete file; + file = 0; + error = 1; + } + } else { + error = 1; + } + + if (error != -1){ + Init_VQ_Threading(file); + Read_VQ_Thread_Block(file); + CountDownTimerClass timer; + timer.Set(60); + while (ThreadReading || timer.Time()){} + } + break; + + case VQACMD_CLOSE: + Cleanup_VQ_Threading(); + file->Close(); + delete file; + file = 0; + vqa->VQAio = 0; + error = 0; + break; + + /* + ** VQACMD_INIT means to prepare your stream for reading. This is used for + ** certain streams that can't be read immediately upon opening, and need + ** further preparation. This operation is allowed to fail; the error code + ** will be returned directly to the client. + */ + case VQACMD_INIT: + + /* + ** IFFCMD_CLEANUP means to terminate the transaction with the associated + ** stream. This is used for streams that can't simply be closed. This + ** operation is not allowed to fail; any error returned will be ignored. + */ + case VQACMD_CLEANUP: + error = 0; + break; + } + + return(error); +} +#endif //(0) +//#if (0) +/*********************************************************************************************** + * MixFileHandler -- Handles VQ file access. * + * * + * This routine is called from the VQ player when it needs to access the source file. By * + * using this routine it is possible to virtualize the file system. * + * * + * INPUT: vqa -- Pointer to the VQA handle for this animation. * + * * + * action-- The requested action to perform. * + * * + * buffer-- Optional buffer pointer as needed by the type of action. * + * * + * nbytes-- The number of bytes (if needed) for this operation. * + * * + * OUTPUT: Returns a value consistent with the action requested. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +long MixFileHandler(VQAHandle *vqa, long action, void *buffer, long nbytes) +{ + CCFileClass *file; + long error; + + file = (CCFileClass *)vqa->VQAio; + + /* + ** Perform the action specified by the stream command. + */ + switch (action) { + + /* + ** VQACMD_READ means read NBytes from the stream and place it in the + ** memory pointed to by Buffer. + ** + ** Any error code returned will be remapped by VQA library into + ** VQAERR_READ. + */ + case VQACMD_READ: + error = (file->Read(buffer, (unsigned short)nbytes) != (unsigned short)nbytes); + break; + + /* + ** VQACMD_WRITE is analogous to VQACMD_READ. + ** + ** Writing is not allowed to the VQA file, VQA library will remap the + ** error into VQAERR_WRITE. + */ + case VQACMD_WRITE: + error = 1; + break; + + /* + ** VQACMD_SEEK asks that you perform a seek relative to the current + ** position. NBytes is a signed number, indicating seek direction + ** (positive for forward, negative for backward). Buffer has no meaning + ** here. + ** + ** Any error code returned will be remapped by VQA library into + ** VQAERR_SEEK. + */ + case VQACMD_SEEK: + error = (file->Seek(nbytes, SEEK_CUR) == -1); + break; + + /* + ** VQACMD_OPEN asks that you open your stream for access. + */ + case VQACMD_OPEN: + file = new CCFileClass((char *)buffer); + + if (file != NULL && file->Is_Available()) { + error = file->Open((char *)buffer, READ); + + if (error != -1) { + vqa->VQAio = (unsigned long)file; + error = 0; + file->Set_Buffer_Size(8*1024); + } else { + delete file; + file = 0; + error = 1; + } + } else { + error = 1; + } + break; + + case VQACMD_CLOSE: + file->Close(); + delete file; + file = 0; + vqa->VQAio = 0; + error = 0; + break; + + /* + ** VQACMD_INIT means to prepare your stream for reading. This is used for + ** certain streams that can't be read immediately upon opening, and need + ** further preparation. This operation is allowed to fail; the error code + ** will be returned directly to the client. + */ + case VQACMD_INIT: + + /* + ** IFFCMD_CLEANUP means to terminate the transaction with the associated + ** stream. This is used for streams that can't simply be closed. This + ** operation is not allowed to fail; any error returned will be ignored. + */ + case VQACMD_CLEANUP: + error = 0; + break; + } + + return(error); +} + +//#endif //(0) + + +void Rebuild_Interpolated_Palette(unsigned char *interpal) +{ + for (int y=0 ; y<255 ; y++){ + for (int x=y+1 ; x<256 ; x++){ + *(interpal + (y*256+x)) = *(interpal + (x*256+y)); + } + } +} + + +unsigned char *InterpolatedPalettes[100]; +BOOL PalettesRead; +unsigned PaletteCounter; + + +int Load_Interpolated_Palettes(char const *filename, BOOL add) +{ + int num_palettes=0; + int i; + int start_palette; + + PalettesRead = FALSE; + CCFileClass file(filename); + +// RawFileClass *palette_file; + + if (!add){ + for (i=0 ; iUnselect(); + } +} + + +/*********************************************************************************************** + * Fading_Table_Name -- Builds a theater specific fading table name. * + * * + * This routine builds a standard fading table name. This name is dependant on the theater * + * being played, since each theater has its own palette. * + * * + * INPUT: base -- The base name of this fading table. The base name can be no longer than * + * seven characters. * + * * + * theater -- The theater that this fading table is specific to. * + * * + * OUTPUT: Returns with a pointer to the constructed fading table filename. This pointer is * + * valid until this function is called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +char const * Fading_Table_Name(char const * base, TheaterType theater) +{ + static char _buffer[_MAX_FNAME+_MAX_EXT]; + char root[_MAX_FNAME]; + + sprintf(root, "%1.1s%s", Theaters[theater].Root, base); + _makepath(_buffer, NULL, NULL, root, ".MRF"); + return(_buffer); +} + + +/*********************************************************************************************** + * Get_Radar_Icon -- Builds and alloc a radar icon from a shape file * + * * + * INPUT: void const * shapefile - pointer to a key framed shapefile * + * int shapenum - shape to extract from shapefile * + * * + * OUTPUT: void const * - 3/3 icon set of shape from file * + * * + * HISTORY: * + * 04/12/1995 PWG : Created. * + * 05/10/1995 JLB : Handles a null shapefile pointer. * + *=============================================================================================*/ +void const * Get_Radar_Icon(void const * shapefile, int shapenum, int frames, int zoomfactor) +{ + static int _offx[]={ 0, 0, -1, 1, 0, -1, 1, -1, 1}; + static int _offy[]={ 0, 0, -1, 1, 0, -1, 1, -1, 1}; + int lp,framelp; + char pixel; + + char *retval = NULL; + char *buffer = NULL; + void *ptr; + + + /* + ** If there is no shape file, then there can be no radar icon imagery. + */ + if (!shapefile) return(NULL); + + /* + ** Get the pixel width and height of the frame we built. This will + ** be used to extract icons and build pixels. + */ + int pixel_width = Get_Build_Frame_Width( shapefile ); + int pixel_height = Get_Build_Frame_Height( shapefile ); + + /* + ** Find the width and height in icons, adjust these by half an + ** icon because the artists may be sloppy and miss the edge of an + ** icon one way or the other. + */ + int icon_width = (pixel_width + 12) / 24; + int icon_height = (pixel_height + 12) / 24; + + /* + ** If we have been told to build as many frames as possible, then + ** find out how many frames there are to build. + */ + if (frames == -1) frames = Get_Build_Frame_Count( shapefile ); + + /* + ** Allocate a position to store our icons. If the alloc fails then + ** we dont add these icons to the set. + **/ + buffer = new char[(icon_width * icon_height * 9 * frames)+2]; + if (!buffer) return(NULL); + + /* + ** Save off the return value so that we can return it to the calling + ** function. + */ + retval = (char *)buffer; + *buffer++ = (char)icon_width; + *buffer++ = (char)icon_height; + int val = 24/zoomfactor; + + for (framelp = 0; framelp < frames; framelp ++) { + /* + ** Build the current frame. If the frame can not be built then we + ** just need to skip past this set of icons and try to build the + ** next frame. + */ + if ((ptr = (void *)(Build_Frame(shapefile, shapenum + framelp, SysMemPage.Get_Buffer()))) != NULL) { + ptr = Get_Shape_Header_Data(ptr); + /* + ** Loop through the icon width and the icon height building icons + ** into the buffer pointer. When the getx or gety falls outside of + ** the width and height of the shape, just insert transparent pixels. + */ + for (int icony = 0; icony < icon_height; icony ++) { + for (int iconx = 0; iconx < icon_width; iconx ++) { + for (int y = 0; y < zoomfactor; y++) { + for (int x = 0; x < zoomfactor; x++) { + int getx = (iconx * 24) + (x * val) + (zoomfactor / 2); + int gety = (icony * 24) + (y * val) + (zoomfactor / 2); + if ((getx < pixel_width) && (gety < pixel_height)) { + for (lp = 0; lp < 9; lp ++) { + pixel = *((char *)(Add_Long_To_Pointer(ptr, ((gety - _offy[lp]) * pixel_width) + getx-_offx[lp]))); + if (pixel == LTGREEN) pixel = 0; + if (pixel) { + break; + } + } + *buffer++ = pixel; + } else { + *buffer++ = 0; + } + } + } + } + } + } else { + buffer += icon_width * icon_height * 9; + } + } + return(retval); +} + + + + +void CC_Texture_Fill (void const *shapefile, int shapenum, int xpos, int ypos, int width, int height) +{ + unsigned char *shape_pointer; + //unsigned char *shape_save; + unsigned long shape_size; + //int x,y; + + if (shapefile && shapenum != -1) { + + /* + ** Build frame returns a pointer now instead of the shapes length + */ + shape_size=Build_Frame(shapefile , shapenum , ShapeBuffer); + if (Get_Last_Frame_Length() > _ShapeBufferSize) { + Mono_Printf("Attempt to use shape buffer for size %d buffer is only size %d", shape_size, _ShapeBufferSize); + Get_Key(); + } + + if (shape_size) { + shape_pointer = (unsigned char *)Get_Shape_Header_Data ((void*)shape_size); + int source_width = Get_Build_Frame_Width (shapefile); + int source_height = Get_Build_Frame_Height (shapefile); + + + LogicPage->Texture_Fill_Rect (xpos, ypos, width, height, shape_pointer, source_width, source_height); +#if (0) + if (LogicPage->Lock()){ + + for (y = ypos ; y < ypos + MIN(source_height, height) ; y++ ){ + + shape_save = shape_pointer; + + for (x = xpos ; x < xpos + MIN(source_width, width) ; x++ ){ + LogicPage->Put_Pixel (x, y, *shape_pointer++); + } + + shape_pointer = shape_save + source_width; + } + + LogicPage->Unlock(); + } +#endif + } + } +} + + + + + +/*********************************************************************************************** + * CC_Draw_Shape -- Custom draw shape handler. * + * * + * All draw shape calls will route through this function. It handles all draws for * + * C&C. Such draws always occur to the logical page and assume certain things about * + * the parameters passed. * + * * + * INPUT: shapefile -- Pointer to the shape data file. This data file contains all the * + * embedded shapes. * + * * + * shapenum -- The shape number within the shapefile that will be drawn. * + * * + * x,y -- The pixel coordinates to draw the shape. * + * * + * window -- The clipping window to use. * + * * + * flags -- The custom draw shape flags. This controls how the parameters * + * are used (if any). * + * * + * fadingdata -- If SHAPE_FADING is desired, then this points to the fading * + * data table. * + * * + * ghostdata -- If SHAPE_GHOST is desired, then this points to the ghost remap * + * table. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +#pragma off(unreferenced) +void CC_Draw_Shape(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata) +{ +#if(TRUE) + int predoffset; + char *shape_pointer; + unsigned long shape_size; + + if (shapefile && shapenum != -1) { + + /* + ** Build frame returns a pointer now instead of the shapes length + */ + shape_size=Build_Frame(shapefile , shapenum , ShapeBuffer); + if (Get_Last_Frame_Length() > _ShapeBufferSize) { + Mono_Printf("Attempt to use shape buffer for size %d buffer is only size %d", shape_size, _ShapeBufferSize); + Get_Key(); + } + + if (shape_size) { + GraphicViewPortClass draw_window(LogicPage->Get_Graphic_Buffer(), + WindowList[window][WINDOWX] << 3 + LogicPage->Get_XPos(), + WindowList[window][WINDOWY] + LogicPage->Get_YPos(), + WindowList[window][WINDOWWIDTH] << 3, + WindowList[window][WINDOWHEIGHT]); + + shape_pointer = (char *)shape_size; + + /* + ** Special shadow drawing code (used for aircraft and bullets). + */ + if ((flags & (SHAPE_FADING|SHAPE_PREDATOR)) == (SHAPE_FADING|SHAPE_PREDATOR)) { + flags = flags & ~(SHAPE_FADING|SHAPE_PREDATOR); + flags = flags | SHAPE_GHOST; + ghostdata = Map.SpecialGhost; + } + + predoffset = Frame; + + if (x > ( WindowList[window][WINDOWWIDTH] << 2)) { + predoffset = -predoffset; + } + + if (draw_window.Lock()){ + if ((flags & (SHAPE_GHOST|SHAPE_FADING)) == (SHAPE_GHOST|SHAPE_FADING)) { + Buffer_Frame_To_Page(x, y, Get_Build_Frame_Width(shapefile), Get_Build_Frame_Height(shapefile), + shape_pointer, draw_window, flags | SHAPE_TRANS, ghostdata, fadingdata, 1, predoffset); + } else { + if (flags & SHAPE_FADING) { + Buffer_Frame_To_Page(x, y, Get_Build_Frame_Width(shapefile), Get_Build_Frame_Height(shapefile), + shape_pointer, draw_window, flags | SHAPE_TRANS, fadingdata, 1, predoffset); + } else { + if (flags & SHAPE_PREDATOR) { + Buffer_Frame_To_Page(x, y, Get_Build_Frame_Width(shapefile), Get_Build_Frame_Height(shapefile), + shape_pointer, draw_window, flags | SHAPE_TRANS, predoffset); + } else { + Buffer_Frame_To_Page(x, y, Get_Build_Frame_Width(shapefile), Get_Build_Frame_Height(shapefile), + shape_pointer, draw_window, flags | SHAPE_TRANS, ghostdata, predoffset); + } + } + } + } + draw_window.Unlock(); +// } else { +// Mono_Printf( "Overrun ShapeBuffer!!!!!!!!!\n" ); + } + } +#endif +} + + + +/*********************************************************************************************** + * Fetch_Techno_Type -- Convert type and ID into TechnoTypeClass pointer. * + * * + * This routine will convert the supplied RTTI type number and the ID value into a valid * + * TechnoTypeClass pointer. If there is an error in conversion, then NULL is returned. * + * * + * INPUT: type -- RTTI type of the techno class object. * + * * + * id -- Integer representation of the techno sub type number. * + * * + * OUTPUT: Returns with a pointer to the techno type class object specified or NULL if the * + * conversion could not occur. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +TechnoTypeClass const * Fetch_Techno_Type(RTTIType type, int id) +{ + switch (type) { + case RTTI_UNITTYPE: + case RTTI_UNIT: + return(&UnitTypeClass::As_Reference((UnitType)id)); + + case RTTI_BUILDINGTYPE: + case RTTI_BUILDING: + return(&BuildingTypeClass::As_Reference((StructType)id)); + + case RTTI_INFANTRYTYPE: + case RTTI_INFANTRY: + return(&InfantryTypeClass::As_Reference((InfantryType)id)); + + case RTTI_AIRCRAFTTYPE: + case RTTI_AIRCRAFT: + return(&AircraftTypeClass::As_Reference((AircraftType)id)); + } + return(NULL); +} + + +/*************************************************************************** + * Trap_Object -- gets a ptr to object of given type & coord * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/02/1995 BRR : Created. * + *=========================================================================*/ +void Trap_Object(void) +{ + int i; + + TrapObject.Ptr.All = NULL; + + switch (TrapObjType) { + case RTTI_AIRCRAFT: + for (i = 0; i < Aircraft.Count(); i++) { + if (Aircraft.Ptr(i)->Coord == TrapCoord || Aircraft.Ptr(i)==TrapThis) { + TrapObject.Ptr.Aircraft = Aircraft.Ptr(i); + break; + } + } + break; + + case RTTI_ANIM: + for (i = 0; i < Anims.Count(); i++) { + if (Anims.Ptr(i)->Coord == TrapCoord || Anims.Ptr(i)==TrapThis) { + TrapObject.Ptr.Anim = Anims.Ptr(i); + break; + } + } + break; + + case RTTI_BUILDING: + for (i = 0; i < Buildings.Count(); i++) { + if (Buildings.Ptr(i)->Coord == TrapCoord || Buildings.Ptr(i)==TrapThis) { + TrapObject.Ptr.Building = Buildings.Ptr(i); + break; + } + } + break; + + case RTTI_BULLET: + for (i = 0; i < Bullets.Count(); i++) { + if (Bullets.Ptr(i)->Coord == TrapCoord || Bullets.Ptr(i)==TrapThis) { + TrapObject.Ptr.Bullet = Bullets.Ptr(i); + break; + } + } + break; + + case RTTI_INFANTRY: + for (i = 0; i < Infantry.Count(); i++) { + if (Infantry.Ptr(i)->Coord == TrapCoord || Infantry.Ptr(i)==TrapThis) { + TrapObject.Ptr.Infantry = Infantry.Ptr(i); + break; + } + } + break; + + case RTTI_UNIT: + for (i = 0; i < Units.Count(); i++) { + if (Units.Ptr(i)->Coord == TrapCoord || Units.Ptr(i)==TrapThis) { + TrapObject.Ptr.Unit = Units.Ptr(i); + break; + } + } + break; + + /* + ** Last-ditch find-the-object-right-now-darnit loop + */ + case RTTI_NONE: + for (i = 0; i < Aircraft.Count(); i++) { + if (Aircraft.Raw_Ptr(i)->Coord == TrapCoord || Aircraft.Raw_Ptr(i)==TrapThis) { + TrapObject.Ptr.Aircraft = Aircraft.Raw_Ptr(i); + TrapObjType = RTTI_AIRCRAFT; + return; + } + } + for (i = 0; i < Anims.Count(); i++) { + if (Anims.Raw_Ptr(i)->Coord == TrapCoord || Anims.Raw_Ptr(i)==TrapThis) { + TrapObject.Ptr.Anim = Anims.Raw_Ptr(i); + TrapObjType = RTTI_ANIM; + return; + } + } + for (i = 0; i < Buildings.Count(); i++) { + if (Buildings.Raw_Ptr(i)->Coord == TrapCoord || Buildings.Raw_Ptr(i)==TrapThis) { + TrapObject.Ptr.Building = Buildings.Raw_Ptr(i); + TrapObjType = RTTI_BUILDING; + return; + } + } + for (i = 0; i < Bullets.Count(); i++) { + if (Bullets.Raw_Ptr(i)->Coord == TrapCoord || Bullets.Raw_Ptr(i)==TrapThis) { + TrapObject.Ptr.Bullet = Bullets.Raw_Ptr(i); + TrapObjType = RTTI_BULLET; + return; + } + } + for (i = 0; i < Infantry.Count(); i++) { + if (Infantry.Raw_Ptr(i)->Coord == TrapCoord || Infantry.Raw_Ptr(i)==TrapThis) { + TrapObject.Ptr.Infantry = Infantry.Raw_Ptr(i); + TrapObjType = RTTI_INFANTRY; + return; + } + } + for (i = 0; i < Units.Count(); i++) { + if (Units.Raw_Ptr(i)->Coord == TrapCoord || Units.Raw_Ptr(i)==TrapThis) { + TrapObject.Ptr.Unit = Units.Raw_Ptr(i); + TrapObjType = RTTI_UNIT; + return; + } + } + + default: + break; + } +} + + +/*********************************************************************************************** + * VQ_Call_Back -- Maintenance callback used for VQ movies. * + * * + * This routine is called every frame of the VQ movie as it is being played. If this * + * routine returns non-zero, then the movie will stop. * + * * + * INPUT: buffer -- Pointer to the image buffer for the current frame. * + * * + * frame -- The frame number about to be displayed. * + * * + * OUTPUT: Should the movie be stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 JLB : Created. * + *=============================================================================================*/ +void VQA_PauseAudio(void); +void Check_VQ_Palette_Set(void); + +long VQ_Call_Back(unsigned char *, long ) +{ + int key = 0; + if (Keyboard::Check()){ + key = Keyboard::Get(); + Keyboard::Clear(); + } + + Check_VQ_Palette_Set(); + + Interpolate_2X_Scale(&SysMemPage,&SeenBuff,NULL); + //Call_Back(); + if ((BreakoutAllowed || Debug_Flag) && key == KN_ESC) { + Keyboard::Clear(); + Brokeout = true; + return(true); + } + + if (!GameInFocus){ + VQA_PauseAudio(); + while (!GameInFocus){ + Keyboard::Check(); + Check_For_Focus_Loss(); + } + } + + return(false); +} + + +/*********************************************************************************************** + * Handle_Team -- Processes team selection command. * + * * + * This routine will handle creation and selection of pseudo teams that the player can * + * create or control. A team in this sense is an arbitrary grouping of units such that * + * rapid selection control is allowed. * + * * + * INPUT: team -- The logical team number to process. * + * * + * action-- The action to perform on this team: * + * 0 - Toggle the select state for all members of this team. * + * 1 - Select the members of this team. * + * 2 - Make all selected objects members of this team. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1995 JLB : Created. * + *=============================================================================================*/ +void Handle_Team(int team, int action) +{ + int index; + + AllowVoice = true; + switch (action) { + + /* + ** Toggle the team selection. If the team is selected, then merely unselect it. If the + ** team is not selected, then unselect all others before selecting this team. + */ + case 3: + case 0: + + /* + ** If a non team member is currently selected, then deselect all objects + ** before selecting this team. + */ + if (CurrentObject.Count()) { + switch (CurrentObject[0]->What_Am_I()) { + case RTTI_UNIT: + case RTTI_INFANTRY: + case RTTI_AIRCRAFT: + if (((FootClass *)CurrentObject[0])->Group != team) { + Unselect_All(); + } + break; + } + } + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House == PlayerPtr) { + if (!obj->IsSelected) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House == PlayerPtr) { + if (!obj->IsSelected) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * obj = Aircraft.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House == PlayerPtr) { + if (!obj->IsSelected) { + obj->Select(); + AllowVoice = false; + } + } + } + + /* + ** Center the map around the team if the ALT key was pressed too. + */ + if (action == 3) { + Map.Center_Map(); + Map.Flag_To_Redraw(true); + } + break; + + /* + ** Additive selection of team. + */ + case 1: + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House == PlayerPtr) { + if (!obj->IsSelected) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House == PlayerPtr) { + if (!obj->IsSelected) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * obj = Aircraft.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House == PlayerPtr) { + if (!obj->IsSelected) { + obj->Select(); + AllowVoice = false; + } + } + } + break; + + /* + ** Create the team. + */ + case 2: + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr) { + if (obj->Group == team) obj->Group = -1; + if (obj->IsSelected) obj->Group = team; + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr) { + if (obj->Group == team) obj->Group = -1; + if (obj->IsSelected) obj->Group = team; + } + } + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * obj = Aircraft.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr) { + if (obj->Group == team) obj->Group = -1; + if (obj->IsSelected) obj->Group = team; + } + } + break; + } + AllowVoice = true; +} + + +/*********************************************************************************************** + * Handle_View -- Either records or restores the tactical view. * + * * + * This routine is used to record or restore the current map tactical view. * + * * + * INPUT: view -- The view number to work with. * + * * + * action-- The action to perform with this view number. * + * 0 = Restore the view to this previously remembered location. * + * 1 = Record the current view location. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +void Handle_View(int view, int action) +{ + if ((unsigned)view < sizeof(Views)/sizeof(Views[0])) { + if (action == 0) { + Map.Set_Tactical_Position(Cell_Coord(Views[view])&0xFF00FF00L); + Map.Flag_To_Redraw(true); + } else { + Views[view] = Coord_Cell(Map.TacticalCoord); + } + } +} + +#ifdef CHEAT_KEYS +void Heap_Dump_Check( char *string ) +{ +#if 0 + struct _heapinfo h_info; + int heap_status; +#endif + + + if ( !Debug_Trap_Check_Heap ) { // check the heap? + return; + } + +// Debug_Heap_Dump = true; + + Smart_Printf( "%s\n", string ); + + Dump_Heap_Pointers(); + +#if 0 + heap_status = _heapset( 0xee ); + +#if (1) + if ( heap_status == _HEAPOK || + heap_status == _HEAPEMPTY ) { + Debug_Heap_Dump = false; + return; + } + + h_info._pentry = NULL; + + for(;;) { + heap_status = _heapwalk( &h_info ); + + if ( heap_status != _HEAPOK ) + break; + } + + if (heap_status != _HEAPEND && + heap_status != _HEAPEMPTY) { +#endif + h_info._pentry = NULL; + + for(;;) { + heap_status = _heapwalk( &h_info ); + + if ( heap_status != _HEAPOK ) + break; + + Smart_Printf( " %s block at %Fp of size %4.4X\n", + (h_info._useflag == _USEDENTRY ? "USED" : "FREE"), + h_info._pentry, h_info._size ); + } + + Smart_Printf( " %d block at %Fp of size %4.4X\n", + h_info._useflag, h_info._pentry, h_info._size ); + + switch ( heap_status ) { +// case _HEAPEND: +// Smart_Printf( "OK - end of heap\n" ); +// break; + +// case _HEAPEMPTY: +// Smart_Printf( "OK - heap is empty\n" ); +// break; + + case _HEAPBADBEGIN: + Smart_Printf( "ERROR - heap is damaged\n" ); + break; + + case _HEAPBADPTR: + Smart_Printf( "ERROR - bad pointer to heap\n" ); + break; + + case _HEAPBADNODE: + Smart_Printf( "ERROR - bad node in heap\n" ); + break; + } +#if (1) + } +#endif +#endif + +// Debug_Heap_Dump = false; +} + + +void Dump_Heap_Pointers( void ) +{ + char *ptr, *lptr, *nptr, *cptr, *dptr, *wlptr, *nlptr, *aptr, *clptr; + int numallocs, numfrees, sizefree; + static char _freeorused[2][5] = { "FREE", "USED" }; + + + ptr = (char *)__nheapbeg; + + while ( ptr ) { + + if ( Debug_Heap_Dump ) { + Smart_Printf( "%p pre header\n", (ptr - 8) ); + Hex_Dump_Data( (ptr - 8), 0x08); + + Smart_Printf( "%p header\n", ptr ); + Hex_Dump_Data( ptr, 0x30); + } + + dptr = (char *)*(int *)(ptr + 0x0c); + + sizefree = *(int *)(ptr + 0x14); + numallocs = *(int *)(ptr + 0x18); + numfrees = *(int *)(ptr + 0x1c); + + cptr = (char *)*(int *)(ptr + 0x24); + lptr = (char *)*(int *)(ptr + 0x28); + + if ( ((int)cptr & 0xff000000) || + ((int)dptr & 0xff000000) || + ((int)lptr & 0xff000000) ) { + Error_In_Heap_Pointers( "local free heap ptrs too large" ); + } + + if ( Debug_Heap_Dump ) { + if ( lptr != dptr || + lptr != cptr || + cptr != dptr ) { + Smart_Printf( "The pointers are different!!\n" ); + } + } + + nptr = (char *)*(int *)(ptr + 8); // next block + + if ( ((int)nptr & 0xFF000000) ) { + Error_In_Heap_Pointers( "next block ptr too large" ); + } + + if ( lptr != (ptr + 0x20) ) { + + if ( !(*((int *)(ptr + 0x20))) ) { // allocated + aptr = (ptr + 0x2c); + + while ( aptr < lptr ) { + + if ( (*(int *)(aptr)) == -1) { +// Smart_Printf( "end alloc chain %p.\n", aptr ); +// Hex_Dump_Data( aptr, 0x10); + break; + } + + if ( Debug_Heap_Dump ) { + Smart_Printf( "%p chain %s, size %X.\n", aptr, + _freeorused[ ((*aptr) & 1) ], + ((*(int *)(aptr)) & 0xfffffffe) ); + Hex_Dump_Data( aptr, 0x10); + } + + if ( ((*(int *)(aptr)) & 0xff000000) ) { + Error_In_Heap_Pointers( "alloc block size way too large" ); + } + + aptr += ((*(int *)(aptr)) & 0xfffffffe); + + if ( ((int)aptr & 0xff000000) ) { + Error_In_Heap_Pointers( "next alloc block ptr way too large" ); + } + + numallocs--; + + if ( aptr > lptr ) { + Error_In_Heap_Pointers( "next alloc block ptr too large" ); + } + } + } else { + if ( sizefree != -1 ) { + sizefree -= ((*(int *)(ptr + 0x20)) & 0xfffffffe); + } + numfrees--; + } + + wlptr = lptr; + + while ( wlptr != (ptr + 0x20) ) { + + if ( Debug_Heap_Dump ) { + Smart_Printf( "%p link %s, size %X.\n", wlptr, + _freeorused[ ((*wlptr) & 1) ], + ((*(int *)(wlptr)) & 0xfffffffe) ); + Hex_Dump_Data( wlptr, 0x10); + } + + nlptr = (char *)*(int *)(wlptr + 8); + + if ( !(*((int *)(wlptr))) ) { // allocated + aptr = (wlptr + 0x0c); + } else { + if ( sizefree != -1 ) { + sizefree -= ((*(int *)(wlptr)) & 0xfffffffe); + } + numfrees--; + + aptr = (wlptr + ((*(int *)(wlptr)) & 0xfffffffe)); + } + + if (nlptr == (ptr + 0x20) ) { + clptr = nptr; + } else { + clptr = nlptr; + } + + while ( aptr < clptr ) { + + if ( (*(int *)(aptr)) == -1) { +// Smart_Printf( "end alloc chain %p.\n", aptr ); +// Hex_Dump_Data( aptr, 0x10); + break; + } + + if ( Debug_Heap_Dump ) { + Smart_Printf( "%p chain %s, size %X.\n", aptr, + _freeorused[ ((*aptr) & 1) ], + ((*(int *)(aptr)) & 0xfffffffe) ); + Hex_Dump_Data( aptr, 0x10); + } + + if ( ((*(int *)(aptr)) & 0xff000000) ) { + Error_In_Heap_Pointers( "alloc block size way too large" ); + } + + aptr += ((*(int *)(aptr)) & 0xfffffffe); + + if ( ((int)aptr & 0xff000000) ) { + Error_In_Heap_Pointers( "next alloc block ptr way too large" ); + } + + numallocs--; + + if ( aptr > clptr ) { + Error_In_Heap_Pointers( "next alloc block ptr too large" ); + } + } + + wlptr = nlptr; + } + } else { +// Smart_Printf( "only link %s, size %X.\n", +// _freeorused[ ((*lptr) & 1) ], +// ((*(int *)(lptr)) & 0xfffffffe) ); + + if ( !(*((int *)(lptr))) ) { // allocated + aptr = (ptr + 0x2c); + + while ( aptr < nptr ) { + + if ( (*(int *)(aptr)) == -1) { +// Smart_Printf( "end alloc chain %p.\n", aptr ); +// Hex_Dump_Data( aptr, 0x10); + break; + } + + if ( Debug_Heap_Dump ) { + Smart_Printf( "%p chain %s, size %X.\n", aptr, + _freeorused[ ((*aptr) & 1) ], + ((*(int *)(aptr)) & 0xfffffffe) ); + Hex_Dump_Data( aptr, 0x10); + } + + if ( ((*(int *)(aptr)) & 0xff000000) ) { + Error_In_Heap_Pointers( "alloc block size way too large" ); + } + + aptr += ((*(int *)(aptr)) & 0xfffffffe); + + if ( ((int)aptr & 0xff000000) ) { + Error_In_Heap_Pointers( "next alloc block ptr way too large" ); + } + + numallocs--; + + if ( aptr > nptr ) { + Error_In_Heap_Pointers( "next alloc block ptr too large" ); + } + } + } else { + if ( sizefree != -1 ) { + sizefree -= ((*(int *)(ptr + 0x20)) & 0xfffffffe); + } + numfrees--; + } + } + + if ( sizefree != 0 && sizefree != -1 ) { + Smart_Printf( "sizefree left over %X.\n", sizefree ); + } + + if ( numallocs != 0 ) { + Smart_Printf( "numallocs unaccounted for %d.\n", numallocs ); + } + + if ( numfrees != 0 ) { + Smart_Printf( "numfrees unaccounted for %d.\n", numfrees ); + } + + ptr = nptr; + } + +} + + +void Error_In_Heap_Pointers( char *string ) +{ + Smart_Printf( "Error in Heap for %s\n", string ); +} +#endif + + +#ifndef ROR_NOT_READY +#define ROR_NOT_READY 21 +#endif + +/*********************************************************************************************** + * Get_CD_Index -- returns the volume type of the CD in the given drive * + * * + * * + * * + * INPUT: drive number * + * timeout * + * * + * OUTPUT: 0 = gdi * + * 1 = nod * + * 2 = covert * + * -1 = non C&C * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/21/96 5:27PM ST : Created * + *=============================================================================================*/ +int Get_CD_Index (int cd_drive, int timeout) +{ + char volume_name[128]; + unsigned filename_length; + unsigned misc_dword; + unsigned error_code; + int count = 0; + + CountDownTimerClass timer; + + static char * _volid[] = { + "GDI95", + "NOD95", + "COVERT" + }; + static int num_volumes = 3; + + char buffer[128]; + + timer.Set(timeout); + + + /* + ** Get the volume label. If we get a 'not ready' error then retry for the timeout + ** period. + */ + do{ + sprintf(buffer, "%c:\\", 'A' + cd_drive); + + if (GetVolumeInformation ((char const *)buffer, + &volume_name[0] , + (unsigned long)128 , + (unsigned long *)NULL , + (unsigned long *)&filename_length, + (unsigned long *)&misc_dword , + (char *)NULL , + (unsigned long)0)) { + + /* + ** Try opening 'movies.mix' to verify that the CD is really there and is what + ** it says it is. + */ + sprintf(buffer, "%c:\\movies.mix", 'A' + cd_drive); + + HANDLE handle = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (handle != INVALID_HANDLE_VALUE) { + CloseHandle(handle); + + /* + ** Match the volume label to the list of known C&C volume labels. + */ + for (int i=0 ; i. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1995 JLB : Created. * + * 05/22/1996 ST : Handles multiple CD drives / CD changers * + *=============================================================================================*/ +bool Force_CD_Available(int cd) +{ +#ifndef DEMO + static int _last = -1; + int open_failed; + int file; +#endif + static char _palette[768]; + static char _hold[256]; + static void *font; + static char * _volid[] = { + "GDI", + "NOD", + "COVERT" + }; + + int drive; + + char volume_name[100]; + unsigned filename_length; + unsigned misc_dword; + int new_cd_drive = 0; + int cd_index; + char buffer[128]; + int cd_drive; + int current_drive; + int drive_search_timeout; + bool old_in_main_loop; + + ThemeType theme_playing = THEME_NONE; + + /* + ** If the required CD is set to -2 then it means that the file is present + ** on the local hard drive and we shouldn't have to worry about it. + */ + if (cd == -2) return(true); + + + /* + ** Find out if the CD in the current drive is the one we are looking for + */ + current_drive = CCFileClass::Get_CD_Drive(); + cd_index = Get_CD_Index(current_drive, 1*60); + if (cd_index >= 0){ + if (cd == cd_index || cd == -1){ + /* + ** The required CD is still in the CD drive we used last time + */ + new_cd_drive = current_drive; + } + } + + + /* + ** Flag that we will have to restart the theme + */ + theme_playing = Theme.What_Is_Playing(); + Theme.Stop(); + + + if (!new_cd_drive){ + /* + ** Check the last CD drive we used if its different from the current one + */ + int last_drive = CCFileClass::Get_Last_CD_Drive(); + /* + ** Make sure the last drive is valid and it isnt the current drive + */ + if (last_drive && last_drive != CCFileClass::Get_CD_Drive()){ + /* + ** Find out if there is a C&C cd in the last drive and if so is it the one we are looking for + ** Give it a nice big timeout so the CD changer has time to swap the discs + */ + cd_index = Get_CD_Index(last_drive, 10*60); + if (cd_index >= 0){ + if (cd == cd_index || cd == -1){ + /* + ** The required CD is in the CD drive we used last time + */ + new_cd_drive = last_drive; + } + } + } + } + + /* + ** Lordy. No sign of that blimming CD anywhere. Search all the CD drives + ** then if we still cant find it prompt the user to insert it. + */ + if (!new_cd_drive){ + + /* + ** Small timeout for the first pass through the drives + */ + drive_search_timeout = 2*60; + + for (;;) { + /* + ** Search all present CD drives for the required disc. + */ + for (int i=0 ; i=0){ + /* + ** We found a C&C cd - lets see if it was the one we were looking for + */ + if (cd == cd_index || cd == -1){ + /* + ** Woohoo! The disk was in a different cd drive. Refresh the search path list + * and return. + */ + new_cd_drive = cd_drive; + break; + } + } + } + + /* + ** A new disc has become available so break + */ + if (new_cd_drive) break; + + /* + ** Increase the timeout for subsequent drive searches. + */ + drive_search_timeout = 5*60; + + /* + ** Prompt to insert the CD into the drive. + */ + if (cd == -1) { + sprintf(buffer, Text_String(TXT_CD_DIALOG_1), cd+1, _volid[cd]); + } else { + if (cd == 2) { + sprintf(buffer, Text_String(TXT_CD_DIALOG_3)); + } else { + sprintf(buffer, Text_String(TXT_CD_DIALOG_2), cd+1, _volid[cd]); + } + } + GraphicViewPortClass * oldpage = Set_Logic_Page(SeenBuff); + theme_playing = Theme.What_Is_Playing(); + Theme.Stop(); + int hidden = Get_Mouse_State(); + font = (void *)FontPtr; + Mem_Copy(CurrentPalette, _palette, 768); + Mem_Copy(Get_Font_Palette_Ptr(), _hold, 256); + + + /* + ** Only set the palette if necessary. + */ +// if (CurrentPalette[3] == 0) { + Set_Palette(GamePalette); +// } + + /* + ** Pretend we are in the game, even if we arent + */ + old_in_main_loop = InMainLoop; + InMainLoop = true; + + Keyboard::Clear(); + + while (Get_Mouse_State()) Show_Mouse(); + + if (CCMessageBox().Process(buffer, TXT_OK, TXT_CANCEL, TXT_NONE, TRUE) == 1) { + Set_Logic_Page(oldpage); + Hide_Mouse(); + InMainLoop = old_in_main_loop; + return(false); + } + while (hidden--) Hide_Mouse(); + Set_Palette(_palette); + Set_Font(font); + Mem_Copy(_hold, Get_Font_Palette_Ptr(), 256); + Set_Logic_Page(oldpage); + InMainLoop = old_in_main_loop; + } + } + + +#ifndef DEMO + + CCFileClass::Set_CD_Drive(new_cd_drive); + CCFileClass::Refresh_Search_Drives(); + + /* + ** If it broke out of the query for CD-ROM loop, then this means that the + ** CD-ROM has been inserted. + */ + if (cd > -1 && _last != cd) { + _last = cd; + + Theme.Stop(); + + if (MoviesMix) delete MoviesMix; + if (GeneralMix) delete GeneralMix; + if (ScoreMix) delete ScoreMix; + + MoviesMix = new MixFileClass("MOVIES.MIX"); + GeneralMix = new MixFileClass("GENERAL.MIX"); + ScoreMix = new MixFileClass("SCORES.MIX"); + ThemeClass::Scan(); + } +#endif + + if (theme_playing != THEME_NONE) { + Theme.Queue_Song(theme_playing); + } + + return(true); +} + + +/*************************************************************************** + * DISK_SPACE_AVAILABLE -- returns bytes of free disk space * + * * + * INPUT: none * + * * + * OUTPUT: returns amount of free disk space * + * * + * HISTORY: * + * 08/11/1995 PWG : Created. * + *=========================================================================*/ +unsigned long Disk_Space_Available(void) +{ + struct diskfree_t diskdata; + unsigned drive; + + _dos_getdrive(&drive); + _dos_getdiskfree(drive, &diskdata); + + return(diskdata.avail_clusters * diskdata.sectors_per_cluster * diskdata.bytes_per_sector); +} + + +/*********************************************************************************************** + * Validate_Error -- prints an error message when an object fails validation * + * * + * INPUT: * + * name name of object type that failed * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/15/1995 BRR : Created. * + *=============================================================================================*/ +void Validate_Error(char *name) +{ +#ifdef CHEAT_KEYS + Prog_End(); + printf("%s object error!\n",name); + exit(0); +#else + name = name; +#endif +} + + +/*********************************************************************************************** + * Do_Record_Playback -- handles saving/loading map pos & current object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/15/1995 BRR : Created. * + *=============================================================================================*/ +static void Do_Record_Playback(void) +{ + int count; + TARGET tgt; + int i; + COORDINATE coord; + ObjectClass *obj; + unsigned long sum; + unsigned long sum2; + unsigned long ltgt; + + /*------------------------------------------------------------------------ + Record a game + ------------------------------------------------------------------------*/ + if (RecordGame) { + + /*..................................................................... + For 'SuperRecord', we'll open & close the file with every entry. + .....................................................................*/ + if (SuperRecord) { + RecordFile.Open(READ|WRITE); + RecordFile.Seek(0,SEEK_END); + } + + /*..................................................................... + Save the map's location + .....................................................................*/ + RecordFile.Write(&Map.DesiredTacticalCoord, sizeof (Map.DesiredTacticalCoord)); + + /*..................................................................... + Save the current object list count + .....................................................................*/ + count = CurrentObject.Count(); + RecordFile.Write(&count, sizeof(count)); + + /*..................................................................... + Save a CRC of the selected-object list. + .....................................................................*/ + sum = 0; + for (i = 0; i < count; i++) { + ltgt = (unsigned long)(CurrentObject[i]->As_Target()); + sum += ltgt; + } + RecordFile.Write (&sum, sizeof(sum)); + + /*..................................................................... + Save all selected objects. + .....................................................................*/ + for (i = 0; i < count; i++) { + tgt = CurrentObject[i]->As_Target(); + RecordFile.Write (&tgt, sizeof(tgt)); + } + + /*..................................................................... + If 'SuperRecord', close the file now. + .....................................................................*/ + if (SuperRecord) { + RecordFile.Close(); + } + } + + /*------------------------------------------------------------------------ + Play back a game ("attract" mode) + ------------------------------------------------------------------------*/ + if (PlaybackGame) { + + /*..................................................................... + Read & set the map's location. + .....................................................................*/ + if (RecordFile.Read(&coord, sizeof(coord))==sizeof(coord)) { + if (coord != Map.DesiredTacticalCoord) { + Map.Set_Tactical_Position(coord); + } + } + + if (RecordFile.Read(&count, sizeof(count))==sizeof(count)) { + /*.................................................................. + Compute a CRC of the current object-selection list. + ..................................................................*/ + sum = 0; + for (i = 0; i < CurrentObject.Count(); i++) { + ltgt = (unsigned long)(CurrentObject[i]->As_Target()); + sum += ltgt; + } + + /*.................................................................. + Load the CRC of the objects on disk; if it doesn't match, select + all objects as they're loaded. + ..................................................................*/ + RecordFile.Read (&sum2, sizeof(sum2)); + if (sum2 != sum) + Unselect_All(); + + AllowVoice = true; + + for (i = 0; i < count; i++) { + if (RecordFile.Read (&tgt, sizeof(tgt))==sizeof(tgt)) { + obj = As_Object(tgt); + if (obj && (sum2 != sum)) { + obj->Select(); + AllowVoice = false; + } + } + } + + AllowVoice = true; + + } + + /*..................................................................... + The map isn't drawn in playback mode, so draw it here. + .....................................................................*/ + Map.Render(); + } +} +/*************************************************************************** + * HIRES_RETRIEVE -- retrieves a resolution dependant file * + * * + * INPUT: char * file name of the file to retrieve * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 01/25/1996 : Created. * + *=========================================================================*/ +void const * Hires_Retrieve(char *name) +{ + char filename[30]; + + if (SeenBuff.Get_Width() != 320) { + sprintf(filename,"H%s", name); + } else { + strcpy(filename, name); + } + return(MixFileClass::Retrieve(filename)); +} +int Get_Resolution_Factor(void) +{ + return ((SeenBuff.Get_Width() == 320) ? 0 : 1); +} diff --git a/CONQUER.DEF b/CONQUER.DEF new file mode 100644 index 0000000..6d74649 --- /dev/null +++ b/CONQUER.DEF @@ -0,0 +1,11 @@ +NAME CONQUER +DESCRIPTION 'Command and Conquer for Windows 95' +EXETYPE WINDOWS + +IMPORTS + DirectDrawCreate=DDRAW.DirectDrawCreate + IID_IDirectDraw2=DDRAW.IID_IDirectDraw2 + DirectSoundCreate=DSOUND.DirectSoundCreate + DirectPlayEnumerate=DPLAY.DirectPlayEnumerate + DirectPlayCreate=DPLAY.DirectPlayCreate + diff --git a/CONQUER.H b/CONQUER.H new file mode 100644 index 0000000..42a7df1 --- /dev/null +++ b/CONQUER.H @@ -0,0 +1,773 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#define TXT_NONE 0 // +#define TXT_CREDIT_FORMAT 1 // %3d.%02d +#define TXT_BUTTON_UPGRADE 2 // Upgrade +#define TXT_UPGRADE 3 // Upgrade Structure +#define TXT_UPGRADE_BUTTON 4 // Upgrade +#define TXT_BUTTON_SELL 5 // Sell +#define TXT_SELL 6 // Sell Structure +#define TXT_DEMOLISH 7 // Demolish Structure +#define TXT_BUTTON_REPAIR 8 // Repair +#define TXT_REPAIR 9 // Repair Structure +#define TXT_REPAIR_BUTTON 10 // Repair +#define TXT_YOU 11 // You: +#define TXT_ENEMY 12 // Enemy: +#define TXT_BUILD_DEST 13 // Buildings Destroyed By +#define TXT_UNIT_DEST 14 // Units Destroyed By +#define TXT_TIB_HARV 15 // Tiberium Harvested By +#define TXT_SCORE_1 16 // Score: %d +#define TXT_RANK_OF 17 // You have attained the rank +#define TXT_YES 18 // Yes +#define TXT_NO 19 // No +#define TXT_READY 20 // Ready +#define TXT_HOLDING 21 // Holding +#define TXT_SCENARIO_WON 22 // Accomplished +#define TXT_SCENARIO_LOST 23 // Failed +#define TXT_CHOOSE_SIDE 24 // Choose Your Side +#define TXT_START_NEW_GAME 25 // Start New Game +#define TXT_INTRO 26 // Intro & Sneak Peek +#define TXT_CANCEL 27 // Cancel +#define TXT_ROCK 28 // Rock +#define TXT_CHOAM_RESUME 29 // Resume Game +#define TXT_CHOAM_BUILD_THIS 30 // Build This +#define TXT_THANK_YOU 31 // Thank you for playing +#define TXT_FAME 32 // Hall of Fame +#define TXT_GDI 33 // Global Defense Initiative +#define TXT_NOD 34 // Brotherhood of Nod +#define TXT_CIVILIAN 35 // Civilian +#define TXT_JP 36 // Containment Team +#define TXT_OK 37 // OK +#define TXT_TREE 38 // Tree +#define TXT_LEFT 39 //  +#define TXT_RIGHT 40 //  +#define TXT_UP 41 //  +#define TXT_DOWN 42 //  +#define TXT_CLEAR_MAP 43 // Clear the map +#define TXT_INHERIT_MAP 44 // Inherit previous map +#define TXT_CLEAR 45 // Clear +#define TXT_WATER 46 // Water +#define TXT_ROAD 47 // Road +#define TXT_TILE 48 // Tile Object +#define TXT_SLOPE 49 // Slope +#define TXT_BRUSH 50 // Brush +#define TXT_PATCH 51 // Patch +#define TXT_RIVER 52 // River +#define TXT_LOAD_MISSION 53 // Load Mission +#define TXT_SAVE_MISSION 54 // Save Mission +#define TXT_DELETE_MISSION 55 // Delete Mission +#define TXT_LOAD_BUTTON 56 // Load +#define TXT_SAVE_BUTTON 57 // Save +#define TXT_DELETE_BUTTON 58 // Delete +#define TXT_GAME_CONTROLS 59 // Game Controls +#define TXT_SOUND_CONTROLS 60 // Sound Controls +#define TXT_RESUME_MISSION 61 // Resume Mission +#define TXT_VISUAL_CONTROLS 62 // Visual Controls +#define TXT_QUIT_MISSION 63 // Abort Mission +#define TXT_EXIT_GAME 64 // Exit Game +#define TXT_OPTIONS 65 // Options +#define TXT_TIBERIUM 66 // Tiberium +#define TXT_TIBERIUM_ON 67 // Tiberium On +#define TXT_TIBERIUM_OFF 68 // Tiberium Off +#define TXT_SQUISH 69 // Squish mark +#define TXT_CRATER 70 // Crater +#define TXT_SCORCH 71 // Scorch Mark +#define TXT_BRIGHTNESS 72 // BRIGHTNESS: +#define TXT_MUSIC 73 // MUSIC VOLUME +#define TXT_VOLUME 74 // SOUND VOLUME +#define TXT_TINT 75 // TINT: +#define TXT_CONTRAST 76 // CONTRAST: +#define TXT_SPEED 77 // GAME SPEED: +#define TXT_SCROLLRATE 78 // SCROLL RATE: +#define TXT_COLOR 79 // COLOR: +#define TXT_RETURN_TO_GAME 80 // Return to game +#define TXT_ENEMY_SOLDIER 81 // Enemy Soldier +#define TXT_ENEMY_VEHICLE 82 // Enemy Vehicle +#define TXT_ENEMY_STRUCTURE 83 // Enemy Structure +#define TXT_FTANK 84 // Flame Tank +#define TXT_STANK 85 // Stealth Tank +#define TXT_LTANK 86 // Light Tank +#define TXT_MTANK 87 // Med. Tank +#define TXT_HTANK 88 // Mammoth Tank +#define TXT_DUNE_BUGGY 89 // Nod Buggy +#define TXT_SAM 90 // SAM Site +#define TXT_EYE 91 // Advanced Com. Center +#define TXT_MLRS 92 // Rocket Launcher +#define TXT_MHQ 93 // Mobile HQ +#define TXT_JEEP 94 // Hum-vee +#define TXT_TRANS 95 // Transport Helicopter +#define TXT_A10 96 // A10 +#define TXT_C17 97 // C17 +#define TXT_HARVESTER 98 // Harvester +#define TXT_ARTY 99 // Artillery +#define TXT_MSAM 100 // S.S.M. Launcher +#define TXT_E1 101 // Minigunner +#define TXT_E2 102 // Grenadier +#define TXT_E3 103 // Bazooka +#define TXT_E4 104 // Flamethrower +#define TXT_E5 105 // Chem-warrior +#define TXT_RAMBO 106 // Commando +#define TXT_HOVER 107 // Hovercraft +#define TXT_HELI 108 // Attack Helicopter +#define TXT_ORCA 109 // Orca +#define TXT_APC 110 // APC +#define TXT_GUARD_TOWER 111 // Guard Tower +#define TXT_COMMAND 112 // Communications Center +#define TXT_HELIPAD 113 // Helicopter Pad +#define TXT_AIRSTRIP 114 // Airstrip +#define TXT_STORAGE 115 // Tiberium Silo +#define TXT_CONST_YARD 116 // Construction Yard +#define TXT_REFINERY 117 // Tiberium Refinery +#define TXT_CIV1 118 // Church +#define TXT_CIV2 119 // Han's and Gretel's +#define TXT_CIV3 120 // Hewitt's Manor +#define TXT_CIV4 121 // Ricktor's House +#define TXT_CIV5 122 // Gretchin's House +#define TXT_CIV6 123 // The Barn +#define TXT_CIV7 124 // Damon's pub +#define TXT_CIV8 125 // Fran's House +#define TXT_CIV9 126 // Music Factory +#define TXT_CIV10 127 // Toymaker's +#define TXT_CIV11 128 // Ludwig's House +#define TXT_CIV12 129 // Haystacks +#define TXT_CIV13 130 // Haystack +#define TXT_CIV14 131 // Wheat Field +#define TXT_CIV15 132 // Fallow Field +#define TXT_CIV16 133 // Corn Field +#define TXT_CIV17 134 // Celery Field +#define TXT_CIV18 135 // Potato Field +#define TXT_CIV20 136 // Sala's House +#define TXT_CIV21 137 // Abdul's House +#define TXT_CIV22 138 // Pablo's Wicked Pub +#define TXT_CIV23 139 // Village Well +#define TXT_CIV24 140 // Camel Trader +#define TXT_CIV25 141 // Church +#define TXT_CIV26 142 // Ali's House +#define TXT_CIV27 143 // Trader Ted's +#define TXT_CIV28 144 // Menelik's House +#define TXT_CIV29 145 // Prestor John's House +#define TXT_CIV30 146 // Village Well +#define TXT_CIV31 147 // Witch Doctor's Hut +#define TXT_CIV32 148 // Rikitikitembo's Hut +#define TXT_CIV33 149 // Roarke's Hut +#define TXT_CIV34 150 // Mubasa's Hut +#define TXT_CIV35 151 // Aksum's Hut +#define TXT_CIV36 152 // Mambo's Hut +#define TXT_CIV37 153 // The Studio +#define TXT_CIVMISS 154 // Technology Center +#define TXT_TURRET 155 // Gun Turret +#define TXT_GUNBOAT 156 // Gun Boat +#define TXT_MCV 157 // Mobile Construction Yard +#define TXT_BIKE 158 // Recon Bike +#define TXT_POWER 159 // Power Plant +#define TXT_ADVANCED_POWER 160 // Advanced Power Plant +#define TXT_HOSPITAL 161 // Hospital +#define TXT_BARRACKS 162 // Barracks +#define TXT_CONCRETE 163 // Concrete +#define TXT_PUMP 164 // Oil Pump +#define TXT_TANKER 165 // Oil Tanker +#define TXT_SANDBAG_WALL 166 // Sandbag Wall +#define TXT_CYCLONE_WALL 167 // Chain Link Fence +#define TXT_BRICK_WALL 168 // Concrete Wall +#define TXT_BARBWIRE_WALL 169 // Barbwire Fence +#define TXT_WOOD_WALL 170 // Wood Fence +#define TXT_WEAPON_FACTORY 171 // Weapons Factory +#define TXT_AGUARD_TOWER 172 // Advanced Guard Tower +#define TXT_OBELISK 173 // Obelisk Guard Tower +#define TXT_BIO_LAB 174 // Bio-Research Laboratory +#define TXT_HAND 175 // Hand of Nod +#define TXT_TEMPLE 176 // Temple of Nod +#define TXT_FIX_IT 177 // Repair Bay +#define TXT_TAB_SIDEBAR 178 // Sidebar +#define TXT_TAB_BUTTON_CONTROLS 179 // Options +#define TXT_TAB_BUTTON_DATABASE 180 // Database +#define TXT_SHADOW 181 // Unrevealed Terrain +#define TXT_OPTIONS_MENU 182 // Options Menu +#define TXT_STOP 183 // STOP +#define TXT_PLAY 184 // PLAY +#define TXT_SHUFFLE 185 // SHUFFLE +#define TXT_REPEAT 186 // REPEAT +#define TXT_MUSIC_VOLUME 187 // Music volume: +#define TXT_SOUND_VOLUME 188 // Sound volume: +#define TXT_ON 189 // On +#define TXT_OFF 190 // Off +#define TXT_THEME_AOI 191 // Act On Instinct +#define TXT_THEME_TROUBLE 192 // Looks Like Trouble +#define TXT_THEME_IND 193 // Industrial +#define TXT_THEME_ROUT 194 // Reaching Out +#define TXT_THEME_OTP 195 // On The Prowl +#define TXT_THEME_PRP 196 // Prepare For Battle +#define TXT_THEME_JUSTDOIT 197 // Just Do It! +#define TXT_THEME_LINEFIRE 198 // In The Line Of Fire +#define TXT_THEME_MARCH 199 // March To Doom +#define TXT_THEME_STOPTHEM 200 // Deception +#define TXT_THEME_CCTHANG 201 // C&C Thang +#define TXT_THEME_BEFEARED 202 // Enemies To Be Feared +#define TXT_THEME_WARFARE 203 // Warfare +#define TXT_THEME_FWP 204 // Fight, Win, Prevail +#define TXT_THEME_DIE 205 // Die!! +#define TXT_THEME_NOMERCY 206 // No Mercy +#define TXT_THEME_TARGET 207 // Mechanical Man +#define TXT_THEME_IAM 208 // I Am +#define TXT_THEME_WIN1 209 // Great Shot! +#define TXT_MULTIPLAYER_GAME 210 // Multiplayer Game +#define TXT_NO_FILES 211 // No files available +#define TXT_DELETE_SINGLE_FILE 212 // Do you want to delete this +#define TXT_DELETE_MULTIPLE_FILES 213 // Do you want to delete %d +#define TXT_RESET_MENU 214 // Reset Values +#define TXT_CONFIRMATION 215 // Confirmation +#define TXT_CONFIRM_EXIT 216 // Do you want to abort the +#define TXT_MISSION_DESCRIPTION 217 // Mission Description +#define TXT_C1 218 // Joe +#define TXT_C2 219 // Bill +#define TXT_C3 220 // Shelly +#define TXT_C4 221 // Maria +#define TXT_C5 222 // Eydie +#define TXT_C6 223 // Dave +#define TXT_C7 224 // Phil +#define TXT_C8 225 // Dwight +#define TXT_C9 226 // Erik +#define TXT_MOEBIUS 227 // Dr. Moebius +#define TXT_BIB 228 // Road Bib +#define TXT_FASTER 229 // Faster +#define TXT_SLOWER 230 // Slower +#define TXT_ION_CANNON 231 // Ion Cannon +#define TXT_NUKE_STRIKE 232 // Nuclear Strike +#define TXT_AIR_STRIKE 233 // Air Strike +#define TXT_TREX 234 // Tyrannosaurus Rex +#define TXT_TRIC 235 // Triceratops +#define TXT_RAPT 236 // Velociraptor +#define TXT_STEG 237 // Stegasaurus +#define TXT_STEEL_CRATE 238 // Steel Crate +#define TXT_WOOD_CRATE 239 // Wood Crate +#define TXT_FLAG_SPOT 240 // Flag Location +#define TXT_G_D_I 241 // GDI +#define TXT_N_O_D 242 // NOD +#define TXT_UNABLE_READ_SCENARIO 243 // Unable to read scenario! +#define TXT_ERROR_LOADING_GAME 244 // Error loading game! +#define TXT_OBSOLETE_SAVEGAME 245 // Obsolete saved game. +#define TXT_MUSTENTER_DESCRIPTION 246 // You must enter a +#define TXT_ERROR_SAVING_GAME 247 // Error saving game! +#define TXT_DELETE_FILE_QUERY 248 // Delete this file? +#define TXT_EMPTY_SLOT 249 // [EMPTY SLOT] +#define TXT_SELECT_MPLAYER_GAME 250 // Select Multiplayer Game +#define TXT_MODEM_SERIAL 251 // Modem/Serial +#define TXT_NETWORK 252 // Network +#define TXT_INIT_NET_ERROR 253 // Unable to initialize +#define TXT_JOIN_NETWORK_GAME 254 // Join Network Game +#define TXT_NEW 255 // New +#define TXT_JOIN 256 // Join +#define TXT_SEND_MESSAGE 257 // Send Message +#define TXT_YOUR_NAME 258 // Your Name: +#define TXT_SIDE_COLON 259 // Side: +#define TXT_COLOR_COLON 260 // Color: +#define TXT_GAMES 261 // Games +#define TXT_PLAYERS 262 // Players +#define TXT_SCENARIO_COLON 263 // Scenario: +#define TXT_NOT_FOUND 264 // >> NOT FOUND << +#define TXT_START_CREDITS_COLON 265 // Starting Credits: +#define TXT_BASES_COLON 266 // Bases: +#define TXT_TIBERIUM_COLON 267 // Tiberium: +#define TXT_CRATES_COLON 268 // Crates: +#define TXT_AI_PLAYERS_COLON 269 // AI Players: +#define TXT_REQUEST_DENIED 270 // Request denied. +#define TXT_UNABLE_PLAY_WAAUGH 271 // Unable to play; scenario +#define TXT_NOTHING_TO_JOIN 272 // Nothing to join! +#define TXT_NAME_ERROR 273 // You must enter a name! +#define TXT_DUPENAMES_NOTALLOWED 274 // Duplicate names are not +#define TXT_YOURGAME_OUTDATED 275 // Your game version is +#define TXT_DESTGAME_OUTDATED 276 // Destination game version is +#define TXT_THATGUYS_GAME 277 // %s's Game +#define TXT_THATGUYS_GAME_BRACKET 278 // [%s's Game] +#define TXT_NETGAME_SETUP 279 // Network Game Setup +#define TXT_REJECT 280 // Reject +#define TXT_CANT_REJECT_SELF 281 // You can't reject yourself! +#define TXT_SELECT_PLAYER_REJECT 282 // You must select a player to +#define TXT_BASES_ON 283 // Bases On +#define TXT_BASES_OFF 284 // Bases Off +#define TXT_CRATES_ON 285 // Crates On +#define TXT_CRATES_OFF 286 // Crates Off +#define TXT_AI_PLAYERS_ON 287 // AI Players On +#define TXT_AI_PLAYERS_OFF 288 // AI Players Off +#define TXT_SCENARIOS 289 // Scenarios +#define TXT_START_CREDITS 290 // Starting Credits +#define TXT_ONLY_ONE 291 // Only one player? +#define TXT_OOPS 292 // Oops! +#define TXT_TO 293 // To %s: +#define TXT_TO_ALL 294 // To All: +#define TXT_MESSAGE 295 // Message: +#define TXT_CONNECTION_LOST 296 // Connection to %s lost! +#define TXT_LEFT_GAME 297 // %s has left the game. +#define TXT_PLAYER_DEFEATED 298 // %s has been defeated! +#define TXT_WAITING_CONNECT 299 // Waiting to Connect... +#define TXT_NULL_CONNERR_CHECK_CABLES 300 // Connection error! Check +#define TXT_MODEM_CONNERR_REDIALING 301 // Connection +#define TXT_MODEM_CONNERR_WAITING 302 // Connection error! Waiting +#define TXT_SELECT_SERIAL_GAME 303 // Select Serial Game +#define TXT_DIAL_MODEM 304 // Dial Modem +#define TXT_ANSWER_MODEM 305 // Answer Modem +#define TXT_NULL_MODEM 306 // Null Modem +#define TXT_SETTINGS 307 // Settings +#define TXT_PORT_COLON 308 // Port: +#define TXT_IRQ_COLON 309 // IRQ: +#define TXT_BAUD_COLON 310 // Baud: +#define TXT_INIT_STRING 311 // Init String: +#define TXT_CWAIT_STRING 312 // Call Waiting String: +#define TXT_TONE_BUTTON 313 // Tone Dialing +#define TXT_PULSE_BUTTON 314 // Pulse Dialing +#define TXT_HOST_SERIAL_GAME 315 // Host Serial Game +#define TXT_OPPONENT_COLON 316 // Opponent: +#define TXT_USER_SIGNED_OFF 317 // User signed off! +#define TXT_JOIN_SERIAL_GAME 318 // Join Serial Game +#define TXT_PHONE_LIST 319 // Phone List +#define TXT_ADD 320 // Add +#define TXT_EDIT 321 // Edit +#define TXT_DIAL 322 // Dial +#define TXT_DEFAULT 323 // Default +#define TXT_DEFAULT_SETTINGS 324 // Default Settings +#define TXT_CUSTOM_SETTINGS 325 // Custom Settings +#define TXT_PHONE_LISTING 326 // Phone Listing +#define TXT_NAME_COLON 327 // Name: +#define TXT_NUMBER_COLON 328 // Number: +#define TXT_UNABLE_FIND_MODEM 329 // Unable to find modem. Check +#define TXT_NO_CARRIER 330 // No carrier. +#define TXT_LINE_BUSY 331 // Line busy. +#define TXT_NUMBER_INVALID 332 // Number invalid. +#define TXT_SYSTEM_NOT_RESPONDING 333 // Other system not +#define TXT_OUT_OF_SYNC 334 // Games are out of sync! +#define TXT_PACKET_TOO_LATE 335 // Packet received too late! +#define TXT_PLAYER_LEFT_GAME 336 // Other player has left the +#define TXT_FROM 337 // From %s:%s +#define TXT_MAP_P01 338 // 2,728,000 +#define TXT_MAP_P02 339 // 38,385,000 +#define TXT_MAP_P03 340 // 10,373,000 +#define TXT_MAP_P04 341 // 51,994,000 +#define TXT_MAP_P05 342 // 80,387,000 +#define TXT_MAP_P06 343 // 10,400,000 +#define TXT_MAP_P07 344 // 5,300,000 +#define TXT_MAP_P08 345 // 7,867,000 +#define TXT_MAP_P09 346 // 10,333,000 +#define TXT_MAP_P10 347 // 1,974,000 +#define TXT_MAP_P11 348 // 23,169,000 +#define TXT_MAP_P12 349 // 10,064,000 +#define TXT_MAP_P13 350 // 3,285,000 +#define TXT_MAP_P14 351 // 8,868,000 +#define TXT_MAP_P15 352 // 10,337,000 +#define TXT_MAP_P16 353 // 4,365,000 +#define TXT_MAP_P17 354 // 1,607,000 +#define TXT_MAP_P18 355 // 4,485,000 +#define TXT_MAP_P19 356 // 56,386,000 +#define TXT_MAP_P20 357 // 28,305,000 +#define TXT_MAP_P21 358 // 5,238,000 +#define TXT_MAP_P22 359 // 2,059,000 +#define TXT_MAP_P23 360 // 13,497,000 +#define TXT_MAP_P24 361 // 4,997,000 +#define TXT_MAP_P25 362 // 88,500,000 +#define TXT_MAP_P26 363 // 1,106,000 +#define TXT_MAP_P27 364 // 12,658,000 +#define TXT_MAP_P28 365 // 3,029,000 +#define TXT_MAP_P29 366 // 39,084,000 +#define TXT_MAP_P30 367 // 23,154,000 +#define TXT_MAP_P31 368 // 8,902,000 +#define TXT_MAP_P32 369 // 27,791,000 +#define TXT_MAP_P33 370 // 1,574,000 +#define TXT_MAP_P34 371 // 15,469,000 +#define TXT_MAP_P35 372 // 1,300,000 +#define TXT_MAP_P36 373 // 41,688,000 +#define TXT_MAP_A00 374 // 24,900 SQ. MI. +#define TXT_MAP_A01 375 // 120,727 SQ. MI. +#define TXT_MAP_A02 376 // 80,134 SQ. MI. +#define TXT_MAP_A03 377 // 233,100 SQ. MI. +#define TXT_MAP_A04 378 // 137,838 SQ. MI. +#define TXT_MAP_A05 379 // 30,449 SQ. MI. +#define TXT_MAP_A06 380 // 18,932 SQ. MI. +#define TXT_MAP_A07 381 // 32,377 SQ. MI. +#define TXT_MAP_A08 382 // 35,919 SQ. MI. +#define TXT_MAP_A09 383 // 7,819 SQ. MI. +#define TXT_MAP_A10 384 // 91,699 SQ. MI. +#define TXT_MAP_A11 385 // 51,146 SQ. MI. +#define TXT_MAP_A12 386 // 11,100 SQ. MI. +#define TXT_MAP_A13 387 // 44,365 SQ. MI. +#define TXT_MAP_A14 388 // 39,449 SQ. MI. +#define TXT_MAP_A15 389 // 19,741 SQ. MI. +#define TXT_MAP_A16 390 // 17,413 SQ. MI. +#define TXT_MAP_C00 391 // RIGA +#define TXT_MAP_C01 392 // WARSAW +#define TXT_MAP_C02 393 // MINSK +#define TXT_MAP_C03 394 // KIEV +#define TXT_MAP_C04 395 // BERLIN +#define TXT_MAP_C05 396 // PRAGUE +#define TXT_MAP_C06 397 // BRATISLAVA +#define TXT_MAP_C07 398 // VIENNA +#define TXT_MAP_C08 399 // BUDAPEST +#define TXT_MAP_C09 400 // LJUBLJANA +#define TXT_MAP_C10 401 // BUCHAREST +#define TXT_MAP_C11 402 // ATHENS +#define TXT_MAP_C12 403 // TIRANA +#define TXT_MAP_C13 404 // SOFIA +#define TXT_MAP_C14 405 // BELGRADE +#define TXT_MAP_C15 406 // SARAJEVO +#define TXT_MAP_C16 407 // TALLINN +#define TXT_MAP_C17 408 // TRIPOLI +#define TXT_MAP_C18 409 // CAIRO +#define TXT_MAP_C19 410 // KHARTOUM +#define TXT_MAP_C20 411 // N'DJAMENA +#define TXT_MAP_C21 412 // NOUAKCHOTT +#define TXT_MAP_C22 413 // YAMOUSSOUKRO +#define TXT_MAP_C23 414 // PORTO-NOVO +#define TXT_MAP_C24 415 // ABUJA +#define TXT_MAP_C25 416 // LIBREVILLE +#define TXT_MAP_C26 417 // YAOUNDE +#define TXT_MAP_C27 418 // BANGUI +#define TXT_MAP_C28 419 // KINSHASA +#define TXT_MAP_C29 420 // CAIRO +#define TXT_MAP_C30 421 // LUANDA +#define TXT_MAP_C31 422 // DAR-ES-SALAAM +#define TXT_MAP_C32 423 // WINDHOEK +#define TXT_MAP_C33 424 // MAPUTO +#define TXT_MAP_C34 425 // GABARONE +#define TXT_MAP_C35 426 // CAPE TOWN +#define TXT_MAP_GDP00 427 // NEGLIGIBLE +#define TXT_MAP_GDP01 428 // $162.7 BLN +#define TXT_MAP_GDP02 429 // $47.6 BLN +#define TXT_MAP_GDP03 430 // $1,131 BLN +#define TXT_MAP_GDP04 431 // $120 BLN +#define TXT_MAP_GDP05 432 // $164 BLN +#define TXT_MAP_GDP06 433 // $60.1 BLN +#define TXT_MAP_GDP07 434 // $21 BLN +#define TXT_MAP_GDP08 435 // $71.9 BLN +#define TXT_MAP_GDP09 436 // $77 BLN +#define TXT_MAP_GDP10 437 // $4.0 BLN +#define TXT_MAP_GDP11 438 // $47.3 BLN +#define TXT_MAP_GDP12 439 // $120.1 BLN +#define TXT_MAP_GDP13 440 // $14.0 BLN +#define TXT_MAP_GDP14 441 // $28.9 BLN +#define TXT_MAP_GDP15 442 // $39.2 BLN +#define TXT_MAP_GDP16 443 // $12.1 BLN +#define TXT_MAP_GDP17 444 // $1.0 BLN +#define TXT_MAP_GDP18 445 // $10.0 BLN +#define TXT_MAP_GDP19 446 // $1.7 BLN +#define TXT_MAP_GDP20 447 // $28.0 BLN +#define TXT_MAP_GDP21 448 // $5.3 BLN +#define TXT_MAP_GDP22 449 // $11.6 BLN +#define TXT_MAP_GDP23 450 // $1.3 BLN +#define TXT_MAP_GDP24 451 // $6.6 BLN +#define TXT_MAP_GDP25 452 // $8.3 BLN +#define TXT_MAP_GDP26 453 // $6.9 BLN +#define TXT_MAP_GDP27 454 // $2.0 BLN +#define TXT_MAP_GDP28 455 // $3.1 BLN +#define TXT_MAP_GDP29 456 // $104.0 BLN +#define TXT_MAP_PC00 457 // JELGAVA +#define TXT_MAP_PC01 458 // GDANSK +#define TXT_MAP_PC02 459 // BYELISTOK +#define TXT_MAP_PC03 460 // BOBYRUSK +#define TXT_MAP_PC04 461 // IVANO-FRANKOVSK +#define TXT_MAP_PC05 462 // HANOVER +#define TXT_MAP_PC06 463 // DRESDEN +#define TXT_MAP_PC07 464 // OSTRAVA +#define TXT_MAP_PC08 465 // BRATISLAVA +#define TXT_MAP_PC09 466 // SALZBURG +#define TXT_MAP_PC10 467 // BUDAPEST +#define TXT_MAP_PC11 468 // TRIESTE +#define TXT_MAP_PC12 469 // ARAD +#define TXT_MAP_PC13 470 // CORINTH +#define TXT_MAP_PC14 471 // SHKODER +#define TXT_MAP_PC15 472 // SOFIA +#define TXT_MAP_PC16 473 // NIS +#define TXT_MAP_PC17 474 // BELGRADE +#define TXT_MAP_PC18 475 // ? +#define TXT_MAP_PC19 476 // PARNU +#define TXT_MAP_PC20 477 // TMASSAH +#define TXT_MAP_PC21 478 // AL-ALAMYN +#define TXT_MAP_PC22 479 // AL-KHARIJAH +#define TXT_MAP_PC23 480 // AL-UBAYYID +#define TXT_MAP_PC24 481 // KAFIA-KINGI +#define TXT_MAP_PC25 482 // OUM HADJER +#define TXT_MAP_PC26 483 // MAO +#define TXT_MAP_PC27 484 // TIDJIKDJA +#define TXT_MAP_PC28 485 // ABIDJAN +#define TXT_MAP_PC29 486 // PORTO-NOVO +#define TXT_MAP_PC30 487 // ABUJA +#define TXT_MAP_PC31 488 // KOULA-MOUTOU +#define TXT_MAP_PC32 489 // BERTOUA +#define TXT_MAP_PC33 490 // BANGASSOU +#define TXT_MAP_PC34 491 // LODJA +#define TXT_MAP_PC35 492 // KINSHASA +#define TXT_MAP_PC36 493 // LUXOR +#define TXT_MAP_PC37 494 // CAIUNDO +#define TXT_MAP_PC38 495 // MZUZU +#define TXT_MAP_PC39 496 // KEETMANSHOOP +#define TXT_MAP_PC40 497 // XAI-XAI +#define TXT_MAP_PC41 498 // GHANZI +#define TXT_MAP_PC42 499 // CAPE TOWN +#define TXT_MAP_GDI 500 // GDI PROGRESSION +#define TXT_MAP_NOD 501 // NOD PROGRESSION +#define TXT_MAP_LOCATE 502 // LOCATING COORDINATES +#define TXT_MAP_NEXT_MISSION 503 // OF NEXT MISSION +#define TXT_MAP_SELECT 504 // SELECT TERRITORY +#define TXT_MAP_TO_ATTACK 505 // TO ATTACK +#define TXT_MAP_GDISTAT0 506 // POPULATION: +#define TXT_MAP_GDISTAT1 507 // GEOGRAPHIC AREA: +#define TXT_MAP_GDISTAT2 508 // CAPITAL: +#define TXT_MAP_GDISTAT3 509 // GOVERNMENT: +#define TXT_MAP_GDISTAT4 510 // GROSS DOMESTIC PRODUCT: +#define TXT_MAP_GDISTAT5 511 // POINT OF CONFLICT: +#define TXT_MAP_GDISTAT6 512 // MILITARY POWER: +#define TXT_MAP_NODSTAT0 513 // EXPENDABILITY: +#define TXT_MAP_NODSTAT1 514 // GOVT CORRUPTABILITY: +#define TXT_MAP_NODSTAT2 515 // NET WORTH: +#define TXT_MAP_NODSTAT3 516 // MILITARY STRENGTH: +#define TXT_MAP_NODSTAT4 517 // MILITARY RESISTANCE: +#define TXT_MAP_COUNTRYNAME0 518 // LATVIA +#define TXT_MAP_COUNTRYNAME1 519 // POLAND +#define TXT_MAP_COUNTRYNAME2 520 // BELARUS +#define TXT_MAP_COUNTRYNAME3 521 // UKRAINE +#define TXT_MAP_COUNTRYNAME4 522 // GERMANY +#define TXT_MAP_COUNTRYNAME5 523 // CZECH REPUBLIC +#define TXT_MAP_COUNTRYNAME6 524 // SLOVAKIA +#define TXT_MAP_COUNTRYNAME7 525 // AUSTRIA +#define TXT_MAP_COUNTRYNAME8 526 // HUNGARY +#define TXT_MAP_COUNTRYNAME9 527 // SLOVENIA +#define TXT_MAP_COUNTRYNAME10 528 // ROMANIA +#define TXT_MAP_COUNTRYNAME11 529 // GREECE +#define TXT_MAP_COUNTRYNAME12 530 // ALBANIA +#define TXT_MAP_COUNTRYNAME13 531 // BULGARIA +#define TXT_MAP_COUNTRYNAME14 532 // YUGOSLAVIA +#define TXT_MAP_COUNTRYNAME15 533 // BOSNIA/HERZOGOVINA +#define TXT_MAP_COUNTRYNAME16 534 // LIBYA +#define TXT_MAP_COUNTRYNAME17 535 // EGYPT +#define TXT_MAP_COUNTRYNAME18 536 // SUDAN +#define TXT_MAP_COUNTRYNAME19 537 // CHAD +#define TXT_MAP_COUNTRYNAME20 538 // MAURITANIA +#define TXT_MAP_COUNTRYNAME21 539 // IVORY COAST +#define TXT_MAP_COUNTRYNAME22 540 // BENIN +#define TXT_MAP_COUNTRYNAME23 541 // NIGERIA +#define TXT_MAP_COUNTRYNAME24 542 // GABON +#define TXT_MAP_COUNTRYNAME25 543 // CAMEROON +#define TXT_MAP_COUNTRYNAME26 544 // CENTRAL AFRICAN REPUBLIC +#define TXT_MAP_COUNTRYNAME27 545 // ZAIRE +#define TXT_MAP_COUNTRYNAME28 546 // ANGOLA +#define TXT_MAP_COUNTRYNAME29 547 // TANZANIA +#define TXT_MAP_COUNTRYNAME30 548 // NAMIBIA +#define TXT_MAP_COUNTRYNAME31 549 // MOZAMBIQUE +#define TXT_MAP_COUNTRYNAME32 550 // BOTSWANA +#define TXT_MAP_COUNTRYNAME33 551 // SOUTH AFRICA +#define TXT_MAP_COUNTRYNAME34 552 // ESTONIA +#define TXT_MAP_GOVT0 553 // REPUBLIC +#define TXT_MAP_GOVT1 554 // DEMOCRATIC STATE +#define TXT_MAP_GOVT2 555 // FEDERAL REPUBLIC +#define TXT_MAP_GOVT3 556 // CONST. REPUBLIC +#define TXT_MAP_GOVT4 557 // PARL. DEMOCRACY +#define TXT_MAP_GOVT5 558 // PRES. PARL. REPUBLIC +#define TXT_MAP_GOVT6 559 // DEMOCRACY +#define TXT_MAP_GOVT7 560 // IN TRANSITION +#define TXT_MAP_GOVT8 561 // ISLAMIC SOCIALIST +#define TXT_MAP_GOVT9 562 // MILITARY +#define TXT_MAP_GOVT10 563 // ISLAMIC REPUBLIC +#define TXT_MAP_GOVT11 564 // PARL. REPUBLIC +#define TXT_MAP_ARMY0 565 // LOCAL MILITIA +#define TXT_MAP_ARMY1 566 // STATE MILITIA +#define TXT_MAP_ARMY2 567 // NATIONAL GUARD +#define TXT_MAP_ARMY3 568 // FREE STANDING ARMY +#define TXT_MAP_ARMY4 569 // ? +#define TXT_MAP_ARMY5 570 // NATIONAL POWER +#define TXT_MAP_MILITARY0 571 // RESPECTABLE +#define TXT_MAP_MILITARY1 572 // FORMIDABLE +#define TXT_MAP_MILITARY2 573 // LAUGHABLE +#define TXT_MAP_MILITARY3 574 // REASONABLE +#define TXT_MAP_MILITARY4 575 // INSIGNIFICANT +#define TXT_MAP_CLICK2 576 // CLICK TO CONTINUE +#define TXT_MAP_LMH0 577 // LOW +#define TXT_MAP_LMH1 578 // MEDIUM +#define TXT_MAP_LMH2 579 // HIGH +#define TXT_SCORE_TIME 580 // TIME: +#define TXT_SCORE_LEAD 581 // LEADERSHIP: +#define TXT_SCORE_EFFI 582 // EFFICIENCY: +#define TXT_SCORE_TOTA 583 // TOTAL SCORE: +#define TXT_SCORE_CASU 584 // CASUALTIES: +#define TXT_SCORE_NEUT 585 // NEUTRAL: +#define TXT_SCORE_GDI 586 // GDI: +#define TXT_SCORE_BUIL 587 // BUILDINGS LOST +#define TXT_SCORE_BUIL1 588 // BUILDINGS +#define TXT_SCORE_BUIL2 589 // LOST: +#define TXT_SCORE_TOP 590 // TOP SCORES +#define TXT_SCORE_ENDCRED 591 // ENDING CREDITS: +#define TXT_SCORE_TIMEFORMAT1 592 // %dh %dm +#define TXT_SCORE_TIMEFORMAT2 593 // %dm +#define TXT_SCORE_NOD 594 // NOD: +#define TXT_DIALING 595 // Dialing... +#define TXT_DIALING_CANCELED 596 // Dialing Canceled +#define TXT_WAITING_FOR_CALL 597 // Waiting for Call... +#define TXT_ANSWERING_CANCELED 598 // Answering Canceled +#define TXT_E7 599 // Engineer +#define TXT_SPECIAL_OPTIONS 600 // Special Options +#define TXT_VISIBLE_TARGET 601 // Targeting flash visible to +#define TXT_TREE_TARGET 602 // Allow targeting of trees. +#define TXT_MCV_DEPLOY 603 // Allow undeploy of +#define TXT_SMART_DEFENCE 604 // Employ smarter self defense +#define TXT_SLOW_BUILD 605 // Moderate production speed. +#define TXT_THREE_POINT 606 // Use three point turn logic. +#define TXT_TIBERIUM_GROWTH 607 // Tiberium will grow. +#define TXT_TIBERIUM_SPREAD 608 // Tiberium will spread. +#define TXT_ROAD_PIECES 609 // Disable building "bib" +#define TXT_SCATTER 610 // Allow running from +#define TXT_MODEM_OR_LOOPBACK 611 // Not a Null Modem Cable +#define TXT_MAP 612 // Map +#define TXT_FROM_COMPUTER 613 // From Computer: +#define TXT_COMP_MSG1 614 // Prepare to die! +#define TXT_COMP_MSG2 615 // How about a bullet +#define TXT_COMP_MSG3 616 // Incoming! +#define TXT_COMP_MSG4 617 // I see you! +#define TXT_COMP_MSG5 618 // Hey, I'm over here! +#define TXT_COMP_MSG6 619 // Come get some! +#define TXT_COMP_MSG7 620 // I got you! +#define TXT_COMP_MSG8 621 // You humans are never a +#define TXT_COMP_MSG9 622 // Abort, Retry, Ignore? (Ha +#define TXT_COMP_MSG10 623 // Format another? (Just +#define TXT_COMP_MSG11 624 // Beat me and I'll reboot! +#define TXT_COMP_MSG12 625 // You're artificial +#define TXT_COMP_MSG13 626 // My AI is better than your +#define TXT_THEME_AIRSTRIKE 627 // Air Strike +#define TXT_THEME_HEAVYG 628 // Demolition +#define TXT_THEME_J1 629 // Untamed Land +#define TXT_THEME_JDI_V2 630 // Take 'em Out +#define TXT_THEME_RADIO 631 // Radio +#define TXT_THEME_RAIN 632 // Rain In The Night +#define TXT_THEME_IND2 633 // Canyon Chase +#define TXT_THEME_HEART 634 // Heartbreak +#define TXT_BLOSSOM_TREE 635 // Blossom Tree +#define TXT_RESTATE_MISSION 636 // Restate +#define TXT_COMPUTER 637 // Computer +#define TXT_COUNT 638 // Unit Count: +#define TXT_LEVEL 639 // Tech Level: +#define TXT_OPPONENT 640 // Opponent +#define TXT_KILLS_COLON 641 // Kills: +#define TXT_VIDEO 642 // Video +#define TXT_C10 643 // Nikoomba +#define TXT_CAPTURE_THE_FLAG 644 // Capture The Flag +#define TXT_THEME_VALK 645 // Ride of the Valkyries +#define TXT_OBJECTIVE 646 // Mission Objective +#define TXT_MISSION 647 // Mission +#define TXT_NO_SAVES 648 // No saved games available. +#define TXT_CIVILIAN_BUILDING 649 // Civilian Building +#define TXT_TECHNICIAN 650 // Technician +#define TXT_VISCEROID 651 // Visceroid +#define TXT_NO_SAVELOAD 652 // Save game options are not +#define TXT_DEFENDER_ADVANTAGE 653 // Defender has the advantage. +#define TXT_SHOW_NAMES 654 // Show true object names. +#define TXT_DELPHI 655 // Agent Delphi +#define TXT_TO_REPLAY 656 // Would you like to replay +#define TXT_RECONN_TO 657 // Reconnecting to %s. +#define TXT_PLEASE_WAIT 658 // Please wait %02d seconds. +#define TXT_SURRENDER 659 // Do you wish to surrender? +#define TXT_GDI_NAME 660 // GLOBAL DEFENSE INITIATIVE +#define TXT_NOD_NAME 661 // BROTHERHOOD OF NOD +#define TXT_SEL_TRANS 662 // SELECT TRANSMISSION +#define TXT_GAMENAME_MUSTBE_UNIQUE 663 // Your game name must be +#define TXT_GAME_IS_CLOSED 664 // Game is closed. +#define TXT_NAME_MUSTBE_UNIQUE 665 // Your name must be unique. +#define TXT_RECONNECTING_TO 666 // Reconnecting to %s +#define TXT_WAITING_FOR_CONNECTIONS 667 // Waiting for connections... +#define TXT_TIME_ALLOWED 668 // Time allowed: %02d seconds +#define TXT_PRESS_ESC 669 // Press ESC to cancel. +#define TXT_JUST_YOU_AND_ME 670 // From Computer: It's just +#define TXT_CAPTURE_THE_FLAG_COLON 671 // Capture the Flag: +#define TXT_CHAN 672 // Dr. Chan +#define TXT_HAS_ALLIED 673 // %s has allied with %s +#define TXT_AT_WAR 674 // %s declares war on %s +#define TXT_SEL_TARGET 675 // Select a target +#define TXT_SEPARATE_HELIPAD 676 // Allow separate helipad +#define TXT_RESIGN 677 // Resign Game +#define TXT_TIBERIUM_FAST 678 // Tiberium grows quickly. +#define TXT_ANSWERING 679 // Answering... +#define TXT_INITIALIZING_MODEM 680 // Initializing Modem... +#define TXT_SCENARIOS_DO_NOT_MATCH 681 // Scenarios don't match. +#define TXT_POWER_OUTPUT 682 // Power Output +#define TXT_POWER_OUTPUT_LOW 683 // Power Output (low) +#define TXT_CONTINUE 684 // Continue +#define TXT_QUEUE_FULL 685 // Data Queue Overflow +#define TXT_SPECIAL_WARNING 686 // %s changed game options! +#define TXT_CD_DIALOG_1 687 // Please insert a Command & +#define TXT_CD_DIALOG_2 688 // Please insert CD %d (%s) +#define TXT_CD_ERROR1 689 // Command & Conquer is unable +#define TXT_NO_SOUND_CARD 690 // No Sound Card Detected +#define TXT_UNKNOWN 691 // UNKNOWN +#define TXT_OLD_GAME 692 // (old) +#define TXT_NO_SPACE 693 // Insufficient Disk Space to +#define TXT_MUST_HAVE_SPACE 694 // You must have %d megabytes +#define TXT_RUN_SETUP 695 // Run SETUP program first. +#define TXT_WAITING_FOR_OPPONENT 696 // Waiting for Opponent +#define TXT_SELECT_SETTINGS 697 // Please select 'Settings' to +#define TXT_PRISON 698 // Prison +#define TXT_GAME_WAS_SAVED 699 // Game Saved +#define TXT_SPACE_CANT_SAVE 700 // Insufficient disk space to +#define TXT_INVALID_PORT_ADDRESS 701 // Invalid Port/Address. COM +#define TXT_INVALID_SETTINGS 702 // Invalid Port and/or IRQ +#define TXT_IRQ_ALREADY_IN_USE 703 // IRQ already in use +#define TXT_ABORT 704 // Abort +#define TXT_RESTART 705 // Restart +#define TXT_RESTARTING 706 // Mission is +#define TXT_LOADING 707 // Mission is loading. Please +#define TXT_ERROR_IN_INITSTRING 708 // Error in the InitString +#define TXT_ORDER_INFO 709 // Order Info +#define TXT_SCENES 710 // Scenes +#define TXT_NEW_MISSIONS 711 // New Missions +#define TXT_THEME_CHRG 712 // Depth Charge +#define TXT_THEME_DRON 713 // Drone +#define TXT_THEME_FIST 714 // Iron Fist +#define TXT_THEME_CREP 715 // Creeping Upon +#define TXT_THEME_80MX 716 // C&C 80's Mix +#define TXT_THEME_DRIL 717 // Drill +#define TXT_CD_DIALOG_3 718 // Please insert the Covert +#define TXT_THEME_RECON 719 // Recon +#define TXT_THEME_VOICE 720 // Voice Rhythm +#define TXT_ERROR_NO_INIT 721 // Error - modem did not +#define TXT_NO_FLOW_CONTROL_RESPONSE 722 // Error - Modem failed to +#define TXT_NO_COMPRESSION_RESPONSE 723 // Error - Modem failed to +#define TXT_NO_ERROR_CORRECTION_RESPONSE 724 // Error - Modem failed to +#define TXT_ERROR_NO_DISABLE 725 // Error - unable to disable +#define TXT_ERROR_TOO_MANY 726 // Error - Too many errors +#define TXT_IGNORE 727 // Ignore +#define TXT_CONNECTING 728 // Connecting... Please Wait. +#define TXT_EXPLAIN_REGISTRATION 729 // To play Command & Conquer +#define TXT_REGISTER 730 // Register +#define TXT_ERROR_UNABLE_TO_RUN_WCHAT 731 // Wchat not installed. Please +#define TXT_INTERNET 732 // Internet Game +#define TXT_UNABLE_TO_SET_VIDEO_MODE 733 // Error - Unable to set the +#define TXT_UNABLE_TO_ALLOCATE_PRIMARY_VIDEO_BUFFER 734 // Error - Unable to allocate +#define TXT_NO_DIAL_TONE 735 // No dial tone. Ensure your +#define TXT_MODEM_INITIALISATION 736 // Modem Initialization +#define TXT_DATA_COMPRESSION 737 // Data Compression +#define TXT_ERROR_CORRECTION 738 // Error Correction +#define TXT_HARDWARE_FLOW_CONTROL 739 // Hardware Flow Control +#define TXT_ADVANCED 740 // Advanced +#define TXT_JUST_INTRO 741 // Intro +#define TXT_READING_IMAGE_DATA 742 // READING IMAGE DATA +#define TXT_ANALYZING 743 // ANALYZING +#define TXT_ENHANCING_IMAGE_DATA 744 // ENHANCING IMAGE DATA +#define TXT_ISOLATING_OPERATIONAL_THEATER 745 // ISOLATING OPERATIONAL +#define TXT_ESTABLISHING_TRADITIONAL_BOUNDARIES 746 // ESTABLISHING TRADITIONAL +#define TXT_FOR_VISUAL_REFERENCE 747 // FOR VISUAL REFERENCE +#define TXT_ENHANCING_IMAGE 748 // ENHANCING IMAGE +#define TXT_BONUS_MISSIONS 749 // Bonus Missions +#define TXT_BONUS_MISSION_1 750 // Bonus Mission 1 +#define TXT_BONUS_MISSION_2 751 // Bonus Mission 2 +#define TXT_BONUS_MISSION_3 752 // Bonus Mission 3 +#define TXT_BONUS_MISSION_4 753 // Bonus Mission 4 +#define TXT_BONUS_MISSION_5 754 // Bonus Mission 5 diff --git a/CONQUER.ICO b/CONQUER.ICO new file mode 100644 index 0000000..41cc42c Binary files /dev/null and b/CONQUER.ICO differ diff --git a/CONQUER.IDE b/CONQUER.IDE new file mode 100644 index 0000000..66bf576 Binary files /dev/null and b/CONQUER.IDE differ diff --git a/CONQUER.LNT b/CONQUER.LNT new file mode 100644 index 0000000..7bb2410 --- /dev/null +++ b/CONQUER.LNT @@ -0,0 +1,79 @@ +// Watcom C, C++ (32 bit), -si4 -sp4, +// Standard lint options + +// Compiler Options for Watcom C, C++ 32 bit + +-cwc + +// This file contains options to allow PC-lint to process source +// files for your compiler. It is used as follows: +// +// lint co-wc32.lnt source-file(s) +// +-d_M_IX86=200 // assume Intel 80286 architecture -- modify to suit +-d__declspec()= // ignore this construct + + // additional reserve words needed ++rw(_loadds,_export) ++rw(__interrupt,__near,__far,__huge,__fortran,__pascal,__cdecl) ++rw(__export,__loadds,__saveregs,__asm,__fastcall,__stdcall) ++rw(_export) + ++fcd // makes cdecl significant -- used for proto generation ++fcu // chars are by default unsigned ++fsu // so are strings +-d__386__ // pre-defined macro for 386 version, not set by -cwc +-d__FLAT__ // not set by -cwc +-si4 // sizeof(int) is 4 +-spN4 // sizeof(near pointer) is 4 +-spF6 // sizeof( far pointer) is 6 +-sld10 // sizeof(long double) is 10. +-function(exit,_exit) // _exit() is like exit() +-emacro(734,putc) // don't complain about items being too large. +-emacro(506,putc) // don't complain about constant Boolean +-emacro(???,va_arg) // the va_arg() macro can yield 415, 416, 661, 662 + // 796 and 797 (out-of-bounds errors). + + // While processing compiler (library) header files ... +-elib(46) // an unsigned short bit field is used as __FILLER__ +-elib(522) // function return value ignored +-elib(537) // repeated include file (ios.h) +-elib(641) // converting enum to int +-elib(652) // suppress message about #define of earlier declared symbols +-elib(655) // ORing enum's +-elib(726) // extraneous comma in enumeration +-elib(760) // suppress message about multiple identical macro defs +-elib(762) // suppress message about multiple identical declarations and +-elib(806) // small bit field is signed +-elib(1053) // prototypes cannot be distinguished +-elib(1511) // member (rdbuf) hides nonvirtual member +-elib(1704) // private copy constructor +-elib(1712) // default constructor missing +-elib(1717) // empty prototypes +-elib(1720) // strange argument to assignment operator +-elib(1721) // unusual operator =() declaration +-elib(1722) // assignment operator does not return ref to class +-elib(1724) // strange argument to copy constructor + +-esym(1702,operator<<,operator>>) + +// The following functions exhibit variable return modes. +// That is, they may equally-usefully be called for a value +// as called just for their effects. Accordingly we inhibit +// Warning 534 for these functions. +// Feel free to add to or subtract from this list. + +-esym(534,close,creat,fclose,fflush,fprintf,fputc) +-esym(534,fputs,fscanf,fseek,fwrite,lseek,memcpy,memmove,memset) +-esym(534,printf,puts,scanf,sprintf,sscanf,strcat,strcpy) +-esym(534,strncat,strncpy,unlink,write) + +//------------------------------------------------------------------ + +-width(0,0) // don't break up message lines + +// 32 bit integer and pointer size is four bytes. +-si4 -sp4 + +// Include directories +-ic:\projects\c&czero\code\watcom\h;..\vq\include;..\gcl510\h diff --git a/CONQUER.PRO b/CONQUER.PRO new file mode 100644 index 0000000..5c4b2e9 --- /dev/null +++ b/CONQUER.PRO @@ -0,0 +1,864 @@ + + Page 1 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 12/13/1993 +Phase: 1 Time: 22:19:33 +Total Ticks: 2696 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +_Main_Program 1 | 2618 2618 97.11 | 1020 1020 37.83 | +_Edit_Unit_Offsets | | | + 1 | 1021 1021 37.87 | 941 941 34.90 | +display.c_NONPUB_Refresh_M | | | +ap 1090 | 206 0 7.64 | 200 0 7.42 | +_Self_Regulate 41213 | 122 0 4.53 | 122 0 4.53 | +scenario.c_NONPUB_Do_Brief | | | +ing 1 | 91 91 3.38 | 91 91 3.38 | +_Fancy_Text_Print 938 | 80 0 2.97 | 80 0 2.97 | +_main 1 | 2694 2694 99.93 | 76 76 2.82 | +_Init_Game 1 | 241 241 8.94 | 54 54 2.00 | +_Check_Menu 40124 | 47 0 1.74 | 47 0 1.74 | +_Load_Map 1 | 16 16 0.59 | 16 16 0.59 | +map.c_NONPUB_Smooth_Shadow | | | + 21775 | 11 0 0.41 | 10 0 0.37 | +_Load_A_Icon_Set 3 | 9 3 0.33 | 9 3 0.33 | +_Call_Back 41213 | 126 0 4.67 | 4 0 0.15 | +_Coord_Cell 77858 | 4 0 0.15 | 4 0 0.15 | +_Draw_Map 1090 | 209 0 7.75 | 3 0 0.11 | +_Anim_Update 2179 | 2 0 0.07 | 2 0 0.07 | +_Game_Screen 1 | 3 3 0.11 | 2 2 0.07 | +_Load_System_Strings | | | + 2 | 2 1 0.07 | 2 1 0.07 | +_Map_Cell 4355 | 13 0 0.48 | 2 0 0.07 | +_Shape_Ptr 1685 | 2 0 0.07 | 2 0 0.07 | +_Load_Brains 3 | 1 0 0.04 | 1 0 0.04 | +_Process_Logic 1089 | 5 0 0.19 | 1 0 0.04 | +_Unit_From_ID 2603 | 1 0 0.04 | 1 0 0.04 | +_Unit_Next 8519 | 1 0 0.04 | 1 0 0.04 | +_Unit_Revealed 12676 | 1 0 0.04 | 1 0 0.04 | +__cleanup 1 | 1 1 0.04 | 1 1 0.04 | +scenario.c_NONPUB_Clear_Sc | | | +enario 1 | 1 1 0.04 | 1 1 0.04 | +DGROUP@ 0 | 0 0 0.00 | 0 0 0.00 | +_Adjacent_Cell 0 | 0 0 0.00 | 0 0 0.00 | +_Adjacent_Free_Cell | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Assign_Destination | | | + 22 | 0 0 0.00 | 0 0 0.00 | +_Assign_Order 47 | 0 0 0.00 | 0 0 0.00 | +_Assign_Target 0 | 0 0 0.00 | 0 0 0.00 | +_Attached_Building | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Attached_Unit 0 | 0 0 0.00 | 0 0 0.00 | +_Base_Under_Attack | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Bldg_Chunk_In 0 | 0 0 0.00 | 0 0 0.00 | +_Bldg_Chunk_Out 0 | 0 0 0.00 | 0 0 0.00 | +_Bound_Cursor 59 | 0 0 0.00 | 0 0 0.00 | +_Break_Contact_With | | | + 106 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 2 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 12/13/1993 +Phase: 1 Time: 22:19:33 +Total Ticks: 2696 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +_Break_Radio_Contact | | | + 53 | 0 0 0.00 | 0 0 0.00 | +_Bttn_Building 0 | 0 0 0.00 | 0 0 0.00 | +_Bttn_Cancel 0 | 0 0 0.00 | 0 0 0.00 | +_Bttn_Custom_Render | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Bttn_Doit_Render 0 | 0 0 0.00 | 0 0 0.00 | +_Bttn_Enter 0 | 0 0 0.00 | 0 0 0.00 | +_Bttn_Enter_Render | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Bttn_Mode 0 | 0 0 0.00 | 0 0 0.00 | +_Bttn_OrderSelect 0 | 0 0 0.00 | 0 0 0.00 | +_Bttn_Repair 0 | 0 0 0.00 | 0 0 0.00 | +_Bttn_Return 0 | 0 0 0.00 | 0 0 0.00 | +_Buildables 0 | 0 0 0.00 | 0 0 0.00 | +_Building_Alloc 0 | 0 0 0.00 | 0 0 0.00 | +_Building_Create 0 | 0 0 0.00 | 0 0 0.00 | +_Building_Damage 0 | 0 0 0.00 | 0 0 0.00 | +_Building_Delete 0 | 0 0 0.00 | 0 0 0.00 | +_Building_Destroy 0 | 0 0 0.00 | 0 0 0.00 | +_Building_First 6 | 0 0 0.00 | 0 0 0.00 | +_Building_Free 0 | 0 0 0.00 | 0 0 0.00 | +_Building_From_ID 0 | 0 0 0.00 | 0 0 0.00 | +_Building_Logic 0 | 0 0 0.00 | 0 0 0.00 | +_Building_Look 0 | 0 0 0.00 | 0 0 0.00 | +_Building_Next 6 | 0 0 0.00 | 0 0 0.00 | +_Building_Render 0 | 0 0 0.00 | 0 0 0.00 | +_Building_Scan 3 | 0 0 0.00 | 0 0 0.00 | +_Building_Spec 0 | 0 0 0.00 | 0 0 0.00 | +_Building_System_Calibrate | | | + 1 | 0 0 0.00 | 0 0 0.00 | +_Building_System_Shutdown | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Building_System_Startup | | | + 2 | 0 0 0.00 | 0 0 0.00 | +_Building_Threat 0 | 0 0 0.00 | 0 0 0.00 | +_Building_Unlimbo 0 | 0 0 0.00 | 0 0 0.00 | +_Building_Untarget | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Calculated_Cell 0 | 0 0 0.00 | 0 0 0.00 | +_Can_Unit_Enter_Building | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Can_Unit_Enter_Cell | | | + 49 | 0 0 0.00 | 0 0 0.00 | +_Can_Upgrade 0 | 0 0 0.00 | 0 0 0.00 | +_Cardinal_To_Fixed | | | + 20 | 0 0 0.00 | 0 0 0.00 | +_CellXY_Coord 463 | 0 0 0.00 | 0 0 0.00 | +_Cell_Building 4700 | 0 0 0.00 | 0 0 0.00 | +_Cell_Coord 199 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 3 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 12/13/1993 +Phase: 1 Time: 22:19:33 +Total Ticks: 2696 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +_Cell_Distance 28 | 0 0 0.00 | 0 0 0.00 | +_Cell_Facing 16 | 0 0 0.00 | 0 0 0.00 | +_Cell_Object 15 | 0 0 0.00 | 0 0 0.00 | +_Cell_Unit 5201 | 0 0 0.00 | 0 0 0.00 | +_Cell_X 0 | 0 0 0.00 | 0 0 0.00 | +_Cell_Y 0 | 0 0 0.00 | 0 0 0.00 | +_Center_Map 0 | 0 0 0.00 | 0 0 0.00 | +_Change_State 30 | 3 0 0.11 | 0 0 0.00 | +_Class_From_Name 2 | 0 0 0.00 | 0 0 0.00 | +_Common_Attached 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Available_Building | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Class_Count | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Dialog 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Display_Message | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Distance 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Is_Clear 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Is_Enemy 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Is_Friendly | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Mono_Print | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Nearest_Spice | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Pause 181 | 0 0 0.00 | 0 0 0.00 | +_Common_Pause_Random | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Play_Sound | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Random 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Scenario 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Target_Distance | | | + 283 | 0 0 0.00 | 0 0 0.00 | +_Common_Target_Facing | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Target_Kind | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Target_Type | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Tutor_Message | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Coord_Add 208 | 0 0 0.00 | 0 0 0.00 | +_Coord_Cell_Distance | | | + 454 | 0 0 0.00 | 0 0 0.00 | +_Coord_Distance 1429 | 0 0 0.00 | 0 0 0.00 | +_Coord_Facing16 5 | 0 0 0.00 | 0 0 0.00 | +_Coord_Facing256 1373 | 0 0 0.00 | 0 0 0.00 | +_Coord_Facing8 0 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 4 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 12/13/1993 +Phase: 1 Time: 22:19:33 +Total Ticks: 2696 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +_Coord_Mid 0 | 0 0 0.00 | 0 0 0.00 | +_Coord_Move 472 | 0 0 0.00 | 0 0 0.00 | +_Coord_Scatter 0 | 0 0 0.00 | 0 0 0.00 | +_Coord_Snap 712 | 0 0 0.00 | 0 0 0.00 | +_Coord_Spillage_Number | | | + 1100 | 0 0 0.00 | 0 0 0.00 | +_Coord_Sub 454 | 0 0 0.00 | 0 0 0.00 | +_Coord_XCell 0 | 0 0 0.00 | 0 0 0.00 | +_Coord_XPixel 0 | 0 0 0.00 | 0 0 0.00 | +_Coord_YCell 0 | 0 0 0.00 | 0 0 0.00 | +_Coord_YPixel 0 | 0 0 0.00 | 0 0 0.00 | +_Coordinates_In_Region | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Cursor 1175 | 0 0 0.00 | 0 0 0.00 | +_Cursor_Move 57 | 0 0 0.00 | 0 0 0.00 | +_Cursor_Size 0 | 0 0 0.00 | 0 0 0.00 | +_Debug_Callback 0 | 0 0 0.00 | 0 0 0.00 | +_Debug_Key 96 | 1021 11 37.87 | 0 0 0.00 | +_Desired_Facing256 | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Destroy_Wall 0 | 0 0 0.00 | 0 0 0.00 | +_Dialog_Message 0 | 0 0 0.00 | 0 0 0.00 | +_Display_Selected_Terrain | | | + 9 | 0 0 0.00 | 0 0 0.00 | +_Display_Status 1089 | 0 0 0.00 | 0 0 0.00 | +_Do_Button_Setup 0 | 0 0 0.00 | 0 0 0.00 | +_Do_Explosion 20 | 0 0 0.00 | 0 0 0.00 | +_Do_Gas 0 | 0 0 0.00 | 0 0 0.00 | +_Do_Lose 0 | 0 0 0.00 | 0 0 0.00 | +_Do_Win 0 | 0 0 0.00 | 0 0 0.00 | +_Draw_Box 0 | 0 0 0.00 | 0 0 0.00 | +_Draw_Radar 1 | 0 0 0.00 | 0 0 0.00 | +_End_Game 1089 | 0 0 0.00 | 0 0 0.00 | +_Establish_Contact_With | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Establish_Origin 20 | 0 0 0.00 | 0 0 0.00 | +_Establish_Radio_Contact | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Fetch_Text_String | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Fill_In_Data 1 | 13 13 0.48 | 0 0 0.00 | +_Find_Brain_Size 0 | 0 0 0.00 | 0 0 0.00 | +_Find_Path 5 | 0 0 0.00 | 0 0 0.00 | +_Find_Waypoint 0 | 0 0 0.00 | 0 0 0.00 | +_Fire_Bullet 0 | 0 0 0.00 | 0 0 0.00 | +_Fire_Weapon 107 | 1 0 0.04 | 0 0 0.00 | +_Fixed_To_Cardinal | | | + 48 | 0 0 0.00 | 0 0 0.00 | +_Format_Window_String | | | + 0 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 5 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 12/13/1993 +Phase: 1 Time: 22:19:33 +Total Ticks: 2696 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +_Free_Brains 0 | 0 0 0.00 | 0 0 0.00 | +_GetPrivateProfileInt | | | + 16 | 0 0 0.00 | 0 0 0.00 | +_GetPrivateProfileString | | | + 41 | 0 0 0.00 | 0 0 0.00 | +_Get_Connection_Info | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Get_Connection_Num | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Get_Internet_Address | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Get_Local_Connection_Numb | | | +er 0 | 0 0 0.00 | 0 0 0.00 | +_Greatest_Building_Threat | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Greatest_Threat 8 | 0 0 0.00 | 0 0 0.00 | +_Greatest_Unit_Noise | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Greatest_Unit_Threat | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Harvester_Check 0 | 0 0 0.00 | 0 0 0.00 | +_House_From_Name 10 | 0 0 0.00 | 0 0 0.00 | +_IPX_Cancel_Event 0 | 0 0 0.00 | 0 0 0.00 | +_IPX_Close_Socket 0 | 0 0 0.00 | 0 0 0.00 | +_IPX_Get_Local_Target | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_IPX_Listen_For_Packet | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_IPX_Open_Socket 0 | 0 0 0.00 | 0 0 0.00 | +_IPX_Relinquish_Control | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_IPX_Schedule_IPX_Event | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_IPX_Send_Packet 0 | 0 0 0.00 | 0 0 0.00 | +_Icon_Install 0 | 0 0 0.00 | 0 0 0.00 | +_Icon_Remove 0 | 0 0 0.00 | 0 0 0.00 | +_Icon_Update 1090 | 0 0 0.00 | 0 0 0.00 | +_In_Radar 75 | 0 0 0.00 | 0 0 0.00 | +_In_Radio_Contact 0 | 0 0 0.00 | 0 0 0.00 | +_In_View 3 | 0 0 0.00 | 0 0 0.00 | +_Info_Chunk_In 0 | 0 0 0.00 | 0 0 0.00 | +_Info_Chunk_Out 0 | 0 0 0.00 | 0 0 0.00 | +_Init_Shapes 1 | 0 0 0.00 | 0 0 0.00 | +_Init_Thread 109 | 0 0 0.00 | 0 0 0.00 | +_Interrupt_Thread 0 | 0 0 0.00 | 0 0 0.00 | +_Is_Allied 1224 | 0 0 0.00 | 0 0 0.00 | +_Is_Lined_Up 668 | 0 0 0.00 | 0 0 0.00 | +_Is_Mapped 3 | 0 0 0.00 | 0 0 0.00 | +_Language_Name 3 | 0 0 0.00 | 0 0 0.00 | +_Launch_GoldenBB 0 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 6 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 12/13/1993 +Phase: 1 Time: 22:19:33 +Total Ticks: 2696 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +_Launch_Special 0 | 0 0 0.00 | 0 0 0.00 | +_Legal_Placement 0 | 0 0 0.00 | 0 0 0.00 | +_Load_Samples 2 | 0 0 0.00 | 0 0 0.00 | +_MO_From_Name 0 | 0 0 0.00 | 0 0 0.00 | +_Make_Button 11 | 0 0 0.00 | 0 0 0.00 | +_Map_Chunk_In 0 | 0 0 0.00 | 0 0 0.00 | +_Map_Chunk_Out 0 | 0 0 0.00 | 0 0 0.00 | +_Map_Edit 0 | 0 0 0.00 | 0 0 0.00 | +_Mouse_Coord 0 | 0 0 0.00 | 0 0 0.00 | +_Nearest_Spice 0 | 0 0 0.00 | 0 0 0.00 | +_Novell_Detect 0 | 0 0 0.00 | 0 0 0.00 | +_Order_From_Name 2 | 0 0 0.00 | 0 0 0.00 | +_Phrase_CallBack 0 | 0 0 0.00 | 0 0 0.00 | +_Pixel_Coordinate 2348 | 0 0 0.00 | 0 0 0.00 | +_Play_Theme 0 | 0 0 0.00 | 0 0 0.00 | +_Player_Alloc 3 | 0 0 0.00 | 0 0 0.00 | +_Player_First 3 | 0 0 0.00 | 0 0 0.00 | +_Player_Free 0 | 0 0 0.00 | 0 0 0.00 | +_Player_From_ID 2428 | 0 0 0.00 | 0 0 0.00 | +_Player_Logic 0 | 0 0 0.00 | 0 0 0.00 | +_Player_Next 12 | 0 0 0.00 | 0 0 0.00 | +_Player_System_Shutdown | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Player_System_Startup | | | + 2 | 0 0 0.00 | 0 0 0.00 | +_Plot_Radar_Pixel 0 | 0 0 0.00 | 0 0 0.00 | +_Plyr_Chunk_In 0 | 0 0 0.00 | 0 0 0.00 | +_Plyr_Chunk_Out 0 | 0 0 0.00 | 0 0 0.00 | +_Power_Effects 0 | 0 0 0.00 | 0 0 0.00 | +_Production_Needs 0 | 0 0 0.00 | 0 0 0.00 | +_Production_Start 0 | 0 0 0.00 | 0 0 0.00 | +_Radar_Checkup 0 | 0 0 0.00 | 0 0 0.00 | +_Radar_Cursor 1090 | 0 0 0.00 | 0 0 0.00 | +_Radar_Pixel 4965 | 0 0 0.00 | 0 0 0.00 | +_Radio_Contact 97 | 0 0 0.00 | 0 0 0.00 | +_Read_Scenario 1 | 34 34 1.26 | 0 0 0.00 | +_Read_Scenario_Ini | | | + 1 | 20 20 0.74 | 0 0 0.00 | +_Read_Scenario_Raw | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Recalc_Storage 3 | 0 0 0.00 | 0 0 0.00 | +_Redistribute_Power | | | + 3 | 0 0 0.00 | 0 0 0.00 | +_Redraw_Objects 0 | 0 0 0.00 | 0 0 0.00 | +_Refresh_Cell 5007 | 0 0 0.00 | 0 0 0.00 | +_Repair_On 0 | 0 0 0.00 | 0 0 0.00 | +_Request_Transport | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Run_Thread 9080 | 0 0 0.00 | 0 0 0.00 | +_Sample_Effect 0 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 7 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 12/13/1993 +Phase: 1 Time: 22:19:33 +Total Ticks: 2696 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +_Say_Phrase 2 | 0 0 0.00 | 0 0 0.00 | +_Scroll_Tactical 48 | 0 0 0.00 | 0 0 0.00 | +_Select_House 0 | 0 0 0.00 | 0 0 0.00 | +_Select_Next 1 | 0 0 0.00 | 0 0 0.00 | +_Send_Message 0 | 0 0 0.00 | 0 0 0.00 | +_Set_Unit_Movement | | | + 42 | 0 0 0.00 | 0 0 0.00 | +_Set_Unit_Rotation | | | + 1187 | 0 0 0.00 | 0 0 0.00 | +_Setup_Menu 1 | 0 0 0.00 | 0 0 0.00 | +_Sight_From 14 | 1 0 0.04 | 0 0 0.00 | +_Sound_Effect 31 | 0 0 0.00 | 0 0 0.00 | +_Special_Discovery | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Speed_Adjust 35 | 0 0 0.00 | 0 0 0.00 | +_Speed_From_Name 0 | 0 0 0.00 | 0 0 0.00 | +_Spew_Spice 0 | 0 0 0.00 | 0 0 0.00 | +_Spice_Adjust 0 | 0 0 0.00 | 0 0 0.00 | +_Spice_Discovery 0 | 0 0 0.00 | 0 0 0.00 | +_Start_Scenario 1 | 125 125 4.64 | 0 0 0.00 | +_Start_Thread 38 | 0 0 0.00 | 0 0 0.00 | +_Struct_From_Name 1 | 0 0 0.00 | 0 0 0.00 | +_System_Error 0 | 0 0 0.00 | 0 0 0.00 | +_Target_Build 277 | 0 0 0.00 | 0 0 0.00 | +_Target_Building 22 | 0 0 0.00 | 0 0 0.00 | +_Target_Cell 27 | 0 0 0.00 | 0 0 0.00 | +_Target_Coord 1222 | 1 0 0.04 | 0 0 0.00 | +_Target_Distance 331 | 0 0 0.00 | 0 0 0.00 | +_Target_Kind 2070 | 0 0 0.00 | 0 0 0.00 | +_Target_Legal 437 | 0 0 0.00 | 0 0 0.00 | +_Target_Object 53 | 0 0 0.00 | 0 0 0.00 | +_Target_Unit 91 | 0 0 0.00 | 0 0 0.00 | +_Target_Value 22 | 0 0 0.00 | 0 0 0.00 | +_Team_Alloc 0 | 0 0 0.00 | 0 0 0.00 | +_Team_Chunk_In 0 | 0 0 0.00 | 0 0 0.00 | +_Team_Chunk_Out 0 | 0 0 0.00 | 0 0 0.00 | +_Team_Create 0 | 0 0 0.00 | 0 0 0.00 | +_Team_First 31 | 0 0 0.00 | 0 0 0.00 | +_Team_Free 0 | 0 0 0.00 | 0 0 0.00 | +_Team_From_ID 0 | 0 0 0.00 | 0 0 0.00 | +_Team_Logic 0 | 0 0 0.00 | 0 0 0.00 | +_Team_Next 31 | 0 0 0.00 | 0 0 0.00 | +_Team_System_Calibrate | | | + 1 | 0 0 0.00 | 0 0 0.00 | +_Team_System_Shutdown | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Team_System_Startup | | | + 2 | 0 0 0.00 | 0 0 0.00 | +_Terrain_Cost 32 | 0 0 0.00 | 0 0 0.00 | +_Terrain_Type 65 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 8 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 12/13/1993 +Phase: 1 Time: 22:19:33 +Total Ticks: 2696 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +_Text_Input 0 | 0 0 0.00 | 0 0 0.00 | +_Text_String 70 | 0 0 0.00 | 0 0 0.00 | +_Track_Func 1089 | 0 0 0.00 | 0 0 0.00 | +_Tutor_Message 2 | 0 0 0.00 | 0 0 0.00 | +_Unit_Alloc 22 | 0 0 0.00 | 0 0 0.00 | +_Unit_Available 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_Brainwash 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_Check 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_Chunk_In 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_Chunk_Out 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_Create 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_Damage 38 | 0 0 0.00 | 0 0 0.00 | +_Unit_Delete 20 | 0 0 0.00 | 0 0 0.00 | +_Unit_Enters_Building | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_First 3316 | 0 0 0.00 | 0 0 0.00 | +_Unit_Free 20 | 0 0 0.00 | 0 0 0.00 | +_Unit_Hidden 31 | 0 0 0.00 | 0 0 0.00 | +_Unit_House 1379 | 0 0 0.00 | 0 0 0.00 | +_Unit_Joins_Team 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_Leaves_Team 32 | 0 0 0.00 | 0 0 0.00 | +_Unit_Limbo 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_Loaner 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_Look 29 | 1 0 0.04 | 0 0 0.00 | +_Unit_Mark 1144 | 0 0 0.00 | 0 0 0.00 | +_Unit_Physics 2295 | 0 0 0.00 | 0 0 0.00 | +_Unit_Rally 18 | 0 0 0.00 | 0 0 0.00 | +_Unit_Select 29 | 0 0 0.00 | 0 0 0.00 | +_Unit_Sort 1090 | 0 0 0.00 | 0 0 0.00 | +_Unit_Stun 1 | 0 0 0.00 | 0 0 0.00 | +_Unit_System_Calibrate | | | + 1 | 0 0 0.00 | 0 0 0.00 | +_Unit_System_Shutdown | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_System_Startup | | | + 2 | 0 0 0.00 | 0 0 0.00 | +_Unit_Threat 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_UnTarget 31 | 0 0 0.00 | 0 0 0.00 | +_Unit_Unlimbo 11 | 0 0 0.00 | 0 0 0.00 | +_Units_Team 8 | 0 0 0.00 | 0 0 0.00 | +_Upgrade_On 0 | 0 0 0.00 | 0 0 0.00 | +_Valid_Thread 1918 | 0 0 0.00 | 0 0 0.00 | +_Wall_Flags 0 | 0 0 0.00 | 0 0 0.00 | +_Window_Box 2 | 0 0 0.00 | 0 0 0.00 | +_WritePrivateProfileInt | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_WritePrivateProfileString | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Write_Scenario 0 | 0 0 0.00 | 0 0 0.00 | +_Write_Scenario_Ini | | | + 0 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 9 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 12/13/1993 +Phase: 1 Time: 22:19:33 +Total Ticks: 2696 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +_Write_Scenario_Raw | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_XYPixel_Coord 20 | 0 0 0.00 | 0 0 0.00 | +_XY_Cell 0 | 0 0 0.00 | 0 0 0.00 | +__MMODEL 0 | 0 0 0.00 | 0 0 0.00 | +__checknull 1 | 0 0 0.00 | 0 0 0.00 | +__restorezero 1 | 0 0 0.00 | 0 0 0.00 | +__terminate 1 | 0 0 0.00 | 0 0 0.00 | +_abort 0 | 0 0 0.00 | 0 0 0.00 | +_strtrim 33 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Anim_Remove | | | + 20 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Animation_In | | | +stall 20 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Cmd_Crash | | | + 0 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Cmd_Crater | | | + 0 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Cmd_Illegal | | | + 0 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Cmd_Normal | | | + 640 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Cmd_RTimer | | | + 0 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Cmd_Repeat | | | + 0 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Cmd_Reverse | | | + 1280 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Cmd_Shake | | | + 0 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Cmd_ShiftX | | | + 0 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Cmd_ShiftY | | | + 0 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Cmd_Sound | | | + 0 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Cmd_Stop | | | + 640 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Cmd_Timer | | | + 1920 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Cmd_Trigger_ | | | +Bloom 0 | 0 0 0.00 | 0 0 0.00 | +audio.c_NONPUB_Dune_Load_S | | | +ample 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Abort_ | | | +Transport 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Busy_S | | | +tate 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Change | | | +_Facing_One 0 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 10 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 12/13/1993 +Phase: 1 Time: 22:19:33 +Total Ticks: 2696 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +blogic.c_NONPUB_Cmd_Check_ | | | +Up 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Direct | | | +ion 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Dummy | | | + 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Fire_A | | | +t 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Image | | | + 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Launch | | | +_Unit 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Look | | | + 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Neares | | | +t_Enemy 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Play_S | | | +ound 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Reques | | | +t_Transport 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Self_E | | | +xplode 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Termin | | | +ate_Self 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Unload | | | +_Harvester 0 | 0 0 0.00 | 0 0 0.00 | +conquer.c_NONPUB_Keyboard_ | | | +Process 97 | 1021 11 37.87 | 0 0 0.00 | +conquer.c_NONPUB_MT32_Init | | | + 1 | 0 0 0.00 | 0 0 0.00 | +cursor.c_NONPUB_Cursor_Mar | | | +k 324 | 0 0 0.00 | 0 0 0.00 | +findpath.c_NONPUB_Follow_E | | | +dge 0 | 0 0 0.00 | 0 0 0.00 | +findpath.c_NONPUB_Optimize | | | +_Moves 5 | 0 0 0.00 | 0 0 0.00 | +icon.c_NONPUB_Cmd_IconSet | | | + 0 | 0 0 0.00 | 0 0 0.00 | +icon.c_NONPUB_Cmd_Illegal | | | + 0 | 0 0 0.00 | 0 0 0.00 | +icon.c_NONPUB_Cmd_Loop | | | + 0 | 0 0 0.00 | 0 0 0.00 | +icon.c_NONPUB_Cmd_Map | | | + 0 | 0 0 0.00 | 0 0 0.00 | +icon.c_NONPUB_Cmd_Normal | | | + 0 | 0 0 0.00 | 0 0 0.00 | +icon.c_NONPUB_Cmd_Repeat | | | + 0 | 0 0 0.00 | 0 0 0.00 | +icon.c_NONPUB_Cmd_Sound | | | + 0 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 11 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 12/13/1993 +Phase: 1 Time: 22:19:33 +Total Ticks: 2696 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +icon.c_NONPUB_Cmd_Stop | | | + 0 | 0 0 0.00 | 0 0 0.00 | +icon.c_NONPUB_Cmd_Term | | | + 0 | 0 0 0.00 | 0 0 0.00 | +icon.c_NONPUB_Cmd_Timer | | | + 0 | 0 0 0.00 | 0 0 0.00 | +init.c_NONPUB_Init_Globals | | | + 1 | 0 0 0.00 | 0 0 0.00 | +init.c_NONPUB_Load_A_Brain | | | + 3 | 1 0 0.04 | 0 0 0.00 | +loadgame.c_NONPUB_BYTE_In | | | + 0 | 0 0 0.00 | 0 0 0.00 | +loadgame.c_NONPUB_LONG_In | | | + 0 | 0 0 0.00 | 0 0 0.00 | +loadgame.c_NONPUB_Struct_I | | | +n 0 | 0 0 0.00 | 0 0 0.00 | +loadgame.c_NONPUB_Thread_G | | | +ameize 0 | 0 0 0.00 | 0 0 0.00 | +loadgame.c_NONPUB_WORD_In | | | + 0 | 0 0 0.00 | 0 0 0.00 | +physics.c_NONPUB_End_Of_Mo | | | +ve 14 | 0 0 0.00 | 0 0 0.00 | +physics.c_NONPUB_Momentum_ | | | +Physics 938 | 0 0 0.00 | 0 0 0.00 | +physics.c_NONPUB_Rotation_ | | | +Physics 237 | 0 0 0.00 | 0 0 0.00 | +physics.c_NONPUB_Start_Of_ | | | +Move 64 | 0 0 0.00 | 0 0 0.00 | +physics.c_NONPUB_While_Mov | | | +ing 930 | 0 0 0.00 | 0 0 0.00 | +plogic.c_NONPUB_Cmd_Dummy | | | + 0 | 0 0 0.00 | 0 0 0.00 | +plogic.c_NONPUB_Cmd_Harves | | | +ter_Check 0 | 0 0 0.00 | 0 0 0.00 | +plogic.c_NONPUB_Cmd_Sound | | | + 0 | 0 0 0.00 | 0 0 0.00 | +plogic.c_NONPUB_Cmd_Unit_C | | | +reate 0 | 0 0 0.00 | 0 0 0.00 | +savegame.c_NONPUB_BYTE_Out | | | + 0 | 0 0 0.00 | 0 0 0.00 | +savegame.c_NONPUB_LONG_Out | | | + 0 | 0 0 0.00 | 0 0 0.00 | +savegame.c_NONPUB_Struct_O | | | +ut 0 | 0 0 0.00 | 0 0 0.00 | +savegame.c_NONPUB_Thread_N | | | +ormalize 0 | 0 0 0.00 | 0 0 0.00 | +savegame.c_NONPUB_WORD_Out | | | + 0 | 0 0 0.00 | 0 0 0.00 | +scenario.c_NONPUB_Typical_ | | | +Briefing 0 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 12 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 12/13/1993 +Phase: 1 Time: 22:19:33 +Total Ticks: 2696 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +strinput.c_NONPUB_Input_Cu | | | +rsor 0 | 0 0 0.00 | 0 0 0.00 | +tlogic.c_NONPUB_Cmd_Attack | | | +_Team_Target 0 | 0 0 0.00 | 0 0 0.00 | +tlogic.c_NONPUB_Cmd_Change | | | +_MO 0 | 0 0 0.00 | 0 0 0.00 | +tlogic.c_NONPUB_Cmd_Displa | | | +y_Message 0 | 0 0 0.00 | 0 0 0.00 | +tlogic.c_NONPUB_Cmd_Dummy | | | + 0 | 0 0 0.00 | 0 0 0.00 | +tlogic.c_NONPUB_Cmd_Find_T | | | +eam_Target 0 | 0 0 0.00 | 0 0 0.00 | +tlogic.c_NONPUB_Cmd_Recrui | | | +t 0 | 0 0 0.00 | 0 0 0.00 | +tlogic.c_NONPUB_Cmd_Regrou | | | +p 0 | 0 0 0.00 | 0 0 0.00 | +tlogic.c_NONPUB_Cmd_Restor | | | +e_MO 0 | 0 0 0.00 | 0 0 0.00 | +tlogic.c_NONPUB_Cmd_Team_C | | | +ount 0 | 0 0 0.00 | 0 0 0.00 | +tlogic.c_NONPUB_Cmd_Team_M | | | +in 0 | 0 0 0.00 | 0 0 0.00 | +tlogic.c_NONPUB_Cmd_Team_S | | | +pread 0 | 0 0 0.00 | 0 0 0.00 | +tlogic.c_NONPUB_Cmd_Team_T | | | +arget 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Abort_ | | | +Transport 14 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_All_St | | | +op 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Altern | | | +ate_Target 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Animat | | | +e 3 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Call_F | | | +or_Landing 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Cargo | | | + 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Change | | | +_Facing 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Change | | | +_Order 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Death_ | | | +Message 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Deploy | | | + 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Dock | | | + 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Dummy | | | + 0 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 13 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 12/13/1993 +Phase: 1 Time: 22:19:33 +Total Ticks: 2696 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +ulogic.c_NONPUB_Cmd_Eject | | | + 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Explos | | | +ion 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Face_M | | | +ove_Toward 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Face_T | | | +arCom 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Flash | | | + 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Greate | | | +st_Threat 8 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Hot_LZ | | | + 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Idle | | | + 6 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Inq_Un | | | +it 862 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Is_Clo | | | +se_To_NavCom 181 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Is_Clo | | | +se_To_TarCom 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Load | | | + 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Loaded | | | + 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Lock_O | | | +n_To_Target 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Look | | | + 27 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Mine_S | | | +pice 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_MultiE | | | +xplosion 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Noisie | | | +st_Target 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Reques | | | +t_Transport 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Rest | | | + 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Set_Hu | | | +lk 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Set_Na | | | +vCom 8 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Set_Sp | | | +eed 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Set_Ta | | | +rCom 16 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Spew_B | | | +odies 0 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 14 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 12/13/1993 +Phase: 1 Time: 22:19:33 +Total Ticks: 2696 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +ulogic.c_NONPUB_Cmd_Target | | | +_Dir 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Termin | | | +ate_Self 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Threat | | | +_Value 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Travel | | | +_To 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Unload | | | + 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Unload | | | +ed_Struct 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Verify | | | +_Contact 0 | 0 0 0.00 | 0 0 0.00 | + +Total Logical Percentage : 362.39 +Total Physical Percentage : 99.93 diff --git a/CONQUER.REP b/CONQUER.REP new file mode 100644 index 0000000..9361013 --- /dev/null +++ b/CONQUER.REP @@ -0,0 +1,880 @@ + + Page 1 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 01/20/1994 +Phase: 1 Time: 17:04:46 +Total Ticks: 2081 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +display.c_NONPUB_Refresh_M | | | +ap 790 | 630 1 30.27 | 620 1 29.79 | +_Call_Back 450547 | 626 0 30.08 | 541 0 26.00 | +_Main_Program 0 | 2071 0 99.52 | 521 0 25.04 | +_Self_Regulate 790 | 156 0 7.50 | 156 0 7.50 | +_Sound_Callback 450547 | 85 0 4.08 | 85 0 4.08 | +_Draw_Map 790 | 667 1 32.05 | 37 0 1.78 | +_Process_Logic 790 | 100 0 4.81 | 21 0 1.01 | +_Icon_Update 790 | 18 0 0.86 | 18 0 0.86 | +_Refresh_Cell 5464 | 9 0 0.43 | 9 0 0.43 | +_Anim_Update 790 | 14 0 0.67 | 7 0 0.34 | +_Unit_Mark 1319 | 15 0 0.72 | 7 0 0.34 | +physics.c_NONPUB_Momentum_ | | | +Physics 570 | 26 0 1.25 | 7 0 0.34 | +_Unit_Physics 1636 | 36 0 1.73 | 5 0 0.24 | +_Coord_Cell 55104 | 4 0 0.19 | 4 0 0.19 | +_Draw_Vehicle 493 | 4 0 0.19 | 4 0 0.19 | +physics.c_NONPUB_While_Mov | | | +ing 553 | 4 0 0.19 | 4 0 0.19 | +anim.c_NONPUB_Cmd_Normal | | | + 256 | 3 0 0.14 | 3 0 0.14 | +$$VMGETPAGE 1 | 2 2 0.10 | 2 2 0.10 | +_Map_Cell 168 | 2 0 0.10 | 2 0 0.10 | +_Unit_First 2054 | 2 0 0.10 | 2 0 0.10 | +_main 1 | 2073 2073 99.62 | 2 2 0.10 | +anim.c_NONPUB_Anim_Remove | | | + 8 | 2 0 0.10 | 2 0 0.10 | +_Can_Unit_Enter_Cell | | | + 540 | 1 0 0.05 | 1 0 0.05 | +_Coord_Distance 1376 | 1 0 0.05 | 1 0 0.05 | +_Coord_Facing256 822 | 1 0 0.05 | 1 0 0.05 | +_Coord_Spillage_Number | | | + 1291 | 1 0 0.05 | 1 0 0.05 | +_Greatest_Threat 8 | 1 0 0.05 | 1 0 0.05 | +_Radio_Contact 45 | 1 0 0.05 | 1 0 0.05 | +_Run_Thread 2430 | 4 0 0.19 | 1 0 0.05 | +_Shape_Ptr 689 | 1 0 0.05 | 1 0 0.05 | +_Target_Coord 773 | 1 0 0.05 | 1 0 0.05 | +_Target_Legal 298 | 1 0 0.05 | 1 0 0.05 | +_Unit_Damage 13 | 1 0 0.05 | 1 0 0.05 | +_Unit_From_ID 1424 | 1 0 0.05 | 1 0 0.05 | +__cleanup 1 | 1 1 0.05 | 1 1 0.05 | +anim.c_NONPUB_Cmd_Reverse | | | + 512 | 2 0 0.10 | 1 0 0.05 | +DGROUP@ 0 | 0 0 0.00 | 0 0 0.00 | +_Adjacent_Cell 0 | 0 0 0.00 | 0 0 0.00 | +_Adjacent_Free_Cell | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Assign_Destination | | | + 5 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 2 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 01/20/1994 +Phase: 1 Time: 17:04:46 +Total Ticks: 2081 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +_Assign_Order 15 | 0 0 0.00 | 0 0 0.00 | +_Assign_Target 2 | 0 0 0.00 | 0 0 0.00 | +_Attached_Building | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Attached_Unit 0 | 0 0 0.00 | 0 0 0.00 | +_Base_Under_Attack | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Bldg_Chunk_In 0 | 0 0 0.00 | 0 0 0.00 | +_Bldg_Chunk_Out 0 | 0 0 0.00 | 0 0 0.00 | +_Bound_Cursor 0 | 0 0 0.00 | 0 0 0.00 | +_Break_Contact_With | | | + 40 | 0 0 0.00 | 0 0 0.00 | +_Break_Radio_Contact | | | + 20 | 0 0 0.00 | 0 0 0.00 | +_Bttn_Building 0 | 0 0 0.00 | 0 0 0.00 | +_Bttn_Cancel 0 | 0 0 0.00 | 0 0 0.00 | +_Bttn_Custom_Render | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Bttn_Doit_Render 0 | 0 0 0.00 | 0 0 0.00 | +_Bttn_Enter 0 | 0 0 0.00 | 0 0 0.00 | +_Bttn_Enter_Render | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Bttn_Mode 0 | 0 0 0.00 | 0 0 0.00 | +_Bttn_OrderSelect 0 | 0 0 0.00 | 0 0 0.00 | +_Bttn_Repair 0 | 0 0 0.00 | 0 0 0.00 | +_Bttn_Return 0 | 0 0 0.00 | 0 0 0.00 | +_Buildables 0 | 0 0 0.00 | 0 0 0.00 | +_Building_Alloc 0 | 0 0 0.00 | 0 0 0.00 | +_Building_Create 0 | 0 0 0.00 | 0 0 0.00 | +_Building_Damage 0 | 0 0 0.00 | 0 0 0.00 | +_Building_Delete 0 | 0 0 0.00 | 0 0 0.00 | +_Building_Destroy 0 | 0 0 0.00 | 0 0 0.00 | +_Building_First 0 | 0 0 0.00 | 0 0 0.00 | +_Building_Free 0 | 0 0 0.00 | 0 0 0.00 | +_Building_From_ID 369 | 0 0 0.00 | 0 0 0.00 | +_Building_Logic 0 | 0 0 0.00 | 0 0 0.00 | +_Building_Look 0 | 0 0 0.00 | 0 0 0.00 | +_Building_Next 0 | 0 0 0.00 | 0 0 0.00 | +_Building_Render 0 | 0 0 0.00 | 0 0 0.00 | +_Building_Scan 0 | 0 0 0.00 | 0 0 0.00 | +_Building_Spec 0 | 0 0 0.00 | 0 0 0.00 | +_Building_System_Calibrate | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Building_System_Shutdown | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Building_System_Startup | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Building_Threat 0 | 0 0 0.00 | 0 0 0.00 | +_Building_Unlimbo 0 | 0 0 0.00 | 0 0 0.00 | +_Building_Untarget | | | + 0 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 3 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 01/20/1994 +Phase: 1 Time: 17:04:46 +Total Ticks: 2081 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +_Calculated_Cell 0 | 0 0 0.00 | 0 0 0.00 | +_Can_Unit_Enter_Building | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Can_Upgrade 0 | 0 0 0.00 | 0 0 0.00 | +_Cardinal_To_Fixed | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_CellXY_Coord 281 | 0 0 0.00 | 0 0 0.00 | +_Cell_Building 28 | 0 0 0.00 | 0 0 0.00 | +_Cell_Coord 46 | 0 0 0.00 | 0 0 0.00 | +_Cell_Distance 3 | 0 0 0.00 | 0 0 0.00 | +_Cell_Facing 28 | 0 0 0.00 | 0 0 0.00 | +_Cell_Object 0 | 0 0 0.00 | 0 0 0.00 | +_Cell_Unit 1104 | 0 0 0.00 | 0 0 0.00 | +_Cell_X 0 | 0 0 0.00 | 0 0 0.00 | +_Cell_Y 0 | 0 0 0.00 | 0 0 0.00 | +_Center_Map 0 | 0 0 0.00 | 0 0 0.00 | +_Change_State 10 | 0 0 0.00 | 0 0 0.00 | +_Check_Menu 0 | 0 0 0.00 | 0 0 0.00 | +_Class_From_Name 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Attached 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Available_Building | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Class_Count | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Dialog 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Display_Message | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Distance 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Is_Clear 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Is_Enemy 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Is_Friendly | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Mono_Print | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Nearest_Spice | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Pause 128 | 0 0 0.00 | 0 0 0.00 | +_Common_Pause_Random | | | + 6 | 0 0 0.00 | 0 0 0.00 | +_Common_Play_Sound | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Random 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Scenario 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Target_Distance | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Target_Facing | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Target_Kind | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Common_Target_Type | | | + 0 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 4 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 01/20/1994 +Phase: 1 Time: 17:04:46 +Total Ticks: 2081 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +_Common_Tutor_Message | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Coord_Add 18 | 0 0 0.00 | 0 0 0.00 | +_Coord_Cell_Distance | | | + 274 | 0 0 0.00 | 0 0 0.00 | +_Coord_Facing16 0 | 0 0 0.00 | 0 0 0.00 | +_Coord_Facing8 0 | 0 0 0.00 | 0 0 0.00 | +_Coord_Mid 0 | 0 0 0.00 | 0 0 0.00 | +_Coord_Move 570 | 0 0 0.00 | 0 0 0.00 | +_Coord_Scatter 0 | 0 0 0.00 | 0 0 0.00 | +_Coord_Snap 0 | 0 0 0.00 | 0 0 0.00 | +_Coord_Sub 730 | 0 0 0.00 | 0 0 0.00 | +_Coord_XCell 0 | 0 0 0.00 | 0 0 0.00 | +_Coord_XPixel 0 | 0 0 0.00 | 0 0 0.00 | +_Coord_YCell 0 | 0 0 0.00 | 0 0 0.00 | +_Coord_YPixel 0 | 0 0 0.00 | 0 0 0.00 | +_Coordinates_In_Region | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Cursor 239 | 0 0 0.00 | 0 0 0.00 | +_Cursor_Move 0 | 0 0 0.00 | 0 0 0.00 | +_Cursor_Size 0 | 0 0 0.00 | 0 0 0.00 | +_Debug_Key 40 | 0 0 0.00 | 0 0 0.00 | +_Desired_Facing256 | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Destroy_Wall 0 | 0 0 0.00 | 0 0 0.00 | +_Dialog_Message 0 | 0 0 0.00 | 0 0 0.00 | +_Display_Selected_Terrain | | | + 10 | 0 0 0.00 | 0 0 0.00 | +_Display_Status 790 | 0 0 0.00 | 0 0 0.00 | +_Do_Button_Setup 0 | 0 0 0.00 | 0 0 0.00 | +_Do_Explosion 8 | 3 0 0.14 | 0 0 0.00 | +_Do_Gas 0 | 0 0 0.00 | 0 0 0.00 | +_Do_Lose 0 | 0 0 0.00 | 0 0 0.00 | +_Do_Win 0 | 0 0 0.00 | 0 0 0.00 | +_Draw_Box 0 | 0 0 0.00 | 0 0 0.00 | +_Edit_Unit_Offsets | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_End_Game 790 | 0 0 0.00 | 0 0 0.00 | +_Establish_Contact_With | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Establish_Origin 9 | 0 0 0.00 | 0 0 0.00 | +_Establish_Radio_Contact | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Fancy_Text_Print 0 | 0 0 0.00 | 0 0 0.00 | +_Fetch_Text_String | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_File_Stream_Sample | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Fill_In_Data 0 | 0 0 0.00 | 0 0 0.00 | +_Find_Brain_Size 0 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 5 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 01/20/1994 +Phase: 1 Time: 17:04:46 +Total Ticks: 2081 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +_Find_Path 5 | 1 0 0.05 | 0 0 0.00 | +_Find_Waypoint 0 | 0 0 0.00 | 0 0 0.00 | +_Fire_Bullet 0 | 0 0 0.00 | 0 0 0.00 | +_Fire_Weapon 267 | 3 0 0.14 | 0 0 0.00 | +_Fixed_To_Cardinal | | | + 20 | 0 0 0.00 | 0 0 0.00 | +_Format_Window_String | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Free_Brains 0 | 0 0 0.00 | 0 0 0.00 | +_Free_Sample 0 | 0 0 0.00 | 0 0 0.00 | +_Game_Screen 0 | 0 0 0.00 | 0 0 0.00 | +_GetPrivateProfileInt | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_GetPrivateProfileString | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Get_Connection_Info | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Get_Connection_Num | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Get_Internet_Address | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Get_Local_Connection_Numb | | | +er 0 | 0 0 0.00 | 0 0 0.00 | +_Greatest_Building_Threat | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Greatest_Unit_Noise | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Greatest_Unit_Threat | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Harvester_Check 0 | 0 0 0.00 | 0 0 0.00 | +_House_From_Name 0 | 0 0 0.00 | 0 0 0.00 | +_IPX_Cancel_Event 0 | 0 0 0.00 | 0 0 0.00 | +_IPX_Close_Socket 0 | 0 0 0.00 | 0 0 0.00 | +_IPX_Get_Local_Target | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_IPX_Listen_For_Packet | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_IPX_Open_Socket 0 | 0 0 0.00 | 0 0 0.00 | +_IPX_Relinquish_Control | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_IPX_Schedule_IPX_Event | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_IPX_Send_Packet 0 | 0 0 0.00 | 0 0 0.00 | +_Icon_Install 0 | 0 0 0.00 | 0 0 0.00 | +_Icon_Remove 0 | 0 0 0.00 | 0 0 0.00 | +_In_Radar 547 | 0 0 0.00 | 0 0 0.00 | +_In_Radio_Contact 0 | 0 0 0.00 | 0 0 0.00 | +_In_View 0 | 0 0 0.00 | 0 0 0.00 | +_Info_Chunk_In 0 | 0 0 0.00 | 0 0 0.00 | +_Info_Chunk_Out 0 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 6 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 01/20/1994 +Phase: 1 Time: 17:04:46 +Total Ticks: 2081 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +_Init_Game 0 | 0 0 0.00 | 0 0 0.00 | +_Init_Shapes 0 | 0 0 0.00 | 0 0 0.00 | +_Init_Thread 46 | 0 0 0.00 | 0 0 0.00 | +_Interrupt_Thread 0 | 0 0 0.00 | 0 0 0.00 | +_Is_Allied 860 | 0 0 0.00 | 0 0 0.00 | +_Is_Lined_Up 273 | 0 0 0.00 | 0 0 0.00 | +_Is_Mapped 11 | 0 0 0.00 | 0 0 0.00 | +_Language_Name 0 | 0 0 0.00 | 0 0 0.00 | +_Launch_GoldenBB 0 | 0 0 0.00 | 0 0 0.00 | +_Launch_Special 0 | 0 0 0.00 | 0 0 0.00 | +_Legal_Placement 0 | 0 0 0.00 | 0 0 0.00 | +_Load_Brains 0 | 0 0 0.00 | 0 0 0.00 | +_Load_Map 0 | 0 0 0.00 | 0 0 0.00 | +_Load_Sample 9 | 0 0 0.00 | 0 0 0.00 | +_Load_Sample_Into_Buffer | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Load_Samples 0 | 0 0 0.00 | 0 0 0.00 | +_Load_System_Strings | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Load_Terrain_Icons | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_MO_From_Name 0 | 0 0 0.00 | 0 0 0.00 | +_Make_Button 0 | 0 0 0.00 | 0 0 0.00 | +_Map_Chunk_In 0 | 0 0 0.00 | 0 0 0.00 | +_Map_Chunk_Out 0 | 0 0 0.00 | 0 0 0.00 | +_Map_Edit 0 | 0 0 0.00 | 0 0 0.00 | +_Mouse_Coord 0 | 0 0 0.00 | 0 0 0.00 | +_Nearest_Spice 0 | 0 0 0.00 | 0 0 0.00 | +_Novell_Detect 0 | 0 0 0.00 | 0 0 0.00 | +_Order_From_Name 0 | 0 0 0.00 | 0 0 0.00 | +_Phrase_CallBack 0 | 0 0 0.00 | 0 0 0.00 | +_Pixel_Coordinate 1389 | 0 0 0.00 | 0 0 0.00 | +_Play_Sample 9 | 0 0 0.00 | 0 0 0.00 | +_Play_Sample_Vol 9 | 0 0 0.00 | 0 0 0.00 | +_Play_Theme 0 | 0 0 0.00 | 0 0 0.00 | +_Player_Alloc 0 | 0 0 0.00 | 0 0 0.00 | +_Player_First 0 | 0 0 0.00 | 0 0 0.00 | +_Player_Free 0 | 0 0 0.00 | 0 0 0.00 | +_Player_From_ID 1663 | 0 0 0.00 | 0 0 0.00 | +_Player_Logic 0 | 0 0 0.00 | 0 0 0.00 | +_Player_Next 0 | 0 0 0.00 | 0 0 0.00 | +_Player_System_Shutdown | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Player_System_Startup | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Plyr_Chunk_In 0 | 0 0 0.00 | 0 0 0.00 | +_Plyr_Chunk_Out 0 | 0 0 0.00 | 0 0 0.00 | +_Power_Effects 0 | 0 0 0.00 | 0 0 0.00 | +_Production_Needs 0 | 0 0 0.00 | 0 0 0.00 | +_Production_Start 0 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 7 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 01/20/1994 +Phase: 1 Time: 17:04:46 +Total Ticks: 2081 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +_Radar_Checkup 0 | 0 0 0.00 | 0 0 0.00 | +_Radar_Cursor 790 | 0 0 0.00 | 0 0 0.00 | +_Radar_Pixel 1053 | 0 0 0.00 | 0 0 0.00 | +_Read_Scenario 0 | 0 0 0.00 | 0 0 0.00 | +_Read_Scenario_Ini | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Read_Scenario_Raw | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Recalc_Storage 0 | 0 0 0.00 | 0 0 0.00 | +_Redistribute_Power | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Redraw_Objects 0 | 0 0 0.00 | 0 0 0.00 | +_Repair_On 0 | 0 0 0.00 | 0 0 0.00 | +_Request_Transport | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Sample_Effect 0 | 0 0 0.00 | 0 0 0.00 | +_Sample_Read 0 | 0 0 0.00 | 0 0 0.00 | +_Sample_Status 0 | 0 0 0.00 | 0 0 0.00 | +_Say_Phrase 0 | 0 0 0.00 | 0 0 0.00 | +_Scroll_Tactical 128 | 0 0 0.00 | 0 0 0.00 | +_Select_House 0 | 0 0 0.00 | 0 0 0.00 | +_Select_Next 0 | 0 0 0.00 | 0 0 0.00 | +_Send_Message 0 | 0 0 0.00 | 0 0 0.00 | +_Set_Unit_Movement | | | + 18 | 0 0 0.00 | 0 0 0.00 | +_Set_Unit_Rotation | | | + 532 | 0 0 0.00 | 0 0 0.00 | +_Setup_Menu 0 | 0 0 0.00 | 0 0 0.00 | +_Sight_From 7 | 2 0 0.10 | 0 0 0.00 | +_Sound_Effect 7 | 0 0 0.00 | 0 0 0.00 | +_Sound_End 1 | 0 0 0.00 | 0 0 0.00 | +_Sound_Init 0 | 0 0 0.00 | 0 0 0.00 | +_Special_Discovery | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Speed_Adjust 16 | 0 0 0.00 | 0 0 0.00 | +_Speed_From_Name 0 | 0 0 0.00 | 0 0 0.00 | +_Spew_Spice 0 | 0 0 0.00 | 0 0 0.00 | +_Spice_Adjust 0 | 0 0 0.00 | 0 0 0.00 | +_Spice_Discovery 0 | 0 0 0.00 | 0 0 0.00 | +_Start_Scenario 0 | 0 0 0.00 | 0 0 0.00 | +_Start_Thread 14 | 0 0 0.00 | 0 0 0.00 | +_Stop_Sample 0 | 0 0 0.00 | 0 0 0.00 | +_Stream_Sample 0 | 0 0 0.00 | 0 0 0.00 | +_Struct_From_Name 0 | 0 0 0.00 | 0 0 0.00 | +_Target_Build 92 | 0 0 0.00 | 0 0 0.00 | +_Target_Building 5 | 0 0 0.00 | 0 0 0.00 | +_Target_Cell 25 | 0 0 0.00 | 0 0 0.00 | +_Target_Distance 151 | 1 0 0.05 | 0 0 0.00 | +_Target_Kind 1265 | 0 0 0.00 | 0 0 0.00 | +_Target_Object 20 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 8 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 01/20/1994 +Phase: 1 Time: 17:04:46 +Total Ticks: 2081 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +_Target_Unit 24 | 0 0 0.00 | 0 0 0.00 | +_Target_Value 299 | 0 0 0.00 | 0 0 0.00 | +_Team_Alloc 0 | 0 0 0.00 | 0 0 0.00 | +_Team_Chunk_In 0 | 0 0 0.00 | 0 0 0.00 | +_Team_Chunk_Out 0 | 0 0 0.00 | 0 0 0.00 | +_Team_Create 0 | 0 0 0.00 | 0 0 0.00 | +_Team_First 15 | 0 0 0.00 | 0 0 0.00 | +_Team_Free 0 | 0 0 0.00 | 0 0 0.00 | +_Team_From_ID 0 | 0 0 0.00 | 0 0 0.00 | +_Team_Logic 0 | 0 0 0.00 | 0 0 0.00 | +_Team_Next 15 | 0 0 0.00 | 0 0 0.00 | +_Team_System_Calibrate | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Team_System_Shutdown | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Team_System_Startup | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Terrain_Cost 516 | 1 0 0.05 | 0 0 0.00 | +_Terrain_Type 356 | 0 0 0.00 | 0 0 0.00 | +_Text_Input 0 | 0 0 0.00 | 0 0 0.00 | +_Text_String 51 | 0 0 0.00 | 0 0 0.00 | +_Track_Func 790 | 0 0 0.00 | 0 0 0.00 | +_Tutor_Message 3 | 0 0 0.00 | 0 0 0.00 | +_Unit_Alloc 9 | 0 0 0.00 | 0 0 0.00 | +_Unit_Available 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_Brainwash 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_Check 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_Chunk_In 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_Chunk_Out 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_Create 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_Delete 8 | 0 0 0.00 | 0 0 0.00 | +_Unit_Enters_Building | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_Free 8 | 0 0 0.00 | 0 0 0.00 | +_Unit_Hidden 15 | 0 0 0.00 | 0 0 0.00 | +_Unit_House 91 | 0 0 0.00 | 0 0 0.00 | +_Unit_Joins_Team 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_Leaves_Team 15 | 0 0 0.00 | 0 0 0.00 | +_Unit_Limbo 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_Loaner 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_Look 7 | 2 0 0.10 | 0 0 0.00 | +_Unit_Next 5013 | 0 0 0.00 | 0 0 0.00 | +_Unit_Rally 5 | 0 0 0.00 | 0 0 0.00 | +_Unit_Revealed 168 | 0 0 0.00 | 0 0 0.00 | +_Unit_Select 10 | 0 0 0.00 | 0 0 0.00 | +_Unit_Sort 790 | 0 0 0.00 | 0 0 0.00 | +_Unit_Stun 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_System_Calibrate | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_System_Shutdown | | | + 0 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 9 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 01/20/1994 +Phase: 1 Time: 17:04:46 +Total Ticks: 2081 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +_Unit_System_Startup | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_Threat 0 | 0 0 0.00 | 0 0 0.00 | +_Unit_UnTarget 15 | 1 0 0.05 | 0 0 0.00 | +_Unit_Unlimbo 7 | 0 0 0.00 | 0 0 0.00 | +_Units_Team 2 | 0 0 0.00 | 0 0 0.00 | +_Upgrade_On 0 | 0 0 0.00 | 0 0 0.00 | +_Valid_Thread 882 | 0 0 0.00 | 0 0 0.00 | +_Vehicle_Edit 0 | 0 0 0.00 | 0 0 0.00 | +_Window_Box 0 | 0 0 0.00 | 0 0 0.00 | +_WritePrivateProfileInt | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_WritePrivateProfileString | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Write_Scenario 0 | 0 0 0.00 | 0 0 0.00 | +_Write_Scenario_Ini | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_Write_Scenario_Raw | | | + 0 | 0 0 0.00 | 0 0 0.00 | +_XYPixel_Coord 9 | 0 0 0.00 | 0 0 0.00 | +_XY_Cell 0 | 0 0 0.00 | 0 0 0.00 | +__MMODEL 0 | 0 0 0.00 | 0 0 0.00 | +__checknull 1 | 0 0 0.00 | 0 0 0.00 | +__restorezero 1 | 0 0 0.00 | 0 0 0.00 | +__terminate 1 | 0 0 0.00 | 0 0 0.00 | +_abort 0 | 0 0 0.00 | 0 0 0.00 | +_strtrim 0 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Animation_In | | | +stall 8 | 2 0 0.10 | 0 0 0.00 | +anim.c_NONPUB_Cmd_Crash | | | + 0 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Cmd_Crater | | | + 0 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Cmd_Illegal | | | + 0 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Cmd_RTimer | | | + 0 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Cmd_Repeat | | | + 0 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Cmd_Shake | | | + 0 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Cmd_ShiftX | | | + 0 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Cmd_ShiftY | | | + 0 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Cmd_Sound | | | + 0 | 0 0 0.00 | 0 0 0.00 | +anim.c_NONPUB_Cmd_Stop | | | + 256 | 2 0 0.10 | 0 0 0.00 | +anim.c_NONPUB_Cmd_Timer | | | + 768 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 10 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 01/20/1994 +Phase: 1 Time: 17:04:46 +Total Ticks: 2081 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +anim.c_NONPUB_Cmd_Trigger_ | | | +Bloom 0 | 0 0 0.00 | 0 0 0.00 | +audio.c_NONPUB_Dune_Load_S | | | +ample 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Abort_ | | | +Transport 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Busy_S | | | +tate 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Change | | | +_Facing_One 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Check_ | | | +Up 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Direct | | | +ion 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Dummy | | | + 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Fire_A | | | +t 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Image | | | + 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Launch | | | +_Unit 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Look | | | + 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Neares | | | +t_Enemy 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Play_S | | | +ound 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Reques | | | +t_Transport 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Self_E | | | +xplode 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Termin | | | +ate_Self 0 | 0 0 0.00 | 0 0 0.00 | +blogic.c_NONPUB_Cmd_Unload | | | +_Harvester 0 | 0 0 0.00 | 0 0 0.00 | +conquer.c_NONPUB_Keyboard_ | | | +Process 40 | 0 0 0.00 | 0 0 0.00 | +cursor.c_NONPUB_Cursor_Mar | | | +k 0 | 0 0 0.00 | 0 0 0.00 | +findpath.c_NONPUB_Follow_E | | | +dge 10 | 1 0 0.05 | 0 0 0.00 | +findpath.c_NONPUB_Optimize | | | +_Moves 14 | 0 0 0.00 | 0 0 0.00 | +icon.c_NONPUB_Cmd_IconSet | | | + 0 | 0 0 0.00 | 0 0 0.00 | +icon.c_NONPUB_Cmd_Illegal | | | + 0 | 0 0 0.00 | 0 0 0.00 | +icon.c_NONPUB_Cmd_Loop | | | + 0 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 11 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 01/20/1994 +Phase: 1 Time: 17:04:46 +Total Ticks: 2081 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +icon.c_NONPUB_Cmd_Map | | | + 130 | 0 0 0.00 | 0 0 0.00 | +icon.c_NONPUB_Cmd_Normal | | | + 0 | 0 0 0.00 | 0 0 0.00 | +icon.c_NONPUB_Cmd_Repeat | | | + 16 | 0 0 0.00 | 0 0 0.00 | +icon.c_NONPUB_Cmd_Sound | | | + 0 | 0 0 0.00 | 0 0 0.00 | +icon.c_NONPUB_Cmd_Stop | | | + 0 | 0 0 0.00 | 0 0 0.00 | +icon.c_NONPUB_Cmd_Term | | | + 1 | 0 0 0.00 | 0 0 0.00 | +icon.c_NONPUB_Cmd_Timer | | | + 129 | 0 0 0.00 | 0 0 0.00 | +init.c_NONPUB_Init_Globals | | | + 0 | 0 0 0.00 | 0 0 0.00 | +init.c_NONPUB_Load_A_Brain | | | + 0 | 0 0 0.00 | 0 0 0.00 | +loadgame.c_NONPUB_BYTE_In | | | + 0 | 0 0 0.00 | 0 0 0.00 | +loadgame.c_NONPUB_LONG_In | | | + 0 | 0 0 0.00 | 0 0 0.00 | +loadgame.c_NONPUB_Struct_I | | | +n 0 | 0 0 0.00 | 0 0 0.00 | +loadgame.c_NONPUB_Thread_G | | | +ameize 0 | 0 0 0.00 | 0 0 0.00 | +loadgame.c_NONPUB_WORD_In | | | + 0 | 0 0 0.00 | 0 0 0.00 | +map.c_NONPUB_Smooth_Shadow | | | + 840 | 0 0 0.00 | 0 0 0.00 | +physics.c_NONPUB_End_Of_Mo | | | +ve 17 | 0 0 0.00 | 0 0 0.00 | +physics.c_NONPUB_Rotation_ | | | +Physics 318 | 0 0 0.00 | 0 0 0.00 | +physics.c_NONPUB_Start_Of_ | | | +Move 78 | 1 0 0.05 | 0 0 0.00 | +plogic.c_NONPUB_Cmd_Dummy | | | + 0 | 0 0 0.00 | 0 0 0.00 | +plogic.c_NONPUB_Cmd_Harves | | | +ter_Check 0 | 0 0 0.00 | 0 0 0.00 | +plogic.c_NONPUB_Cmd_Sound | | | + 0 | 0 0 0.00 | 0 0 0.00 | +plogic.c_NONPUB_Cmd_Unit_C | | | +reate 0 | 0 0 0.00 | 0 0 0.00 | +savegame.c_NONPUB_BYTE_Out | | | + 0 | 0 0 0.00 | 0 0 0.00 | +savegame.c_NONPUB_LONG_Out | | | + 0 | 0 0 0.00 | 0 0 0.00 | +savegame.c_NONPUB_Struct_O | | | +ut 0 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 12 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 01/20/1994 +Phase: 1 Time: 17:04:46 +Total Ticks: 2081 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +savegame.c_NONPUB_Thread_N | | | +ormalize 0 | 0 0 0.00 | 0 0 0.00 | +savegame.c_NONPUB_WORD_Out | | | + 0 | 0 0 0.00 | 0 0 0.00 | +scenario.c_NONPUB_Clear_Sc | | | +enario 0 | 0 0 0.00 | 0 0 0.00 | +scenario.c_NONPUB_Do_Brief | | | +ing 0 | 0 0 0.00 | 0 0 0.00 | +scenario.c_NONPUB_Typical_ | | | +Briefing 0 | 0 0 0.00 | 0 0 0.00 | +soundio.c_NONPUB_DigiCallb | | | +ack 0 | 0 0 0.00 | 0 0 0.00 | +soundio.c_NONPUB_File_Call | | | +back 0 | 0 0 0.00 | 0 0 0.00 | +strinput.c_NONPUB_Input_Cu | | | +rsor 0 | 0 0 0.00 | 0 0 0.00 | +tlogic.c_NONPUB_Cmd_Attack | | | +_Team_Target 0 | 0 0 0.00 | 0 0 0.00 | +tlogic.c_NONPUB_Cmd_Change | | | +_MO 0 | 0 0 0.00 | 0 0 0.00 | +tlogic.c_NONPUB_Cmd_Displa | | | +y_Message 0 | 0 0 0.00 | 0 0 0.00 | +tlogic.c_NONPUB_Cmd_Dummy | | | + 0 | 0 0 0.00 | 0 0 0.00 | +tlogic.c_NONPUB_Cmd_Find_T | | | +eam_Target 0 | 0 0 0.00 | 0 0 0.00 | +tlogic.c_NONPUB_Cmd_Recrui | | | +t 0 | 0 0 0.00 | 0 0 0.00 | +tlogic.c_NONPUB_Cmd_Regrou | | | +p 0 | 0 0 0.00 | 0 0 0.00 | +tlogic.c_NONPUB_Cmd_Restor | | | +e_MO 0 | 0 0 0.00 | 0 0 0.00 | +tlogic.c_NONPUB_Cmd_Team_C | | | +ount 0 | 0 0 0.00 | 0 0 0.00 | +tlogic.c_NONPUB_Cmd_Team_M | | | +in 0 | 0 0 0.00 | 0 0 0.00 | +tlogic.c_NONPUB_Cmd_Team_S | | | +pread 0 | 0 0 0.00 | 0 0 0.00 | +tlogic.c_NONPUB_Cmd_Team_T | | | +arget 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Abort_ | | | +Transport 3 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_All_St | | | +op 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Altern | | | +ate_Target 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Animat | | | +e 8 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Call_F | | | +or_Landing 0 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 13 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 01/20/1994 +Phase: 1 Time: 17:04:46 +Total Ticks: 2081 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +ulogic.c_NONPUB_Cmd_Cargo | | | + 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Change | | | +_Facing 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Change | | | +_Order 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Death_ | | | +Message 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Deploy | | | + 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Dock | | | + 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Dummy | | | + 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Eject | | | + 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Explos | | | +ion 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Face_M | | | +ove_Toward 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Face_T | | | +arCom 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Flash | | | + 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Greate | | | +st_Threat 8 | 1 0 0.05 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Hot_LZ | | | + 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Idle | | | + 2 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Inq_Un | | | +it 127 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Is_Clo | | | +se_To_NavCom 21 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Is_Clo | | | +se_To_TarCom 96 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Load | | | + 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Loaded | | | + 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Lock_O | | | +n_To_Target 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Look | | | + 7 | 2 0 0.10 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Mine_S | | | +pice 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_MultiE | | | +xplosion 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Noisie | | | +st_Target 0 | 0 0 0.00 | 0 0 0.00 | +------------------------------------------------------------------------------- + + Page 14 + .RTProfiler Performance Report + +Program: CONQUER.EXE Date: 01/20/1994 +Phase: 1 Time: 17:04:46 +Total Ticks: 2081 + + ---------- Logical --------------- Physical -------- + | Total Average Pct | Total Average Pct | + Total | Clock No. of Total | Clock No. of Total| +Symbol Name Calls | Ticks Ticks Time | Ticks Ticks Time | +------------------------------------------------------------------------------- +ulogic.c_NONPUB_Cmd_Reques | | | +t_Transport 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Rest | | | + 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Set_Hu | | | +lk 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Set_Na | | | +vCom 87 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Set_Sp | | | +eed 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Set_Ta | | | +rCom 10 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Spew_B | | | +odies 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Target | | | +_Dir 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Termin | | | +ate_Self 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Threat | | | +_Value 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Travel | | | +_To 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Unload | | | + 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Unload | | | +ed_Struct 0 | 0 0 0.00 | 0 0 0.00 | +ulogic.c_NONPUB_Cmd_Verify | | | +_Contact 0 | 0 0 0.00 | 0 0 0.00 | + +Total Logical Percentage : 316.67 +Total Physical Percentage : 99.62 diff --git a/CONQUER.TXT b/CONQUER.TXT new file mode 100644 index 0000000..5a6567d --- /dev/null +++ b/CONQUER.TXT @@ -0,0 +1,1417 @@ +# TXT_NONE +%3d.%02d +# TXT_CREDIT_FORMAT +Upgrade +# TXT_BUTTON_UPGRADE +Upgrade Structure +# TXT_UPGRADE +Upgrade +# TXT_UPGRADE_BUTTON +Sell +# TXT_BUTTON_SELL +Sell Structure +# TXT_SELL +Demolish Structure +# TXT_DEMOLISH +Repair +# TXT_BUTTON_REPAIR +Repair Structure +# TXT_REPAIR +Repair +# TXT_REPAIR_BUTTON +You: +# TXT_YOU +Enemy: +# TXT_ENEMY +Buildings Destroyed By +# TXT_BUILD_DEST +Units Destroyed By +# TXT_UNIT_DEST +Tiberium Harvested By +# TXT_TIB_HARV +Score: %d +# TXT_SCORE_1 +You have attained the rank of +# TXT_RANK_OF +Yes +# TXT_YES +No +# TXT_NO +Ready +# TXT_READY +Holding +# TXT_HOLDING +Accomplished +# TXT_SCENARIO_WON +Failed +# TXT_SCENARIO_LOST +Choose Your Side +# TXT_CHOOSE_SIDE +Start New Game +# TXT_START_NEW_GAME +Replay Introduction +# TXT_INTRO +Cancel +# TXT_CANCEL +Rock +# TXT_ROCK +Resume Game +# TXT_CHOAM_RESUME +Build This +# TXT_CHOAM_BUILD_THIS +Thank you for playing Command & Conquer.\r +# TXT_THANK_YOU +Hall of Fame +# TXT_FAME +Global Defense Initiative +# TXT_GDI +Brotherhood of Nod +# TXT_NOD +Civilian +# TXT_CIVILIAN +Containment Team +# TXT_JP +OK +# TXT_OK +Tree +# TXT_TREE +\x011 +# TXT_LEFT +\x010 +# TXT_RIGHT +\x01E +# TXT_UP +\x01F +# TXT_DOWN +Clear the map +# TXT_CLEAR_MAP +Inherit previous map +# TXT_INHERIT_MAP +Clear +# TXT_CLEAR +Water +# TXT_WATER +Road +# TXT_ROAD +Tile Object +# TXT_TILE +Slope +# TXT_SLOPE +Brush +# TXT_BRUSH +Patch +# TXT_PATCH +River +# TXT_RIVER +Load Mission +# TXT_LOAD_MISSION +Save Mission +# TXT_SAVE_MISSION +Delete Mission +# TXT_DELETE_MISSION +Load +# TXT_LOAD_BUTTON +Save +# TXT_SAVE_BUTTON +Delete +# TXT_DELETE_BUTTON +Game Controls +# TXT_GAME_CONTROLS +Sound Controls +# TXT_SOUND_CONTROLS +Resume Mission +# TXT_RESUME_MISSION +Visual Controls +# TXT_VISUAL_CONTROLS +Abort Mission +# TXT_QUIT_MISSION +Exit Game +# TXT_EXIT_GAME +Options +# TXT_OPTIONS +Tiberium +# TXT_TIBERIUM +Tiberium On +# TXT_TIBERIUM_ON +Tiberium Off +# TXT_TIBERIUM_OFF +Squish mark +# TXT_SQUISH +Crater +# TXT_CRATER +Scorch Mark +# TXT_SCORCH +BRIGHTNESS: +# TXT_BRIGHTNESS +MUSIC VOLUME +# TXT_MUSIC +SOUND VOLUME +# TXT_VOLUME +TINT: +# TXT_TINT +CONTRAST: +# TXT_CONTRAST +GAME SPEED: +# TXT_SPEED +SCROLL RATE: +# TXT_SCROLLRATE +COLOR: +# TXT_COLOR +Return to game +# TXT_RETURN_TO_GAME +Enemy Soldier +# TXT_ENEMY_SOLDIER +Enemy Vehicle +# TXT_ENEMY_VEHICLE +Enemy Structure +# TXT_ENEMY_STRUCTURE +Flame Tank +# TXT_FTANK +Stealth Tank +# TXT_STANK +Light Tank +# TXT_LTANK +Med. Tank +# TXT_MTANK +Mammoth Tank +# TXT_HTANK +Nod Buggy +# TXT_DUNE_BUGGY +SAM Site +# TXT_SAM +Advanced Com. Center +# TXT_EYE +Rocket Launcher +# TXT_MLRS +Mobile HQ +# TXT_MHQ +Hum-vee +# TXT_JEEP +Transport Helicopter +# TXT_TRANS +A10 +# TXT_A10 +C17 +# TXT_C17 +Harvester +# TXT_HARVESTER +Artillery +# TXT_ARTY +S.S.M. Launcher +# TXT_MSAM +Minigunner +# TXT_E1 +Grenadier +# TXT_E2 +Bazooka +# TXT_E3 +Flamethrower +# TXT_E4 +Chem-warrior +# TXT_E5 +Commando +#TXT_RAMBO +Hovercraft +# TXT_HOVER +Attack Helicopter +# TXT_HELI +Orca +# TXT_ORCA +APC +# TXT_APC +Guard Tower +# TXT_GUARD_TOWER +Communications Center +# TXT_COMMAND +Helicopter Pad +# TXT_HELIPAD +Airstrip +# TXT_AIRSTRIP +Tiberium Silo +# TXT_STORAGE +Construction Yard +# TXT_CONST_YARD +Tiberium Refinery +# TXT_REFINERY +Church +# TXT_CIV1 +Han's and Gretel's +# TXT_CIV2 +Hewitt's Manor +# TXT_CIV3 +Ricktor's House +# TXT_CIV4 +Gretchin's House +# TXT_CIV5 +The Barn +# TXT_CIV6 +Damon's pub +# TXT_CIV7 +Fran's House +# TXT_CIV8 +Music Factory +# TXT_CIV9 +Toymaker's +# TXT_CIV10 +Ludwig's House +# TXT_CIV11 +Haystacks +# TXT_CIV12 +Haystack +# TXT_CIV13 +Wheat Field +# TXT_CIV14 +Fallow Field +# TXT_CIV15 +Corn Field +# TXT_CIV16 +Celery Field +# TXT_CIV17 +Potato Field +# TXT_CIV18 +Sala's House +# TXT_CIV20 +Abdul's House +# TXT_CIV21 +Pablo's Wicked Pub +# TXT_CIV22 +Village Well +# TXT_CIV23 +Camel Trader +# TXT_CIV24 +Church +# TXT_CIV25 +Ali's House +# TXT_CIV26 +Trader Ted's +# TXT_CIV27 +Menelik's House +# TXT_CIV28 +Prestor John's House +# TXT_CIV29 +Village Well +# TXT_CIV30 +Witch Doctor's Hut +# TXT_CIV31 +Rikitikitembo's Hut +# TXT_CIV32 +Roarke's Hut +# TXT_CIV33 +Mubasa's Hut +# TXT_CIV34 +Aksum's Hut +# TXT_CIV35 +Mambo's Hut +# TXT_CIV36 +The Studio +# TXT_CIV37 +Technology Center +# TXT_CIVMISS +Gun Turret +# TXT_TURRET +Gun Boat +# TXT_GUNBOAT +Mobile Construction Yard +# TXT_MCV +Recon Bike +# TXT_BIKE +Power Plant +# TXT_POWER +Advanced Power Plant +# TXT_ADVANCED_POWER +Hospital +# TXT_HOSPITAL +Barracks +# TXT_BARRACKS +Concrete +# TXT_CONCRETE +Oil Pump +# TXT_PUMP +Oil Tanker +# TXT_TANKER +Sandbag Wall +# TXT_SANDBAG_WALL +Chain Link Fence +# TXT_CYCLONE_WALL +Concrete Wall +# TXT_BRICK_WALL +Barbwire Fence +# TXT_BARBWIRE_WALL +Wood Fence +# TXT_WOOD_WALL +Weapons Factory +# TXT_WEAPON_FACTORY +Advanced Guard Tower +# TXT_AGUARD_TOWER +Obelisk Guard Tower +# TXT_OBELISK +Bio-Research Laboritory +# TXT_BIO_LAB +Hand of Nod +# TXT_HAND +Temple of Nod +# TXT_TEMPLE +Repair Bay +# TXT_FIX_IT +Sidebar +# TXT_TAB_SIDEBAR +Options +# TXT_TAB_BUTTON_CONTROLS +Database +# TXT_TAB_BUTTON_DATABASE +Unrevealed Terrain +# TXT_SHADOW +Options Menu +# TXT_OPTIONS_MENU +STOP +# TXT_STOP +PLAY +# TXT_PLAY +SHUFFLE +# TXT_SHUFFLE +REPEAT +# TXT_REPEAT +Music volume: +# TXT_MUSIC_VOLUME +Sound volume: +# TXT_SOUND_VOLUME +On +# TXT_ON +Off +# TXT_OFF +Act On Instinct +# TXT_THEME_AOI +Looks Like Trouble +# TXT_THEME_TROUBLE +Industrial +# TXT_THEME_IND +Reaching Out +# TXT_THEME_ROUT +On The Prowl +# TXT_THEME_OTP +Prepare For Battle +# TXT_THEME_PRP +Just Do It! +# TXT_THEME_JUSTDOIT +In The Line Of Fire +# TXT_THEME_LINEFIRE +March To Doom +# TXT_THEME_MARCH +Deception +# TXT_THEME_STOPTHEM +C&C Thang +# TXT_THEME_CCTHANG +Enemies To Be Feared +# TXT_THEME_BEFEARED +Warfare +# TXT_THEME_WARFARE +Fight, Win, Prevail +# TXT_THEME_FWP +Die!! +# TXT_THEME_DIE +No Mercy +# TXT_THEME_NOMERCY +Mechanical Man +# TXT_THEME_TARGET +I Am +# TXT_THEME_IAM +Great Shot! +# TXT_THEME_WIN1 +Multiplayer Game +# TXT_MULTIPLAYER_GAME +No files available +# TXT_NO_FILES +Do you want to delete this file? +# TXT_DELETE_SINGLE_FILE +Do you want to delete %d files? +# TXT_DELETE_MULTIPLE_FILES +Reset Values +# TXT_RESET_MENU +Confirmation +# TXT_CONFIRMATION +Do you want to abort the mission? +# TXT_CONFIRM_EXIT +Mission Description +# TXT_MISSION_DESCRIPTION +Joe +# TXT_C1 +Bill +# TXT_C2 +Shelly +# TXT_C3 +Maria +# TXT_C4 +Eydie +# TXT_C5 +Dave +# TXT_C6 +Phil +# TXT_C7 +Dwight +# TXT_C8 +Erik +# TXT_C9 +Dr. Moebius +# TXT_MOEBIUS +Road Bib +# TXT_BIB +Faster +# TXT_FASTER +Slower +# TXT_SLOWER +Ion Cannon +# TXT_ION_CANNON +Nuclear Strike +# TXT_NUKE_STRIKE +Air Strike +# TXT_AIR_STRIKE +Tyrannosaurus Rex +# TXT_TREX +Triceratops +# TXT_TRIC +Velociraptor +# TXT_RAPT +Stegasaurus +# TXT_STEG +Steel Crate +# TXT_STEEL_CRATE +Wood Crate +# TXT_WOOD_CRATE +Flag Location +# TXT_FLAG_SPOT +GDI +# TXT_G_D_I +NOD +# TXT_N_O_D +Unable to read scenario! +# TXT_UNABLE_READ_SCENARIO +Error loading game! +# TXT_ERROR_LOADING_GAME +Obsolete saved game. +# TXT_OBSOLETE_SAVEGAME +You must enter a description! +# TXT_MUSTENTER_DESCRIPTION +Error saving game! +# TXT_ERROR_SAVING_GAME +Delete this file? +# TXT_DELETE_FILE_QUERY +[EMPTY SLOT] +# TXT_EMPTY_SLOT +Select Multiplayer Game +# TXT_SELECT_MPLAYER_GAME +Modem/Serial +# TXT_MODEM_SERIAL +Network +# TXT_NETWORK +Unable to initialize network! +# TXT_INIT_NET_ERROR +Join Network Game +# TXT_JOIN_NETWORK_GAME +New +# TXT_NEW +Join +# TXT_JOIN +Send Message +# TXT_SEND_MESSAGE +Your Name: +# TXT_YOUR_NAME +Side: +# TXT_SIDE_COLON +Color: +# TXT_COLOR_COLON +Games +# TXT_GAMES +Players +# TXT_PLAYERS +Scenario: +# TXT_SCENARIO_COLON +>> NOT FOUND << +# TXT_NOT_FOUND +Starting Credits: +# TXT_START_CREDITS_COLON +Bases: +# TXT_BASES_COLON +Tiberium: +# TXT_TIBERIUM_COLON +Crates: +# TXT_CRATES_COLON +AI Players: +# TXT_AI_PLAYERS_COLON +Request denied. +# TXT_REQUEST_DENIED +Unable to play; scenario not found. +# TXT_UNABLE_PLAY_WAAUGH +Nothing to join! +# TXT_NOTHING_TO_JOIN +You must enter a name! +# TXT_NAME_ERROR +Duplicate names are not allowed. +# TXT_DUPENAMES_NOTALLOWED +Your game version is outdated. +# TXT_YOURGAME_OUTDATED +Destination game version is outdated. +# TXT_DESTGAME_OUTDATED +%s's Game +# TXT_THATGUYS_GAME +[%s's Game] +# TXT_THATGUYS_GAME_BRACKET +Network Game Setup +# TXT_NETGAME_SETUP +Reject +# TXT_REJECT +You can't reject yourself! You might develop serious self-esteem problems. +# TXT_CANT_REJECT_SELF +You must select a player to reject. +# TXT_SELECT_PLAYER_REJECT +Bases On +# TXT_BASES_ON +Bases Off +# TXT_BASES_OFF +Crates On +# TXT_CRATES_ON +Crates Off +# TXT_CRATES_OFF +AI Players On +# TXT_AI_PLAYERS_ON +AI Players Off +# TXT_AI_PLAYERS_OFF +Scenarios +# TXT_SCENARIOS +Starting Credits +# TXT_START_CREDITS +Only one player? +# TXT_ONLY_ONE +Oops! +# TXT_OOPS +To %s: +# TXT_TO +To All: +# TXT_TO_ALL +Message: +# TXT_MESSAGE +Connection to %s lost! +# TXT_CONNECTION_LOST +%s has left the game. +# TXT_LEFT_GAME +%s has been defeated! +# TXT_PLAYER_DEFEATED +Waiting to Connect... +# TXT_WAITING_CONNECT +Connection error!\rCheck your cables.\rAttempting to Reconnect... +# TXT_NULL_CONNERR_CHECK_CABLES +Connection error!\rRedialing... +# TXT_MODEM_CONNERR_REDIALING +Connection error!\rWaiting for Call... +# TXT_MODEM_CONNERR_WAITING +Select Serial Game +# TXT_SELECT_SERIAL_GAME +Dial Modem +# TXT_DIAL_MODEM +Answer Modem +# TXT_ANSWER_MODEM +Null Modem +# TXT_NULL_MODEM +Settings +# TXT_SETTINGS +Port: +# TXT_PORT_COLON +IRQ: +# TXT_IRQ_COLON +Baud: +# TXT_BAUD_COLON +Init String: +# TXT_INIT_STRING +Call Waiting String: +# TXT_CWAIT_STRING +Tone Dialing +# TXT_TONE_BUTTON +Pulse Dialing +# TXT_PULSE_BUTTON +Host Serial Game +# TXT_HOST_SERIAL_GAME +Opponent: +# TXT_OPPONENT_COLON +User signed off! +# TXT_USER_SIGNED_OFF +Join Serial Game +# TXT_JOIN_SERIAL_GAME +Phone List +# TXT_PHONE_LIST +Add +# TXT_ADD +Edit +# TXT_EDIT +Dial +# TXT_DIAL +Default +# TXT_DEFAULT +Default Settings +# TXT_DEFAULT_SETTINGS +Custom Settings +# TXT_CUSTOM_SETTINGS +Phone Listing +# TXT_PHONE_LISTING +Name: +# TXT_NAME_COLON +Number: +# TXT_NUMBER_COLON +Unable to find modem.\rCheck power and cables. +# TXT_UNABLE_FIND_MODEM +No carrier. +# TXT_NO_CARRIER +Line busy. +# TXT_LINE_BUSY +Number invalid. +# TXT_NUMBER_INVALID +Other system not responding! +# TXT_SYSTEM_NOT_RESPONDING +Games are out of sync! +# TXT_OUT_OF_SYNC +Packet received too late! +# TXT_PACKET_TOO_LATE +Other player has left the game. +# TXT_PLAYER_LEFT_GAME +From %s:%s +# TXT_FROM +2,728,000 +# TXT_MAP_P01 +38,385,000 +# TXT_MAP_P02 +10,373,000 +# TXT_MAP_P03 +51,994,000 +# TXT_MAP_P04 +80,387,000 +# TXT_MAP_P05 +10,400,000 +# TXT_MAP_P06 +5,300,000 +# TXT_MAP_P07 +7,867,000 +# TXT_MAP_P08 +10,333,000 +# TXT_MAP_P09 +1,974,000 +# TXT_MAP_P10 +23,169,000 +# TXT_MAP_P11 +10,064,000 +# TXT_MAP_P12 +3,285,000 +# TXT_MAP_P13 +8,868,000 +# TXT_MAP_P14 +10,337,000 +# TXT_MAP_P15 +4,365,000 +# TXT_MAP_P16 +1,607,000 +# TXT_MAP_P17 +4,485,000 +# TXT_MAP_P18 +56,386,000 +# TXT_MAP_P19 +28,305,000 +# TXT_MAP_P20 +5,238,000 +# TXT_MAP_P21 +2,059,000 +# TXT_MAP_P22 +13,497,000 +# TXT_MAP_P23 +4,997,000 +# TXT_MAP_P24 +88,500,000 +# TXT_MAP_P25 +1,106,000 +# TXT_MAP_P26 +12,658,000 +# TXT_MAP_P27 +3,029,000 +# TXT_MAP_P28 +39,084,000 +# TXT_MAP_P29 +23,154,000 +# TXT_MAP_P30 +8,902,000 +# TXT_MAP_P31 +27,791,000 +# TXT_MAP_P32 +1,574,000 +# TXT_MAP_P33 +15,469,000 +# TXT_MAP_P34 +1,300,000 +# TXT_MAP_P35 +41,688,000 +# TXT_MAP_P36 +24,900 SQ. MI. +# TXT_MAP_A00 +120,727 SQ. MI. +# TXT_MAP_A01 +80,134 SQ. MI. +# TXT_MAP_A02 +233,100 SQ. MI. +# TXT_MAP_A03 +137,838 SQ. MI. +# TXT_MAP_A04 +30,449 SQ. MI. +# TXT_MAP_A05 +18,932 SQ. MI. +# TXT_MAP_A06 +32,377 SQ. MI. +# TXT_MAP_A07 +35,919 SQ. MI. +# TXT_MAP_A08 +7,819 SQ. MI. +# TXT_MAP_A09 +91,699 SQ. MI. +# TXT_MAP_A10 +51,146 SQ. MI. +# TXT_MAP_A11 +11,100 SQ. MI. +# TXT_MAP_A12 +44,365 SQ. MI. +# TXT_MAP_A13 +39,449 SQ. MI. +# TXT_MAP_A14 +19,741 SQ. MI. +# TXT_MAP_A15 +17,413 SQ. MI. +# TXT_MAP_A16 +RIGA +# TXT_MAP_C00 +WARSAW +# TXT_MAP_C01 +MINSK +# TXT_MAP_C02 +KIEV +# TXT_MAP_C03 +BERLIN +# TXT_MAP_C04 +PRAGUE +# TXT_MAP_C05 +BRATISLAVA +# TXT_MAP_C06 +VIENNA +# TXT_MAP_C07 +BUDAPEST +# TXT_MAP_C08 +LJUBLJANA +# TXT_MAP_C09 +BUCHAREST +# TXT_MAP_C10 +ATHENS +# TXT_MAP_C11 +TIRANA +# TXT_MAP_C12 +SOFIA +# TXT_MAP_C13 +BELGRADE +# TXT_MAP_C14 +SARAJEVO +# TXT_MAP_C15 +TALLINN +# TXT_MAP_C16 +TRIPOLI +# TXT_MAP_C17 +CAIRO +# TXT_MAP_C18 +KHARTOUM +# TXT_MAP_C19 +N'DJAMENA +# TXT_MAP_C20 +NOUAKCHOTT +# TXT_MAP_C21 +YAMOUSSOUKRO +# TXT_MAP_C22 +PORTO-NOVO +# TXT_MAP_C23 +ABUJA +# TXT_MAP_C24 +LIBREVILLE +# TXT_MAP_C25 +YAOUNDE +# TXT_MAP_C26 +BANGUI +# TXT_MAP_C27 +KINSHASA +# TXT_MAP_C28 +CAIRO +# TXT_MAP_C29 +LUANDA +# TXT_MAP_C30 +DAR-ES-SALAAM +# TXT_MAP_C31 +WINDHOEK +# TXT_MAP_C32 +MAPUTO +# TXT_MAP_C33 +GABARONE +# TXT_MAP_C34 +CAPE TOWN +# TXT_MAP_C35 +NEGLIGIBLE +# TXT_MAP_GDP00 +$162.7 BLN +# TXT_MAP_GDP01 +$47.6 BLN +# TXT_MAP_GDP02 +$1,131 BLN +# TXT_MAP_GDP03 +$120 BLN +# TXT_MAP_GDP04 +$164 BLN +# TXT_MAP_GDP05 +$60.1 BLN +# TXT_MAP_GDP06 +$21 BLN +# TXT_MAP_GDP07 +$71.9 BLN +# TXT_MAP_GDP08 +$77 BLN +# TXT_MAP_GDP09 +$4.0 BLN +# TXT_MAP_GDP10 +$47.3 BLN +# TXT_MAP_GDP11 +$120.1 BLN +# TXT_MAP_GDP12 +$14.0 BLN +# TXT_MAP_GDP13 +$28.9 BLN +# TXT_MAP_GDP14 +$39.2 BLN +# TXT_MAP_GDP15 +$12.1 BLN +# TXT_MAP_GDP16 +$1.0 BLN +# TXT_MAP_GDP17 +$10.0 BLN +# TXT_MAP_GDP18 +$1.7 BLN +# TXT_MAP_GDP19 +$28.0 BLN +# TXT_MAP_GDP20 +$5.3 BLN +# TXT_MAP_GDP21 +$11.6 BLN +# TXT_MAP_GDP22 +$1.3 BLN +# TXT_MAP_GDP23 +$6.6 BLN +# TXT_MAP_GDP24 +$8.3 BLN +# TXT_MAP_GDP25 +$6.9 BLN +# TXT_MAP_GDP26 +$2.0 BLN +# TXT_MAP_GDP27 +$3.1 BLN +# TXT_MAP_GDP28 +$104.0 BLN +# TXT_MAP_GDP29 +JELGAVA +# TXT_MAP_PC00 +GDANSK +# TXT_MAP_PC01 +BYELISTOK +# TXT_MAP_PC02 +BOBYRUSK +# TXT_MAP_PC03 +IVANO-FRANKOVSK +# TXT_MAP_PC04 +HANOVER +# TXT_MAP_PC05 +DRESDEN +# TXT_MAP_PC06 +OSTRAVA +# TXT_MAP_PC07 +BRATISLAVA +# TXT_MAP_PC08 +SALZBURG +# TXT_MAP_PC09 +BUDAPEST +# TXT_MAP_PC10 +TRIESTE +# TXT_MAP_PC11 +ARAD +# TXT_MAP_PC12 +CORINTH +# TXT_MAP_PC13 +SHKODER +# TXT_MAP_PC14 +SOFIA +# TXT_MAP_PC15 +NIS +# TXT_MAP_PC16 +BELGRADE +# TXT_MAP_PC17 +? +# TXT_MAP_PC18 +PARNU +# TXT_MAP_PC19 +TMASSAH +# TXT_MAP_PC20 +AL-ALAMYN +# TXT_MAP_PC21 +AL-KHARIJAH +# TXT_MAP_PC22 +AL-UBAYYID +# TXT_MAP_PC23 +KAFIA-KINGI +# TXT_MAP_PC24 +OUM HADJER +# TXT_MAP_PC25 +MAO +# TXT_MAP_PC26 +TIDJIKDJA +# TXT_MAP_PC27 +ABIDJAN +# TXT_MAP_PC28 +PORTO-NOVO +# TXT_MAP_PC29 +ABUJA +# TXT_MAP_PC30 +KOULA-MOUTOU +# TXT_MAP_PC31 +BERTOUA +# TXT_MAP_PC32 +BANGASSOU +# TXT_MAP_PC33 +LODJA +# TXT_MAP_PC34 +KINSHASA +# TXT_MAP_PC35 +LUXOR +# TXT_MAP_PC36 +CAIUNDO +# TXT_MAP_PC37 +MZUZU +# TXT_MAP_PC38 +KEETMANSHOOP +# TXT_MAP_PC39 +XAI-XAI +# TXT_MAP_PC40 +GHANZI +# TXT_MAP_PC41 +CAPE TOWN +# TXT_MAP_PC42 +GDI PROGRESSION +# TXT_MAP_GDI +NOD PROGRESSION +# TXT_MAP_NOD +LOCATING COORDINATES +# TXT_MAP_LOCATE +OF NEXT MISSION +# TXT_MAP_NEXT_MISSION +SELECT TERRITORY +# TXT_MAP_SELECT +TO ATTACK +# TXT_MAP_TO_ATTACK +POPULATION: +# TXT_MAP_GDISTAT0 +GEOGRAPHIC AREA: +# TXT_MAP_GDISTAT1 +CAPITAL: +# TXT_MAP_GDISTAT2 +GOVERNMENT: +# TXT_MAP_GDISTAT3 +GROSS DOMESTIC PRODUCT: +# TXT_MAP_GDISTAT4 +POINT OF CONFLICT: +# TXT_MAP_GDISTAT5 +MILITARY POWER: +# TXT_MAP_GDISTAT6 +EXPENDABILITY: +# TXT_MAP_NODSTAT0 +GOVT CORRUPTABILITY: +# TXT_MAP_NODSTAT1 +NET WORTH: +# TXT_MAP_NODSTAT2 +MILITARY STRENGTH: +# TXT_MAP_NODSTAT3 +MILITARY RESISTANCE: +# TXT_MAP_NODSTAT4 +LATVIA +# TXT_MAP_COUNTRYNAME0 +POLAND +# TXT_MAP_COUNTRYNAME1 +BELARUS +# TXT_MAP_COUNTRYNAME2 +UKRAINE +# TXT_MAP_COUNTRYNAME3 +GERMANY +# TXT_MAP_COUNTRYNAME4 +CZECH REPUBLIC +# TXT_MAP_COUNTRYNAME5 +SLOVAKIA +# TXT_MAP_COUNTRYNAME6 +AUSTRIA +# TXT_MAP_COUNTRYNAME7 +HUNGARY +# TXT_MAP_COUNTRYNAME8 +SLOVENIA +# TXT_MAP_COUNTRYNAME9 +ROMANIA +# TXT_MAP_COUNTRYNAME10 +GREECE +# TXT_MAP_COUNTRYNAME11 +ALBANIA +# TXT_MAP_COUNTRYNAME12 +BULGARIA +# TXT_MAP_COUNTRYNAME13 +YUGOSLAVIA +# TXT_MAP_COUNTRYNAME14 +BOSNIA/HERZOGOVINA +# TXT_MAP_COUNTRYNAME15 +LIBYA +# TXT_MAP_COUNTRYNAME16 +EGYPT +# TXT_MAP_COUNTRYNAME17 +SUDAN +# TXT_MAP_COUNTRYNAME18 +CHAD +# TXT_MAP_COUNTRYNAME19 +MAURITANIA +# TXT_MAP_COUNTRYNAME20 +IVORY COAST +# TXT_MAP_COUNTRYNAME21 +BENIN +# TXT_MAP_COUNTRYNAME22 +NIGERIA +# TXT_MAP_COUNTRYNAME23 +GABON +# TXT_MAP_COUNTRYNAME24 +CAMEROON +# TXT_MAP_COUNTRYNAME25 +CENTRAL AFRICAN REPUBLIC +# TXT_MAP_COUNTRYNAME26 +ZAIRE +# TXT_MAP_COUNTRYNAME27 +ANGOLA +# TXT_MAP_COUNTRYNAME28 +TANZANIA +# TXT_MAP_COUNTRYNAME29 +NAMIBIA +# TXT_MAP_COUNTRYNAME30 +MOZAMBIQUE +# TXT_MAP_COUNTRYNAME31 +BOTSWANA +# TXT_MAP_COUNTRYNAME32 +SOUTH AFRICA +# TXT_MAP_COUNTRYNAME33 +ESTONIA +# TXT_MAP_COUNTRYNAME34 +REPUBLIC +# TXT_MAP_GOVT0 +DEMOCRATIC STATE +# TXT_MAP_GOVT1 +FEDERAL REPUBLIC +# TXT_MAP_GOVT2 +CONST. REPUBLIC +# TXT_MAP_GOVT3 +PARL. DEMOCRACY +# TXT_MAP_GOVT4 +PRES. PARL. REPUBLIC +# TXT_MAP_GOVT5 +DEMOCRACY +# TXT_MAP_GOVT6 +IN TRANSITION +# TXT_MAP_GOVT7 +ISLAMIC SOCIALIST +# TXT_MAP_GOVT8 +MILITARY +# TXT_MAP_GOVT9 +ISLAMIC REPUBLIC +# TXT_MAP_GOVT10 +PARL. REPUBLIC +# TXT_MAP_GOVT11 +LOCAL MILITIA +# TXT_MAP_ARMY0 +STATE MILITIA +# TXT_MAP_ARMY1 +NATIONAL GUARD +# TXT_MAP_ARMY2 +FREE STANDING ARMY +# TXT_MAP_ARMY3 +? +# TXT_MAP_ARMY4 +NATIONAL POWER +# TXT_MAP_ARMY5 +RESPECTABLE +# TXT_MAP_MILITARY0 +FORMIDABLE +# TXT_MAP_MILITARY1 +LAUGHABLE +# TXT_MAP_MILITARY2 +REASONABLE +# TXT_MAP_MILITARY3 +INSIGNIFICANT +# TXT_MAP_MILITARY4 +CLICK TO CONTINUE +# TXT_MAP_CLICK2 +LOW +# TXT_MAP_LMH0 +MEDIUM +# TXT_MAP_LMH1 +HIGH +# TXT_MAP_LMH2 +TIME: +# TXT_SCORE_TIME +LEADERSHIP: +# TXT_SCORE_LEAD +EFFICIENCY: +# TXT_SCORE_EFFI +TOTAL SCORE: +# TXT_SCORE_TOTA +CASUALTIES: +# TXT_SCORE_CASU +NEUTRAL: +# TXT_SCORE_NEUT +GDI: +# TXT_SCORE_GDI +BUILDINGS LOST +# TXT_SCORE_BUIL +BUILDINGS +# TXT_SCORE_BUIL1 +LOST: +# TXT_SCORE_BUIL2 +TOP SCORES +# TXT_SCORE_TOP +ENDING CREDITS: +# TXT_SCORE_ENDCRED +%dh %dm +# TXT_SCORE_TIMEFORMAT1 +%dm +# TXT_SCORE_TIMEFORMAT2 +NOD: +# TXT_SCORE_NOD +Dialing... +# TXT_DIALING +Dialing Canceled +# TXT_DIALING_CANCELED +Waiting for Call... +# TXT_WAITING_FOR_CALL +Answering Canceled +# TXT_ANSWERING_CANCELED +Engineer +# TXT_E7 +Special Options +# TXT_SPECIAL_OPTIONS +Targeting flash visible to all. +# TXT_VISIBLE_TARGET +Allow targeting of trees. +# TXT_TREE_TARGET +Allow undeploy of construction yard. +# TXT_MCV_DEPLOY +Employ smarter self defense logic. +# TXT_SMART_DEFENCE +Moderate production speed. +# TXT_SLOW_BUILD +Use three point turn logic. +# TXT_THREE_POINT +Tiberium will grow. +# TXT_TIBERIUM_GROWTH +Tiberium will spread. +# TXT_TIBERIUM_SPREAD +Disable building "bib" pieces. +# TXT_ROAD_PIECES +Allow running from immediate threats. +# TXT_SCATTER +Not a Null Modem Cable Attached!\rIt is a modem or loopback cable. +# TXT_MODEM_OR_LOOPBACK +Map +# TXT_MAP +From Computer: +# TXT_FROM_COMPUTER +Prepare to die! +# TXT_COMP_MSG1 +How about a bullet sandwich?! +# TXT_COMP_MSG2 +Incoming! +# TXT_COMP_MSG3 +I see you! +# TXT_COMP_MSG4 +Hey, I'm over here! +# TXT_COMP_MSG5 +Come get some! +# TXT_COMP_MSG6 +I got you! +# TXT_COMP_MSG7 +You humans are never a challenge! +# TXT_COMP_MSG8 +Abort, Retry, Ignore? (Ha ha!) +# TXT_COMP_MSG9 +Format another? (Just kidding!) +# TXT_COMP_MSG10 +Beat me and I'll reboot! +# TXT_COMP_MSG11 +You're artificial intelligence! +# TXT_COMP_MSG12 +My AI is better than your AI. +# TXT_COMP_MSG13 +Air Strike +# TXT_THEME_AIRSTRIKE +Demolition +# TXT_THEME_HEAVYG +Untamed Land +# TXT_THEME_J1 +Take 'em Out +# TXT_THEME_JDI_V2 +Radio +# TXT_THEME_RADIO +Rain In The Night +# TXT_THEME_RAIN +Canyon Chase +# TXT_THEME_IND2 +Heartbreak +# TXT_THEME_HEART +Blossom Tree +# TXT_BLOSSOM_TREE +Restate +# TXT_RESTATE_MISSION +Computer +# TXT_COMPUTER +Unit Count: +# TXT_COUNT +Tech Level: +# TXT_LEVEL +Opponent +# TXT_OPPONENT +Kills: +# TXT_KILLS_COLON +Video +# TXT_VIDEO +Nikoomba +# TXT_C10 +Capture The Flag +# TXT_CAPTURE_THE_FLAG +Ride of the Valkyries +# TXT_THEME_VALK +Mission Objective +# TXT_OBJECTIVE +Mission +# TXT_MISSION +No saved games available. +# TXT_NO_SAVES +Civilian Building +# TXT_CIVILIAN_BUILDING +Technician +# TXT_TECHNICIAN +Visceroid +# TXT_VISCEROID +Save game options are not allowed during a multiplayer session. +# TXT_NO_SAVELOAD +Defender has the advantage. +# TXT_DEFENDER_ADVANTAGE +Show true object names. +# TXT_SHOW_NAMES +Agent Delphi +# TXT_DELPHI +Would you like to replay this mission? +# TXT_TO_REPLAY +Reconnecting to %s. +# TXT_RECONN_TO +Please wait %02d seconds. +# TXT_PLEASE_WAIT +Do you wish\rto surrender? +# TXT_SURRENDER +GLOBAL DEFENSE INITIATIVE +# TXT_GDI_NAME +BROTHERHOOD OF NOD +# TXT_NOD_NAME +SELECT TRANSMISSION +# TXT_SEL_TRANS +Your game name must be unique. +# TXT_GAMENAME_MUSTBE_UNIQUE +Game is closed. +# TXT_GAME_IS_CLOSED +Your name must be unique. +# TXT_NAME_MUSTBE_UNIQUE +Reconnecting to %s +# TXT_RECONNECTING_TO +Waiting for connections... +# TXT_WAITING_FOR_CONNECTIONS +Time allowed: %02d seconds +# TXT_TIME_ALLOWED +Press ESC to cancel. +# TXT_PRESS_ESC +From Computer: It's just you and me now! +# TXT_JUST_YOU_AND_ME +Capture the Flag: +# TXT_CAPTURE_THE_FLAG_COLON +Dr. Chan +# TXT_CHAN +%s has allied with %s +# TXT_HAS_ALLIED +%s declares war on %s +# TXT_AT_WAR +Select a target +# TXT_SEL_TARGET +Allow separate helipad purchase +# TXT_SEPARATE_HELIPAD +Resign Game +# TXT_RESIGN +Tiberium grows quickly. +# TXT_TIBERIUM_FAST +Answering... +# TXT_ANSWERING +Initializing Modem... +# TXT_INITIALIZING_MODEM +Scenarios don't match. +# TXT_SCENARIOS_DO_NOT_MATCH +Power Output +# TXT_POWER_OUTPUT +Power Output (low) +# TXT_POWER_OUTPUT_LOW +Continue +# TXT_CONTINUE +Data Queue Overflow +# TXT_QUEUE_FULL +%s changed game options! +# TXT_SPECIAL_WARNING +Please insert a Command & Conquer CD into the CD-ROM drive. +# TXT_CD_DIALOG_1 +Please insert CD %d (%s) into the CD-ROM drive. +# TXT_CD_DIALOG_2 +Command & Conquer is unable to detect your CD ROM drive. +# TXT_CD_ERROR1 +No Sound Card Detected +# TXT_NO_SOUND_CARD +UNKNOWN +# TXT_UNKNOWN +(old) +# TXT_OLD_GAME +Insufficient Disk Space to run Command & Conquer. +# TXT_NO_SPACE +You must have %d megabytes of free disk space. +# TXT_MUST_HAVE_SPACE +Run SETUP program first. +# TXT_RUN_SETUP +Waiting for Opponent +# TXT_WAITING_FOR_OPPONENT +Please select 'Settings' to setup default configuration +# TXT_SELECT_SETTINGS +Prison +# TXT_PRISON +Game Saved +# TXT_GAME_WAS_SAVED +Insufficient disk space to save a game. Please delete a previous save to free up some disk space and try again. +# TXT_SPACE_CANT_SAVE +Invalid Port/Address.\rCOM 1-4 OR ADDRESS +# TXT_INVALID_PORT_ADDRESS +Invalid Port and/or IRQ settings +# TXT_INVALID_SETTINGS +IRQ already in use +# TXT_IRQ_ALREADY_IN_USE +Abort +# TXT_ABORT +Restart +# TXT_RESTART +Mission is restarting.\rPlease wait... +# TXT_RESTARTING +Mission is loading.\rPlease wait... +# TXT_LOADING +Error in the InitString +# TXT_ERROR_IN_INITSTRING diff --git a/CONST.CPP b/CONST.CPP new file mode 100644 index 0000000..654e761 --- /dev/null +++ b/CONST.CPP @@ -0,0 +1,425 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\const.cpv 2.17 16 Oct 1995 16:52:24 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONST.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 20, 1993 * + * * + * Last Update : September 20, 1993 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +char const * SourceName[SOURCE_COUNT] = +{ + "North", + "East", + "South", + "West", + "Shipping", + "Beach", + "Air", + "Visible", + "EnemyBase", + "HomeBase", + "Ocean", +}; + + +/*************************************************************************** +** Relative coordinate offsets from the center of a cell for each +** of the legal positions that an object in a cell may stop at. Only infantry +** are allowed to stop at other than the center of the cell. +*/ +COORDINATE const StoppingCoordAbs[5] = { + 0x00800080L, // center + 0x00400040L, // upper left + 0x004000C0L, // upper right + 0x00C00040L, // lower left + 0x00C000C0L // lower right +}; + + +/*************************************************************************** +** These are the various weapons and their characteristics. +** +** bullet type dmg, rof, range, sound +*/ +WeaponTypeClass const Weapons[WEAPON_COUNT] = { + {BULLET_SNIPER, 125, 40, 0x0580, VOC_SNIPER, ANIM_NONE}, // WEAPON_RIFLE + {BULLET_SPREADFIRE, 25, 50, 0x0400, VOC_MINI, ANIM_GUN_N}, // WEAPON_CHAIN_GUN + {BULLET_BULLET, 1, 7, 0x01C0, VOC_RIFLE, ANIM_NONE}, // WEAPON_PISTOL + {BULLET_BULLET, 15, 20, 0x0200, VOC_MGUN2, ANIM_NONE}, // WEAPON_M16 + {BULLET_TOW, 30, 60, 0x0400, VOC_BAZOOKA,ANIM_NONE}, // WEAPON_DRAGON + {BULLET_FLAME, 35, 50, 0x0200, VOC_FLAMER1,ANIM_FLAME_N}, // WEAPON_FLAMETHROWER + {BULLET_FLAME, 50, 50, 0x0200, VOC_FLAMER1,ANIM_FLAME_N}, // WEAPON_FLAME_TONGUE + {BULLET_CHEMSPRAY, 80, 70, 0x0200, VOC_FLAMER1,ANIM_CHEM_N}, // WEAPON_CHEMSPRAY + {BULLET_GRENADE, 50, 60, 0x0340, VOC_TOSS, ANIM_NONE}, // WEAPON_GRENADE + {BULLET_APDS, 25, 60, 0x0400, VOC_TANK2, ANIM_MUZZLE_FLASH}, // WEAPON_75MM + {BULLET_APDS, 30, 50, 0x04C0, VOC_TANK3, ANIM_MUZZLE_FLASH}, // WEAPON_105MM + {BULLET_APDS, 40, 80, 0x04C0, VOC_TANK4, ANIM_MUZZLE_FLASH}, // WEAPON_120MM + {BULLET_APDS, 40, 60, 0x0600, VOC_TANK4, ANIM_MUZZLE_FLASH}, // WEAPON_TURRET_GUN + {BULLET_SSM, 75, 80, 0x0500, VOC_ROCKET1,ANIM_NONE}, // WEAPON_MAMMOTH_TUSK + {BULLET_SSM2, 75, 80, 0x0600, VOC_ROCKET1,ANIM_NONE}, // WEAPON_MLRS + {BULLET_HE, 150, 65, 0x0600, VOC_TANK1, ANIM_MUZZLE_FLASH}, // WEAPON_155MM + {BULLET_BULLET, 15, 30, 0x0400, VOC_MGUN11, ANIM_GUN_N}, // WEAPON_M60MG + {BULLET_SSM, 60, 35, 0x0780, VOC_ROCKET2,ANIM_NONE}, // WEAPON_TOMAHAWK + {BULLET_SSM, 60, 40, 0x0680, VOC_ROCKET2,ANIM_NONE}, // WEAPON_TOW_TWO + {BULLET_NAPALM, 100, 20, 0x0480, VOC_NONE, ANIM_NONE}, // WEAPON_NAPALM + {BULLET_LASER, 200, 90, 0x0780, VOC_LASER, ANIM_NONE}, // WEAPON_OBELISK_LASER + {BULLET_SAM, 50, 50, 0x0780, VOC_ROCKET2,ANIM_NONE}, // WEAPON_NIKE + {BULLET_HONEST_JOHN, 100, 200, 0x0A00, VOC_ROCKET1,ANIM_NONE}, // WEAPON_HONEST_JOHN + {BULLET_HEADBUTT, 100, 30, 0x0180, VOC_DINOATK1,ANIM_NONE}, // WEAPON_STEG + {BULLET_TREXBITE, 155, 30, 0x0180, VOC_DINOATK1,ANIM_NONE}, // WEAPON_TREX +}; + + +/*************************************************************************** +** These are the various warheads. +** +** spread factor, destroys walls, destroys wood, destroys Tiberium, {armor defense table} +** -vs- {none, wood, aluminum, steel, concrete} +*/ +WarheadTypeClass const Warheads[WARHEAD_COUNT] = { + { 2,false,false,false,{0xFF, 0x80, 0x90, 0x40, 0x40}}, // WARHEAD_SA Small arms -- good against infantry. + { 6,true,true,true,{0xE0, 0xC0, 0x90, 0x40, 0xFF}}, // WARHEAD_HE High explosive -- good against buildings & infantry. + { 6,true,true,false,{0x40, 0xC0, 0xC0, 0xFF, 0x80}}, // WARHEAD_AP Armor piercing -- good against armor. + { 8,false,true,true,{0xE0, 0xFF, 0xB0, 0x40, 0x80}}, // WARHEAD_FIRE Incendiary -- Good against flammables. + { 4,false,false,false,{0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, // WARHEAD_LASER Light Amplification of Stimulated Emission by Radiation. + { 7,true,true,true,{0xFF, 0xFF, 0xC0, 0xC0, 0xC0}}, // WARHEAD_PB Particle beam (neutron beam). + { 4,false,false,false,{0xFF, 0x20, 0x20, 0x10, 0x10}}, // WARHEAD_FIST Punching in hand-to-hand combat. + { 4,false,false,false,{0xFF, 0x20, 0x20, 0x10, 0x10}}, // WARHEAD_FOOT Kicking in hand-to-hand combat. + { 4,false,false,false,{0xFF, 0x08, 0x08, 0x08, 0x08}}, // WARHEAD_HOLLOW_POINT Sniper bullet type. + {255,false,false,false,{0xFF, 0x01, 0x01, 0x01, 0x01}}, // WARHEAD_SPORE + { 1, true,true,false,{0xFF, 0xC0, 0x80, 0x20, 0x08}}, // WARHEAD_HEADBUTT + { 1, true,true,false,{0xFF, 0xC0, 0x80, 0x20, 0x08}}, // WARHEAD_FEEDME +}; + + +/*************************************************************************** +** Converts pixel values (cell relative) into the appropriate lepton (sub cell) +** value. This is used to convert pixel (screen) coordinates into the underlying +** coordinate system. +*/ +unsigned char const Pixel2Lepton[24] = { + 0x00,0x0B,0x15,0x20,0x2B,0x35,0x40,0x4B, + 0x55,0x60,0x6B,0x75,0x80,0x8B,0x95,0xA0, + 0xAB,0xB5,0xC0,0xCB,0xD5,0xE0,0xEB,0xF5 +}; + + +/*************************************************************************** +** This array is used to index a facing in order to retrieve a cell +** offset that, when added to another cell, will achieve the adjacent cell +** in the indexed direction. +*/ +CELL const AdjacentCell[FACING_COUNT] = { + -(MAP_CELL_W), // North + -(MAP_CELL_W-1), // North East + 1, // East + MAP_CELL_W+1, // South East + MAP_CELL_W, // South + MAP_CELL_W-1, // South West + -1, // West + -(MAP_CELL_W+1) // North West +}; + +COORDINATE const AdjacentCoord[FACING_COUNT] = { + 0xFF000000L, + 0xFF000100L, + 0x00000100L, + 0x01000100L, + 0x01000000L, + 0x0100FF00L, + 0x0000FF00L, + 0xFF00FF00L +}; + + +/*************************************************************************** +** This converts 0..255 facing values into either 8, 16, or 32 facing values. +** Note: a simple shift won't suffice because 0..255 facing values should +** be converted to the CLOSEST appropriate facing, NOT rounded down to the +** nearest facing. +*/ +unsigned char const Facing8[256] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + +#ifdef NEVER +unsigned char const Facing16[256] = { + 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4, + 4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,0,0,0,0,0,0,0 +}; +#endif + +/* +** This table incorporates a compensating factor for the distortion caused +** by 3D-Studio when it tries to render 45% angles. +*/ +unsigned char const Facing32[256] = { + 0,0,0,0,0,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3, + 3,4,4,4,4,4,4,5,5,5,5,5,5,5,6,6,6,6,6,6,6,7,7,7,7,7,7,7,8,8,8,8, + 8,8,8,9,9,9,9,9,9,9,10,10,10,10,10,10,10,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12, + 13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16, + 16,16,16,16,16,17,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19, + 19,20,20,20,20,20,20,21,21,21,21,21,21,21,22,22,22,22,22,22,22,23,23,23,23,23,23,23,24,24,24,24, + 24,24,24,25,25,25,25,25,25,25,26,26,26,26,26,26,26,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28, + 29,29,29,29,29,29,29,29,30,30,30,30,30,30,30,30,30,31,31,31,31,31,31,31,31,31,0,0,0,0,0,0 +}; + +#ifdef OBSOLETE +unsigned char const Facing32[256] = { + 0,0,0,0, + 1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7, + 8,8,8,8,8,8,8,8, + 9,9,9,9,9,9,9,9, + 10,10,10,10,10,10,10,10, + 11,11,11,11,11,11,11,11, + 12,12,12,12,12,12,12,12, + 13,13,13,13,13,13,13,13, + 14,14,14,14,14,14,14,14, + 15,15,15,15,15,15,15,15, + 16,16,16,16,16,16,16,16, + 17,17,17,17,17,17,17,17, + 18,18,18,18,18,18,18,18, + 19,19,19,19,19,19,19,19, + 20,20,20,20,20,20,20,20, + 21,21,21,21,21,21,21,21, + 22,22,22,22,22,22,22,22, + 23,23,23,23,23,23,23,23, + 24,24,24,24,24,24,24,24, + 25,25,25,25,25,25,25,25, + 26,26,26,26,26,26,26,26, + 27,27,27,27,27,27,27,27, + 28,28,28,28,28,28,28,28, + 29,29,29,29,29,29,29,29, + 30,30,30,30,30,30,30,30, + 31,31,31,31,31,31,31,31, + 0,0,0,0 +}; +#endif + + +/*************************************************************************** +** These are the movement costs (in ticks at fastest speed) to enter each +** of the given terrain cells. +*/ +#define S1 0x00 +#define S2 0x40 +#define S3 0x70 +#define S4 0xA0 +#define S5 0xC0 +#define S6 0xFF +GroundType const Ground[LAND_COUNT] = { +// Foot +// | Tracked +// | | Harvester +// | | | Wheeled +// | | | | Winged +// | | | | | Hover +// | | | | | | float build + {66, {S3, S3, S3, S4, S6, S5, S1 }, true}, // LAND_CLEAR + {68, {S5, S4, S4, S4, S6, S5, S1 }, true}, // LAND_ROAD + {BLUE, {S1, S1, S1, S1, S6, S5, S6 }, false}, // LAND_WATER + {DKGREY, {S1, S1, S1, S1, S6, S1, S1 }, false}, // LAND_ROCK + {DKGREY, {S1, S1, S1, S1, S6, S1, S1 }, false}, // LAND_WALL + {143, {S3, S3, S3, S4, S6, S5, S1 }, false}, // LAND_TIBERIUM + {66, {S3, S3, S3, S4, S6, S5, S1 }, false}, // LAND_BEACH +}; + + +/*************************************************************************** +** These are the names of the theaters. +*/ +TheaterDataType const Theaters[THEATER_COUNT] = { + {"DESERT","DESERT","DES"}, + {"JUNGLE","JUNGLE","JUN"}, + {"TEMPERATE","TEMPERAT","TEM"}, + {"WINTER","WINTER","WIN"} +}; + + +/*************************************************************************** +** These are the remap tables that are used to convert the units/buildings +** into the other color schemes. +*/ +unsigned char const RemapYellow[256] = { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapRed[256] = { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 127,126,125,124,122,46,120,47,125,124,123,122,42,121,120,120, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapBlueGreen[256] = { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 2,119,118,135,136,138,112,12,118,135,136,137,138,139,114,112, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapOrange[256] = { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 24,25,26,27,29,31,46,47,26,27,28,29,30,31,43,47, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapGreen[256] = { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 5,165,166,167,159,142,140,199,166,167,157,3,159,143,142,141, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapBlue[256] = { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 161,200,201,202,204,205,206,12,201,202,203,204,205,115,198,114, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapNone[256] = { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; diff --git a/CONTROL.CPP b/CONTROL.CPP new file mode 100644 index 0000000..f5e8468 --- /dev/null +++ b/CONTROL.CPP @@ -0,0 +1,202 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\control.cpv 2.18 16 Oct 1995 16:51:38 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONTROL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 19, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ControlClass::Action -- Normal action for control gaget objects. * + * ControlClass::ControlClass -- Constructor for control class objects. * + * ControlClass::Draw_Me -- Draw logic for the control class object. * + * ControlClass::Get_ID -- Gets the ID number for this gadget. * + * ControlClass::Make_Peer -- Assigns a peer gadget to this gadget. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * ControlClass::ControlClass -- Constructor for control class objects. * + * * + * This is the normal constructor for control class objects. At this level, it only needs * + * to record the ID number assigned to this button. * + * * + * INPUT: id -- The ID number for this gadget. If the ID number specified is 0, then * + * this tells the system that no special ID code should be returned. * + * * + * x,y -- Pixel coordinate of upper left corner of gadget's region. * + * * + * w,h -- Pixel dimensions of the gadget's region. * + * * + * flags -- The input event flags that this gadget recognizes. * + * * + * sticky-- This this a "sticky" gadget? A sticky gadget is one that takes over the * + * gadget list while the mouse button is held down, if the mouse button was * + * initially clicked over its region. This is the behavior of "normal" * + * buttons in Windows. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +ControlClass::ControlClass(unsigned id, int x, int y, int w, int h, unsigned flags, int sticky) + : GadgetClass(x, y, w, h, flags, sticky) +{ + ID = id; + Peer = 0; +} + + +/*********************************************************************************************** + * ControlClass::Action -- Normal action for control gaget objects. * + * * + * This function gets called when the input event that this control gadget is looking for * + * occurs. In such a case, the return key code value is changed to the gaget's ID number * + * with the special button bit flag attached. * + * * + * INPUT: flags -- The event that triggered this function call. If this value is NULL, then * + * this is a forced (probably due to the sticky flag) call and the key code * + * is not altered. * + * * + * key -- Reference to the key code that will be returned by the controlling * + * Input() function. * + * * + * OUTPUT: bool; Should futher list processing be aborted? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int ControlClass::Action(unsigned flags, KeyNumType & key) +{ + /* + ** If there is a peer link established, inform that gadget of this + ** action call. + */ + if (Peer) { + Peer->Peer_To_Peer(flags, key, *this); + } + + /* + ** Only if the flags indicate that a recognized action has occured, do the + ** normal processing of this gadget and set return value to the gadget ID. + */ + if (flags) { + if (ID) { + key = (KeyNumType)(ID | KN_BUTTON); + } else { + key = KN_NONE; + } + } + + return(GadgetClass::Action(flags, key)); +} + + +/*********************************************************************************************** + * ControlClass::Make_Peer -- Assigns a peer gadget to this gadget. * + * * + * This function will assign another gadget to this one. That other gadget will receive * + * notification of any Action() call to this gadget. Presumably, this is how one gadget * + * can automatically adapt to changes in another. Say for example, a slider bar can affect * + * the list box it is attached to. * + * * + * INPUT: gadget -- The gadget to inform when any Action() function is called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void ControlClass::Make_Peer(GadgetClass & gadget) +{ + Peer = &gadget; +} + + +/*********************************************************************************************** + * ControlClass::Get_ID -- Gets the ID number for this gadget. * + * * + * This function will query and return with the ID number for this gadget. It is primarily * + * used by the Extract_Gadget() function. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the ID number for this gadget. If zero is returned, this means that * + * no ID was assigned to this gadget. This is a special case since a zero value will * + * never be returned as a pseudo-key as is done with non-zero values. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +unsigned ControlClass::Get_ID(void) const +{ + return(ID); +} + + +/*********************************************************************************************** + * ControlClass::Draw_Me -- Draw logic for the control class object. * + * * + * This is called when the control object might need to be redrawn or when redrawing is * + * necessary. Since at this level of the class heirarchy, no actual drawing occurs, this * + * routine doesn't perform any rendering. It does, however, inform any peer attached * + * object that a Draw_Me function has been called. Presumably, the attached peer gadget * + * might very well need to be redrawn as a result of some action by this gadget. Since this * + * gadget might, more than likely, be of the "sticky" variety, a normal call to Draw_Me * + * for the other gadget will not occur. It must rely on the call by this routine in order * + * to update correctly. A typical example of this would be a slider that is attached to * + * a list box. As the slider is being drug around, the attached list box must be redrawn. * + * * + * INPUT: forced -- Should the redraw be forced regardless of the redraw flag? * + * * + * OUTPUT: bool; Was the gadget redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ControlClass::Draw_Me(int forced) +{ + if (Peer) { + Peer->Draw_Me(); + } + return(GadgetClass::Draw_Me(forced)); +} diff --git a/CONTROL.H b/CONTROL.H new file mode 100644 index 0000000..174e734 --- /dev/null +++ b/CONTROL.H @@ -0,0 +1,89 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\control.h_v 2.18 16 Oct 1995 16:46:08 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONTROL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CONTROL_H +#define CONTROL_H + +#include "gadget.h" + +/*************************************************************************** + * ControlClass -- Region tracking class * + * * + * INPUT: int x -- x position of gadget * + * int y -- y position of gadget * + * int w -- width of gadget * + * int h -- height of gadget * + * UWORD flags -- see enumeration choices * + * * + * OUTPUT: 0 = new scenario created, -1 = not * + * WARNINGS: This class is Abstract (cannot make an instance of it) * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + *=========================================================================*/ +class ControlClass : public GadgetClass +{ + public: + ControlClass(unsigned id, int x, int y, int w, int h, unsigned flags=LEFTPRESS|RIGHTPRESS, int sticky=false); +// static ControlClass * Create_One_Of(unsigned id, int x, int y, int w, int h, unsigned flags=LEFTPRESS|RIGHTPRESS, int sticky=false); + + virtual void Make_Peer(GadgetClass & gadget); + + /* + ** Render support function. + */ + virtual int Draw_Me(int forced=false); + + /* + ** This is the ID number for this control gadget. This number is used to generate + ** a special pseudo-key when the gadget detects valid input. + */ + unsigned ID; + + protected: + virtual unsigned Get_ID(void) const; + virtual int Action(unsigned flags, KeyNumType & key); + + /* + ** This points to the peer button to inform when something happens to this + ** gadget. + */ + GadgetClass * Peer; +}; + +#endif + diff --git a/COORD.CPP b/COORD.CPP new file mode 100644 index 0000000..369b276 --- /dev/null +++ b/COORD.CPP @@ -0,0 +1,545 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\coord.cpv 2.18 16 Oct 1995 16:51:24 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COORD.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : January 7, 1995 [JLB] * + * * + * Support code to handle the coordinate system is located in this module. * + * Routines here will be called QUITE frequently during play and must be * + * as efficient as possible. * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Cardinal_To_Fixed -- Converts cardinal numbers into a fixed point number. * + * Coord_Move -- Moves a coordinate an arbitrary direction for an arbitrary distance * + * Coord_Scatter -- Determines a random coordinate from an anchor point. * + * Coord_Spillage_List -- Determines the offset list for cell spillage/occupation. * + * Fixed_To_Cardinal -- Converts a fixed point number into a cardinal number. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * Coord_Spillage_List -- Determines the offset list for cell spillage/occupation. * + * * + * This routine will take an arbitrary position and object size and return with a list of * + * cell offsets from the current cell for all cells that are overlapped by the object. The * + * first cell offset is always zero, so to just get the adjacent spill cell list, add one * + * to the return pointer. * + * * + * INPUT: coord -- The coordinate to examine. * + * * + * maxsize -- The maximum width/height of the object (pixels). * + * * + * OUTPUT: Returns with a pointer to a spillage list. * + * * + * WARNINGS: The algorithm is limited to working with a maxsize of 48 or less. Larger values * + * will generate an incomplete overlap list. * + * * + * HISTORY: * + * 11/06/1993 JLB : Created. * + * 03/25/1994 JLB : Added width optimization. * + * 04/29/1994 JLB : Converted to C. * + * 06/03/1994 JLB : Converted to general purpose spillage functionality. * + * 01/07/1995 JLB : Manually calculates spillage list for large objects. * + *=============================================================================================*/ +short const * Coord_Spillage_List(COORDINATE coord, int maxsize) +{ + static short const _MoveSpillage[(int)FACING_COUNT+1][5] = { + {0, -MAP_CELL_W, REFRESH_EOL, 0, 0}, // N + {0, -MAP_CELL_W, 1, -(MAP_CELL_W-1), REFRESH_EOL}, // NE + {0, 1, REFRESH_EOL, 0, 0}, // E + {0, 1, MAP_CELL_W, MAP_CELL_W+1, REFRESH_EOL}, // SE + {0, MAP_CELL_W, REFRESH_EOL, 0, 0}, // S + {0, -1, MAP_CELL_W, MAP_CELL_W-1, REFRESH_EOL}, // SW + {0, -1, REFRESH_EOL, 0, 0}, // W + {0, -1, -MAP_CELL_W, -(MAP_CELL_W+1), REFRESH_EOL}, // NW + {0, REFRESH_EOL, 0, 0, 0} // non-moving. +// {0, -MAP_CELL_W, -(MAP_CELL_W-1), 1, MAP_CELL_W+1, MAP_CELL_W, MAP_CELL_W-1, -1, -(MAP_CELL_W+1), REFRESH_EOL} + }; + static short _manual[10]; +//; 00 = on axis +//; 01 = below axis +//; 10 = above axis +//; 11 = undefined + static char const _SpillTable[16] = {8,6,2,-1,0,7,1,-1,4,5,3,-1,-1,-1,-1,-1}; + int index=0; + int x,y; + + /* + ** For mondo-enourmo-gigundo objects, use a prebuilt mammoth table + ** that covers a 5x5 square region. + */ + if (maxsize > ICON_PIXEL_W * 2) { + static short const _gigundo[] = { + -((2*MAP_CELL_W)-2),-((2*MAP_CELL_W)-1),-((2*MAP_CELL_W)),-((2*MAP_CELL_W)+1),-((2*MAP_CELL_W)+2), + -((1*MAP_CELL_W)-2),-((1*MAP_CELL_W)-1),-((1*MAP_CELL_W)),-((1*MAP_CELL_W)+1),-((1*MAP_CELL_W)+2), + -((0*MAP_CELL_W)-2),-((0*MAP_CELL_W)-1),-((0*MAP_CELL_W)),-((0*MAP_CELL_W)+1),-((0*MAP_CELL_W)+2), + ((1*MAP_CELL_W)-2),((1*MAP_CELL_W)-1),((1*MAP_CELL_W)),((1*MAP_CELL_W)+1),((1*MAP_CELL_W)+2), + +((2*MAP_CELL_W)-2),+((2*MAP_CELL_W)-1),+((2*MAP_CELL_W)),+((2*MAP_CELL_W)+1),+((2*MAP_CELL_W)+2), + REFRESH_EOL + }; + return(&_gigundo[0]); + } + + /* + ** For very large objects, build the overlap list by hand. This is time consuming, but + ** not nearly as time consuming as drawing even a single cell unnecessarily. + */ + if (maxsize > ICON_PIXEL_W) { + maxsize = MIN(maxsize, (ICON_PIXEL_W*2))/2; + + x = Fixed_To_Cardinal(ICON_PIXEL_W, Coord_XLepton(coord)); + y = Fixed_To_Cardinal(ICON_PIXEL_H, Coord_YLepton(coord)); + int left = x-maxsize; + int right = x+maxsize; + int top = y-maxsize; + int bottom = y+maxsize; + + _manual[index++] = 0; + if (left < 0) _manual[index++] = -1; + if (right >= ICON_PIXEL_W) _manual[index++] = 1; + if (top < 0) _manual[index++] = -MAP_CELL_W; + if (bottom >= ICON_PIXEL_H) _manual[index++] = MAP_CELL_W; + if (left < 0 && top < 0) _manual[index++] = -(MAP_CELL_W+1); + if (right >= ICON_PIXEL_W && bottom >= ICON_PIXEL_H) _manual[index++] = MAP_CELL_W+1; + if (left < 0 && bottom >= ICON_PIXEL_H) _manual[index++] = MAP_CELL_W-1; + if (right >= ICON_PIXEL_H && top < 0) _manual[index++] = -(MAP_CELL_W-1); + _manual[index] = REFRESH_EOL; + return(&_manual[0]); + } + + /* + ** Determine the number of leptons "leeway" allowed this unit. + */ + int posval = Pixel2Lepton[(ICON_PIXEL_W-maxsize)/2]; + + x = Coord_XLepton(coord) - 0x0080; + y = Coord_YLepton(coord) - 0x0080; + if (y > posval) index |= 0x08; // Spilling South. + if (y < -posval) index |= 0x04; // Spilling North. + if (x > posval) index |= 0x02; // Spilling East. + if (x < -posval) index |= 0x01; // Spilling West. + + return(&_MoveSpillage[_SpillTable[index]][0]); +} + + +/*********************************************************************************************** + * Coord_Move -- Moves a coordinate an arbitrary direction for an arbitrary distance * + * * + * This function will move a coordinate in a using SIN and COS arithmetic. * + * * + * INPUT: start -- The starting coordinate. * + * * + * dir -- The direction to move the coordinate. * + * * + * distance -- The distance to move the coordinate position (in leptons). * + * * + * OUTPUT: Returns the new coordinate position. * + * * + * WARNINGS: This routine uses multiplies -- use with caution. * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +COORDINATE Coord_Move(COORDINATE start, register DirType dir, unsigned short distance) +{ + short x = Coord_X(start); + short y = Coord_Y(start); + + Move_Point(x, y, dir, distance); + return(XY_Coord(x,y)); +#ifdef NEVER + static char const CosTable[256] = { + 0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x15, + 0x18,0x1b,0x1e,0x21,0x24,0x27,0x2a,0x2d, + 0x30,0x33,0x36,0x39,0x3b,0x3e,0x41,0x43, + 0x46,0x49,0x4b,0x4e,0x50,0x52,0x55,0x57, + 0x59,0x5b,0x5e,0x60,0x62,0x64,0x65,0x67, + 0x69,0x6b,0x6c,0x6e,0x6f,0x71,0x72,0x74, + 0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7b, + 0x7c,0x7d,0x7d,0x7e,0x7e,0x7e,0x7e,0x7e, + + 0x7f,0x7e,0x7e,0x7e,0x7e,0x7e,0x7d,0x7d, + 0x7c,0x7b,0x7b,0x7a,0x79,0x78,0x77,0x76, + 0x75,0x74,0x72,0x71,0x70,0x6e,0x6c,0x6b, + 0x69,0x67,0x66,0x64,0x62,0x60,0x5e,0x5b, + 0x59,0x57,0x55,0x52,0x50,0x4e,0x4b,0x49, + 0x46,0x43,0x41,0x3e,0x3b,0x39,0x36,0x33, + 0x30,0x2d,0x2a,0x27,0x24,0x21,0x1e,0x1b, + 0x18,0x15,0x12,0x0f,0x0c,0x09,0x06,0x03, + + 0x00,0xfd,0xfa,0xf7,0xf4,0xf1,0xee,0xeb, + 0xe8,0xe5,0xe2,0xdf,0xdc,0xd9,0xd6,0xd3, + 0xd0,0xcd,0xca,0xc7,0xc5,0xc2,0xbf,0xbd, + 0xba,0xb7,0xb5,0xb2,0xb0,0xae,0xab,0xa9, + 0xa7,0xa5,0xa2,0xa0,0x9e,0x9c,0x9a,0x99, + 0x97,0x95,0x94,0x92,0x91,0x8f,0x8e,0x8c, + 0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x85, + 0x84,0x83,0x83,0x82,0x82,0x82,0x82,0x82, + + 0x82,0x82,0x82,0x82,0x82,0x82,0x83,0x83, + 0x84,0x85,0x85,0x86,0x87,0x88,0x89,0x8a, + 0x8b,0x8c,0x8e,0x8f,0x90,0x92,0x94,0x95, + 0x97,0x99,0x9a,0x9c,0x9e,0xa0,0xa2,0xa5, + 0xa7,0xa9,0xab,0xae,0xb0,0xb2,0xb5,0xb7, + 0xba,0xbd,0xbf,0xc2,0xc5,0xc7,0xca,0xcd, + 0xd0,0xd3,0xd6,0xd9,0xdc,0xdf,0xe2,0xe5, + 0xe8,0xeb,0xee,0xf1,0xf4,0xf7,0xfa,0xfd, + }; + + static char const SinTable[256] = { + 0x7f,0x7e,0x7e,0x7e,0x7e,0x7e,0x7d,0x7d, + 0x7c,0x7b,0x7b,0x7a,0x79,0x78,0x77,0x76, + 0x75,0x74,0x72,0x71,0x70,0x6e,0x6c,0x6b, + 0x69,0x67,0x66,0x64,0x62,0x60,0x5e,0x5b, + 0x59,0x57,0x55,0x52,0x50,0x4e,0x4b,0x49, + 0x46,0x43,0x41,0x3e,0x3b,0x39,0x36,0x33, + 0x30,0x2d,0x2a,0x27,0x24,0x21,0x1e,0x1b, + 0x18,0x15,0x12,0x0f,0x0c,0x09,0x06,0x03, + + 0x00,0xfd,0xfa,0xf7,0xf4,0xf1,0xee,0xeb, + 0xe8,0xe5,0xe2,0xdf,0xdc,0xd9,0xd6,0xd3, + 0xd0,0xcd,0xca,0xc7,0xc5,0xc2,0xbf,0xbd, + 0xba,0xb7,0xb5,0xb2,0xb0,0xae,0xab,0xa9, + 0xa7,0xa5,0xa2,0xa0,0x9e,0x9c,0x9a,0x99, + 0x97,0x95,0x94,0x92,0x91,0x8f,0x8e,0x8c, + 0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x85, + 0x84,0x83,0x83,0x82,0x82,0x82,0x82,0x82, + + 0x82,0x82,0x82,0x82,0x82,0x82,0x83,0x83, + 0x84,0x85,0x85,0x86,0x87,0x88,0x89,0x8a, + 0x8b,0x8c,0x8e,0x8f,0x90,0x92,0x94,0x95, + 0x97,0x99,0x9a,0x9c,0x9e,0xa0,0xa2,0xa5, + 0xa7,0xa9,0xab,0xae,0xb0,0xb2,0xb5,0xb7, + 0xba,0xbd,0xbf,0xc2,0xc5,0xc7,0xca,0xcd, + 0xd0,0xd3,0xd6,0xd9,0xdc,0xdf,0xe2,0xe5, + 0xe8,0xeb,0xee,0xf1,0xf4,0xf7,0xfa,0xfd, + + 0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x15, + 0x18,0x1b,0x1e,0x21,0x24,0x27,0x2a,0x2d, + 0x30,0x33,0x36,0x39,0x3b,0x3e,0x41,0x43, + 0x46,0x49,0x4b,0x4e,0x50,0x52,0x55,0x57, + 0x59,0x5b,0x5e,0x60,0x62,0x64,0x65,0x67, + 0x69,0x6b,0x6c,0x6e,0x6f,0x71,0x72,0x74, + 0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7b, + 0x7c,0x7d,0x7d,0x7e,0x7e,0x7e,0x7e,0x7e, + }; + distance = distance; // Keep LINT quiet. + + /* + ** Calculate and add in the X component of the move. + */ + _AX = CosTable[dir]; + asm imul word ptr distance + asm shl ax,1 + asm rcl dx,1 + asm mov al,ah + asm mov ah,dl + _DX = _AX; + *((int*)&start) += _DX; +// asm add [word ptr start],ax + + /* + ** Calculate and add in the Y component of the move. + */ + _AX = SinTable[dir]; + asm imul word ptr distance + asm shl ax,1 + asm rcl dx,1 + asm mov al,ah + asm mov ah,dl + asm neg ax // Subtraction needed because of inverted sine table. + _DX = _AX; + *(((int*)&start)+1) += _DX; +// asm add [word ptr start+2],ax + + return(start); +#endif +} + +#ifdef OBSOLETE +//BG: Note, this routine is replaced by assembly in COORDA.ASM +/*********************************************************************************************** + * Cardinal_To_Fixed -- Converts cardinal numbers into a fixed point number. * + * * + * This utility function will convert cardinal numbers into a fixed point fraction. The * + * use of fixed point numbers occurs throughout the product -- since it is a convenient * + * tool. The fixed point number is based on the formula: * + * * + * result = cardinal / base * + * * + * The accuracy of the fixed point number is limited to 1/256 as the lowest and up to * + * 256 as the largest. * + * * + * INPUT: base -- The key number to base the fraction about. * + * * + * cardinal -- The other number (hey -- what do you call it?) * + * * + * OUTPUT: Returns with the fixed point number of the "cardinal" parameter as it relates * + * to the "base" parameter. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +unsigned Cardinal_To_Fixed(unsigned base, unsigned cardinal) +{ + long temp = 0; + + if (base) { + *(unsigned*)(((char*)&temp)+1) = cardinal; // Shift up by 8 bits. + _DX = FP_SEG(temp); + _AX = FP_OFF(temp); + asm div word ptr base + return(_AX); + } + return(0xFFFF); +} +#endif + +#ifdef OBSOLETE +//BG: Note, this routine is replaced by assembly in COORDA.ASM +/*********************************************************************************************** + * Fixed_To_Cardinal -- Converts a fixed point number into a cardinal number. * + * * + * Use this routine to convert a fixed point number into a cardinal number. * + * * + * INPUT: base -- The base number that the original fixed point number was created from. * + * * + * fixed -- The fixed point number to convert. * + * * + * OUTPUT: Returns with the reconverted number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +unsigned Fixed_To_Cardinal(unsigned base, unsigned fixed) +{ + unsigned long temp; + + _AX = base; + asm mul word ptr fixed + _CX = _AX; + temp = ((unsigned long)MK_FP(_DX, _CX)) + 0x80; + if ( *((char*)&temp+3) ) { + return(0xFFFF); + } + return(*(unsigned*)(((char*)&temp)+1)); +} +#endif + + +/*********************************************************************************************** + * Coord_Scatter -- Determines a random coordinate from an anchor point. * + * * + * This routine will perform a scatter algorithm on the specified * + * anchor point in order to return with another coordinate that is * + * randomly nearby the original. Typical use of this would be for * + * missile targeting. * + * * + * INPUT: coord -- This is the anchor coordinate. * + * * + * distance -- This is the distance in pixels that the scatter * + * should fall within. * + * * + * lock -- bool; Convert the new coordinate into a center * + * cell based coordinate? * + * * + * OUTPUT: Returns with a new coordinate that is nearby the original. * + * * + * WARNINGS: Maximum pixel scatter distance is 255. * + * * + * HISTORY: * + * 02/01/1992 JLB : Created. * + * 05/13/1992 JLB : Only uses Random(). * + *=============================================================================================*/ +COORDINATE Coord_Scatter(COORDINATE coord, unsigned distance, bool lock) +{ + COORDINATE newcoord; + + newcoord = Coord_Move(coord, Random_Pick(DIR_N, DIR_MAX), distance); + + if (newcoord & 0xC000C000L) newcoord = coord; + + if (lock) { + newcoord = Coord_Snap(newcoord); + } + + return(newcoord); +} + +extern int calcx(signed short, short distance); +#pragma aux calcx parm [ax] [bx] \ + modify [eax dx] \ + value [eax] = \ + "imul bx" \ + "shl ax,1" \ + "rcl dx,1" \ + "mov al,ah" \ + "mov ah,dl" \ + "cwd" \ +// "and eax,0FFFFh"; + +extern int calcy(signed short, short distance); +#pragma aux calcy parm [ax] [bx] \ + modify [eax dx] \ + value [eax] = \ + "imul bx" \ + "shl ax,1" \ + "rcl dx,1" \ + "mov al,ah" \ + "mov ah,dl" \ + "cwd" \ + "neg eax"; +// "and eax,0FFFFh" \ + +void Move_Point(short &x, short &y, register DirType dir, unsigned short distance) +{ +// static char const CosTable[256] = { + static char const CosTable[256] = { + 0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x15, + 0x18,0x1b,0x1e,0x21,0x24,0x27,0x2a,0x2d, + 0x30,0x33,0x36,0x39,0x3b,0x3e,0x41,0x43, + 0x46,0x49,0x4b,0x4e,0x50,0x52,0x55,0x57, + 0x59,0x5b,0x5e,0x60,0x62,0x64,0x65,0x67, + 0x69,0x6b,0x6c,0x6e,0x6f,0x71,0x72,0x74, + 0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7b, + 0x7c,0x7d,0x7d,0x7e,0x7e,0x7e,0x7e,0x7e, + + 0x7f,0x7e,0x7e,0x7e,0x7e,0x7e,0x7d,0x7d, + 0x7c,0x7b,0x7b,0x7a,0x79,0x78,0x77,0x76, + 0x75,0x74,0x72,0x71,0x70,0x6e,0x6c,0x6b, + 0x69,0x67,0x66,0x64,0x62,0x60,0x5e,0x5b, + 0x59,0x57,0x55,0x52,0x50,0x4e,0x4b,0x49, + 0x46,0x43,0x41,0x3e,0x3b,0x39,0x36,0x33, + 0x30,0x2d,0x2a,0x27,0x24,0x21,0x1e,0x1b, + 0x18,0x15,0x12,0x0f,0x0c,0x09,0x06,0x03, + + 0x00,0xfd,0xfa,0xf7,0xf4,0xf1,0xee,0xeb, + 0xe8,0xe5,0xe2,0xdf,0xdc,0xd9,0xd6,0xd3, + 0xd0,0xcd,0xca,0xc7,0xc5,0xc2,0xbf,0xbd, + 0xba,0xb7,0xb5,0xb2,0xb0,0xae,0xab,0xa9, + 0xa7,0xa5,0xa2,0xa0,0x9e,0x9c,0x9a,0x99, + 0x97,0x95,0x94,0x92,0x91,0x8f,0x8e,0x8c, + 0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x85, + 0x84,0x83,0x83,0x82,0x82,0x82,0x82,0x82, + + 0x82,0x82,0x82,0x82,0x82,0x82,0x83,0x83, + 0x84,0x85,0x85,0x86,0x87,0x88,0x89,0x8a, + 0x8b,0x8c,0x8e,0x8f,0x90,0x92,0x94,0x95, + 0x97,0x99,0x9a,0x9c,0x9e,0xa0,0xa2,0xa5, + 0xa7,0xa9,0xab,0xae,0xb0,0xb2,0xb5,0xb7, + 0xba,0xbd,0xbf,0xc2,0xc5,0xc7,0xca,0xcd, + 0xd0,0xd3,0xd6,0xd9,0xdc,0xdf,0xe2,0xe5, + 0xe8,0xeb,0xee,0xf1,0xf4,0xf7,0xfa,0xfd, + }; + +// static char const SinTable[256] = { + static char const SinTable[256] = { + 0x7f,0x7e,0x7e,0x7e,0x7e,0x7e,0x7d,0x7d, + 0x7c,0x7b,0x7b,0x7a,0x79,0x78,0x77,0x76, + 0x75,0x74,0x72,0x71,0x70,0x6e,0x6c,0x6b, + 0x69,0x67,0x66,0x64,0x62,0x60,0x5e,0x5b, + 0x59,0x57,0x55,0x52,0x50,0x4e,0x4b,0x49, + 0x46,0x43,0x41,0x3e,0x3b,0x39,0x36,0x33, + 0x30,0x2d,0x2a,0x27,0x24,0x21,0x1e,0x1b, + 0x18,0x15,0x12,0x0f,0x0c,0x09,0x06,0x03, + + 0x00,0xfd,0xfa,0xf7,0xf4,0xf1,0xee,0xeb, + 0xe8,0xe5,0xe2,0xdf,0xdc,0xd9,0xd6,0xd3, + 0xd0,0xcd,0xca,0xc7,0xc5,0xc2,0xbf,0xbd, + 0xba,0xb7,0xb5,0xb2,0xb0,0xae,0xab,0xa9, + 0xa7,0xa5,0xa2,0xa0,0x9e,0x9c,0x9a,0x99, + 0x97,0x95,0x94,0x92,0x91,0x8f,0x8e,0x8c, + 0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x85, + 0x84,0x83,0x83,0x82,0x82,0x82,0x82,0x82, + + 0x82,0x82,0x82,0x82,0x82,0x82,0x83,0x83, + 0x84,0x85,0x85,0x86,0x87,0x88,0x89,0x8a, + 0x8b,0x8c,0x8e,0x8f,0x90,0x92,0x94,0x95, + 0x97,0x99,0x9a,0x9c,0x9e,0xa0,0xa2,0xa5, + 0xa7,0xa9,0xab,0xae,0xb0,0xb2,0xb5,0xb7, + 0xba,0xbd,0xbf,0xc2,0xc5,0xc7,0xca,0xcd, + 0xd0,0xd3,0xd6,0xd9,0xdc,0xdf,0xe2,0xe5, + 0xe8,0xeb,0xee,0xf1,0xf4,0xf7,0xfa,0xfd, + + 0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x15, + 0x18,0x1b,0x1e,0x21,0x24,0x27,0x2a,0x2d, + 0x30,0x33,0x36,0x39,0x3b,0x3e,0x41,0x43, + 0x46,0x49,0x4b,0x4e,0x50,0x52,0x55,0x57, + 0x59,0x5b,0x5e,0x60,0x62,0x64,0x65,0x67, + 0x69,0x6b,0x6c,0x6e,0x6f,0x71,0x72,0x74, + 0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7b, + 0x7c,0x7d,0x7d,0x7e,0x7e,0x7e,0x7e,0x7e, + }; + distance = distance; // Keep LINT quiet. + +#ifdef OBSOLETE + /* + ** Calculate and add in the X component of the move. + */ + _AX = CosTable[dir]; + asm imul word ptr distance + asm shl ax,1 + asm rcl dx,1 + asm mov al,ah + asm mov ah,dl + _DX = _AX; + x += _DX; +#else + x += calcx(CosTable[dir], distance); +#endif +// asm add [word ptr start],ax + +#ifdef OBSOLETE + /* + ** Calculate and add in the Y component of the move. + */ + _AX = SinTable[dir]; + asm imul word ptr distance + asm shl ax,1 + asm rcl dx,1 + asm mov al,ah + asm mov ah,dl + asm neg ax // Subtraction needed because of inverted sine table. + _DX = _AX; + y += _DX; +#else + y += calcy(SinTable[dir], distance); +#endif +// asm add [word ptr start+2],ax + +} diff --git a/COORDA.ASM b/COORDA.ASM new file mode 100644 index 0000000..3d31d1e --- /dev/null +++ b/COORDA.ASM @@ -0,0 +1,134 @@ +; +; Command & Conquer(tm) +; Copyright 2025 Electronic Arts Inc. +; +; 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 3 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 General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . +; + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S I N C ** +;*************************************************************************** +;* * +;* Project Name : Command & Conquer * +;* * +;* File Name : COORDA.ASM * +;* * +;* Programmer : Barry W. Green * +;* * +;* Start Date : February 17, 1995 * +;* * +;* Last Update : February 17, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Cardinal_To_Fixed -- Converts cardinal numbers into a fixed point number. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +global C Cardinal_To_Fixed :NEAR +global C Fixed_To_Cardinal :NEAR + + CODESEG + +;*********************************************************************************************** +;* Cardinal_To_Fixed -- Converts cardinal numbers into a fixed point number. * +;* * +;* This utility function will convert cardinal numbers into a fixed point fraction. The * +;* use of fixed point numbers occurs throughout the product -- since it is a convenient * +;* tool. The fixed point number is based on the formula: * +;* * +;* result = cardinal / base * +;* * +;* The accuracy of the fixed point number is limited to 1/256 as the lowest and up to * +;* 256 as the largest. * +;* * +;* INPUT: base -- The key number to base the fraction about. * +;* * +;* cardinal -- The other number (hey -- what do you call it?) * +;* * +;* OUTPUT: Returns with the fixed point number of the "cardinal" parameter as it relates * +;* to the "base" parameter. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 02/17/1995 BWG : Created. * +;*=============================================================================================*/ +;unsigned int Cardinal_To_Fixed(unsigned base, unsigned cardinal); + + PROC Cardinal_To_Fixed C near + USES ebx, edx + + ARG base:DWORD + ARG cardinal:DWORD + + mov eax,0FFFFh ; establish default return value + + mov ebx,[base] + or ebx,ebx + jz near ??retneg1 ; if base==0, return 65535 + + mov eax,[cardinal] ; otherwise, return (cardinal*256)/base + shl eax,8 + xor edx,edx + div ebx + +??retneg1: + ret + + ENDP Cardinal_To_Fixed + + +;*********************************************************************************************** +;* Fixed_To_Cardinal -- Converts a fixed point number into a cardinal number. * +;* * +;* Use this routine to convert a fixed point number into a cardinal number. * +;* * +;* INPUT: base -- The base number that the original fixed point number was created from. * +;* * +;* fixed -- The fixed point number to convert. * +;* * +;* OUTPUT: Returns with the reconverted number. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 02/17/1995 BWG : Created. * +;*=============================================================================================*/ +;unsigned int Fixed_To_Cardinal(unsigned base, unsigned fixed); + PROC Fixed_To_Cardinal C near + USES edx + + ARG base:DWORD + ARG fixed:DWORD + + mov eax,[base] + mul [fixed] + add eax,080h ; eax = (base * fixed) + 0x80 + + test eax,0FF000000h ; if high byte set, return FFFF + jnz ??rneg1 + shr eax,8 ; else, return eax/256 + ret +??rneg1 : + mov eax,0FFFFh ; establish default return value + ret + + ENDP Fixed_To_Cardinal + + END diff --git a/CREDITS.CPP b/CREDITS.CPP new file mode 100644 index 0000000..774afc2 --- /dev/null +++ b/CREDITS.CPP @@ -0,0 +1,176 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\credits.cpv 2.17 16 Oct 1995 16:51:28 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CREDITS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 17, 1994 * + * * + * Last Update : March 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CreditClass::AI -- Handles updating the credit display. * + * CreditClass::Graphic_Logic -- Handles the credit redraw logic. * + * CreditClass::CreditClass -- Default constructor for the credit class object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * CreditClass::CreditClass -- Default constructor for the credit class object. * + * * + * This is the constructor for the credit class object. It merely sets the credit display * + * state to null. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +CreditClass::CreditClass(void) +{ + IsToRedraw = false; + IsUp = false; + IsAudible = false; + Credits = 0; + Current = 0; + Countdown = 0; +} + + +/*********************************************************************************************** + * CreditClass::Graphic_Logic -- Handles the credit redraw logic. * + * * + * This routine should be called whenever the main game screen is to be updated. It will * + * check to see if the credit display should be redrawn. If so, it will redraw it. * + * * + * INPUT: forced -- Should the credit display be redrawn regardless of whether the redraw * + * flag is set? This is typically the case when the screen needs to be * + * redrawn from scratch. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +#define XX (320-120) +#define WW 50 +void CreditClass::Graphic_Logic(bool forced) +{ + int factor = Get_Resolution_Factor(); + int xx = SeenBuff.Get_Width() - (120 << factor); + if (forced || IsToRedraw) { + + /* + ** Play a sound effect when the money display changes, but only if a sound + ** effect was requested. + */ + if (IsAudible) { + if (IsUp) { + Sound_Effect(VOC_UP, VOL_1); + } else { + Sound_Effect(VOC_DOWN, VOL_1); + } + } + + /* + ** Display the new current value. + */ + //LogicPage->Fill_Rect(xx-(20 << factor), 1 << factor, xx+(20 << factor), 6 << factor, LTGREY); + TabClass::Draw_Credits_Tab(); + Fancy_Text_Print("%ld", xx, 0, 11, TBLACK, TPF_GREEN12_GRAD|TPF_CENTER | TPF_USE_GRAD_PAL, Current); + + IsToRedraw = false; + IsAudible = false; + } +} + + +/*********************************************************************************************** + * CreditClass::AI -- Handles updating the credit display. * + * * + * This routine handles the logic that controls the rate of credit change in the credit * + * display. It doesn't actually redraw the credit display, but will flag it to be redrawn * + * if it detects that a change is to occur. * + * * + * INPUT: forced -- Should the credit display immediately reflect the current credit * + * total for the player? This is usually desired when initially loading * + * a scenario or saved game. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +void CreditClass::AI(bool forced) +{ + Credits = PlayerPtr->Available_Money(); + + /* + ** Make sure that the credit counter doesn't drop below zero. + */ + Credits = MAX(Credits, 0L); + + if (Current == Credits) return; + + if (forced) { + IsAudible = false; + Current = Credits; + } else { + + if (Countdown) Countdown--; + if (Countdown) return; + + /* + ** Determine the amount to change the display toward the + ** desired value. + */ + long adder = Credits - Current; + adder = ABS(adder); + adder >>= 5; + adder = Bound(adder, 1L, 71+72); + if (Current > Credits) adder = -adder; + Current += adder; + Countdown = 1; + + if (Current-adder != Current) { + IsAudible = true; + IsUp = (adder > 0); + } + } + IsToRedraw = true; + Map.Flag_To_Redraw(false); +} diff --git a/CREDITS.H b/CREDITS.H new file mode 100644 index 0000000..b08c46c --- /dev/null +++ b/CREDITS.H @@ -0,0 +1,72 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\credits.h_v 2.18 16 Oct 1995 16:47:26 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CREDIT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 19, 1994 * + * * + * Last Update : April 19, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CREDITS_H +#define CREDITS_H + +/**************************************************************************** +** The animating credit counter display is controlled by this class. +*/ +class CreditClass { + public: + long Credits; // Value of credits trying to update display to. + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + CreditClass(void); + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + void Update(bool forced=false, bool redraw=false); + + void Graphic_Logic(bool forced=false); + void AI(bool forced=false); + + long Current; // Credit value currently displayed. + + unsigned IsToRedraw:1; + unsigned IsUp:1; + unsigned IsAudible:1; + + private: + int Countdown; // Delay between ticks. +}; + +#endif + diff --git a/CREW.CPP b/CREW.CPP new file mode 100644 index 0000000..18bf6fc --- /dev/null +++ b/CREW.CPP @@ -0,0 +1,38 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\crew.cpv 2.18 16 Oct 1995 16:50:50 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CREW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" diff --git a/CREW.H b/CREW.H new file mode 100644 index 0000000..0b9bac8 --- /dev/null +++ b/CREW.H @@ -0,0 +1,65 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\crew.h_v 2.18 16 Oct 1995 16:47:56 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CREW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CREW_H +#define CREW_H + + +/**************************************************************************** +** This class handles the basic crew logic. This includes hero tracking, +** crew bail-out, and attached object logic. +*/ +class CrewClass +{ + public: + /* + ** This keeps track of the number of "kills" the unit as accumulated. + ** When it reaches a certain point, the unit improves. + */ + unsigned short Kills; + + /* + ** Constructors, Destructors, and overloaded operators. + */ + CrewClass(void) {Kills = 0;}; + + int Made_A_Kill(void) {Kills++;return(Kills);}; + + private: +}; + +#endif diff --git a/CWSTUB.C b/CWSTUB.C new file mode 100644 index 0000000..0834d8d --- /dev/null +++ b/CWSTUB.C @@ -0,0 +1,71 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include + +char *dos4g_path() +{ + static char *paths_to_check[] = { + "DOS4GPATH", + "PATH" + }; + static char fullpath[80]; + char *dos4gpath; + int i; + + /* If DOS4GPATH points to an executable file name, don't bother + searching any paths for DOS4GW.EXE. + */ + if (dos4gpath = getenv("DOS4GPATH")) { + strlwr(strcpy(fullpath, dos4gpath)); + if (strstr(fullpath, ".exe")) { + return(fullpath); + } + } + for( i = 0; i < sizeof(paths_to_check) / sizeof(paths_to_check[0]); i++ ) { + _searchenv("dos4gw.exe", paths_to_check[i], fullpath); + if (fullpath[0]) { + return( &fullpath ); + } + } + return("dos4gw.exe"); +} + +main( int argc, char *argv[] ) +{ + char *av[4]; + auto char cmdline[128]; + + av[0] = dos4g_path(); /* Locate the DOS/4GW loader */ + av[1] = argv[0]; /* name of executable to run */ + av[2] = getcmd(cmdline); /* command line */ + av[3] = NULL; /* end of list */ +#ifdef VMM + putenv("DOS4GVM=MINMEM#2000 MAXMEM#16000 SWAPMIN#4096 SWAPINC#1024 VIRTUALSIZE#10000 SWAPFILE#CONQUER.SWP DELETESWAP @CONQUER.VMC"); +#endif +#ifdef QUIET + putenv("DOS4G=QUIET"); /* disables DOS/4GW Copyright banner */ +#endif + execvp(av[0], av); + perror(av[0]); + exit(1); /* indicate error */ +} diff --git a/DDE.CPP b/DDE.CPP new file mode 100644 index 0000000..5fbdebe --- /dev/null +++ b/DDE.CPP @@ -0,0 +1,450 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Dynamic Data Encapsulation * + * * + * File Name : DDE.CPP * + * * + * Programmer : Steve Wetherill * + * * + * Start Date : June 1, 1996 * + * * + * Last Update : June 8, 1996 [SW] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Instance_Class::InstanceClass -- class constructor * + * Instance_Class::InstanceClass -- class destructor * + * Instance_Class::Enable_Callback -- enables local processing of pokes * + * Instance_Class::Register_Servers -- registers a local DDE DNS service * + * Instance_Class::Cleanup_App -- currently does nothing * + * Instance_Class::Test_Server_Running -- does a trial connect to remote * + * Instance_Class::Open_Poke_Connection -- pokes some data to server * + * Instance_Class::Close_Poke_Connectionp -- closes connection to remote * + * Instance_Class::Poke_Server -- sends a chunk of data to remote * + * Instance_Class::dde_callback -- processes DDE transactions * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#define WIN32 +#include +#include "dde.h" + +/*************************************************************************** + * These are static members of Instance_Class + *=========================================================================*/ + +static DWORD Instance_Class::id_inst; // instance identifier set by DdeInitialize +static BOOL Instance_Class::process_pokes; // controls response to pokes +static char Instance_Class::ascii_name[32]; // name of server + +static BOOL CALLBACK (*Instance_Class::callback) ( + LPBYTE pointer, // pointer to received data + long length // length of received data or advisory flag + ) = NULL; + +/*************************************************************************** + * Instance_Class::InstanceClass -- class constructor * + * * + * INPUT: * + * name1 null terminated ASCII client name * + * name1 null terminated ASCII server name * + * * + * OUTPUT: * + * dde_error = TRUE if error occurs when initializing DDE * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +Instance_Class::Instance_Class( LPSTR name1, LPSTR name2 ) +{ + + dde_error = FALSE; // no errors + process_pokes = FALSE; // disable pokes in callback + + id_inst = 0; // set to 0 for first time through + conv_handle = 0; // conversation handle reset + + lstrcpy( ascii_name, name1 ); // keep a record of ASCII name + + if ( DdeInitialize( + (LPDWORD) &id_inst, // instance identifier + dde_callback, + APPCLASS_STANDARD | // filter server messages + CBF_FAIL_SELFCONNECTIONS, // prevent from connecting with self + 0) != DMLERR_NO_ERROR) { // reserved + dde_error = TRUE; // flag an error + } + + local_name = DdeCreateStringHandle( + id_inst, // instance identifier + name1, // string to register + CP_WINANSI); // Windows ANSI code page + + remote_name = DdeCreateStringHandle( + id_inst, // instance identifier + name2, // string to register + CP_WINANSI); // Windows ANSI code page + + poke_topic = DdeCreateStringHandle( + id_inst, // instance identifier + "POKE TOPIC", // System topic + CP_WINANSI); // Windows ANSI code page + + poke_item = DdeCreateStringHandle( + id_inst, // instance identifier + "POKE ITEM", // System topic + CP_WINANSI); // Windows ANSI code page + + system_topic = DdeCreateStringHandle( + id_inst, // instance identifier + SZDDESYS_TOPIC, // System topic + CP_WINANSI); // Windows ANSI code page + +} + +/*************************************************************************** + * Instance_Class::~Instance_Class -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +Instance_Class::~Instance_Class() +{ + DdeUninitialize( id_inst ); +} + +/*************************************************************************** + * Instance_Class::Enable_Callback -- enables user callback * + * * + * INPUT: * + * TRUE = enable poke processing * + * FALSE = disable poke processing * + * * + * OUTPUT: * + * echos the input * + * * + * WARNINGS: * + * user callback must be explicitly enabled. Disbabled by default. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +BOOL Instance_Class::Enable_Callback( BOOL flag ) // enable or disable callback +{ + return (process_pokes = flag); + +} + +/*************************************************************************** + * Instance_Class::Register_Server -- registers a local DDE DNS service * + * * + * INPUT: * + * BOOL CALLBACK ( *callback_fnc) ( LPBYTE, DWORD) = user poke callbacl * + * * + * OUTPUT: * + * TRUE == success * + * FALSE == failed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +BOOL Instance_Class::Register_Server( BOOL CALLBACK ( *callback_fnc) (LPBYTE, long) ) +{ + + if (DdeNameService( id_inst, local_name, 0L, DNS_REGISTER ) != 0L) { + callback = callback_fnc; + return ( TRUE ); + } else { + return ( FALSE ); + } +} + +/*************************************************************************** + * Instance_Class::Test_Server_Running -- does a trial connect to remote * + * * + * INPUT: * + * name = HSZ string handle of server name. * + * * + * OUTPUT: * + * TRUE == successfully connected to remote * + * FALSE == failed to connect * + * * + * WARNINGS: * + * - Can be called for local or remote server but of course will * + * fail if a called for local and local server is not "up". * + * - Disconects before exiting. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +BOOL Instance_Class::Test_Server_Running( HSZ name ) +{ + + if( Open_Poke_Connection( name ) == TRUE) { + Close_Poke_Connection(); + return( TRUE ); + } else { + return( FALSE ); + } +} + +/*************************************************************************** + * Instance_Class::Open_Poke_Connection -- open a connection to server * + * * + * INPUT: * + * name = HSZ server name. * + * * + * OUTPUT: * + * TRUE == successfully opened connection * + * FALSE == failed to connect * + * * + * WARNINGS: * + * Can be called for local or remote server but of course will * + * fail if a called for local and local server is not "up". * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +BOOL Instance_Class::Open_Poke_Connection( HSZ name ) +{ + conv_handle = DdeConnect( + id_inst, // instance identifier + name, // service name string handle + poke_topic, // topic string handle + (PCONVCONTEXT) NULL);// use default context + + if (conv_handle == NULL) { + return FALSE; + } else { + return TRUE; + } +} + +/*************************************************************************** + * Instance_Class::Close_Poke_Connection -- closes poke connection * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * TRUE == successfully closed connection * + * FALSE == failed to close connection for some reason * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +BOOL Instance_Class::Close_Poke_Connection( void ) +{ + if( conv_handle ) { + HCONV temp_handle = conv_handle; + conv_handle = NULL; + return( DdeDisconnect( temp_handle )); + } else { + return( TRUE ); + } +} + +/*************************************************************************** + * Instance_Class::Poke_Server -- pokes some data to server * + * * + * INPUT: * + * poke_data points to data to send to remote * + * poke_length length of buffer to send * + * * + * OUTPUT: * + * TRUE == successfully poked the data * + * FALSE == failed to connect * + * * + * WARNINGS: * + * has a 3 second timeout (change POKE_TIMEOUT, in milliseconds) * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +#define POKE_TIMEOUT 60*1000 // 60 sec timeout + +BOOL Instance_Class::Poke_Server( LPBYTE poke_data, DWORD poke_length ) +{ + + if( DdeClientTransaction( + + poke_data, // address of data to pass to server + poke_length, // length of data + conv_handle, // handle of conversation + poke_topic, // handle of item name string + CF_TEXT, // no special clipboard data format + XTYP_POKE, // transaction type + POKE_TIMEOUT, // time-out duration (millisecs) + (LPDWORD) NULL // address of transaction result (don't check) + ) == 0) { + + return( FALSE); + } else { + return( TRUE ); + } +} + +/*************************************************************************** + * Instance_Class::dde_callback -- callback dde event handler * + * * + * INPUT: * + * dde_event transaction type * + * uFmt clipboard data format * + * hconv handle of the conversation * + * hsz1 handle of a string * + * hsz2 handle of a string * + * hdata handle of a global memory object * + * dwData1 transaction-specific data * + * dwData2 transaction-specific data * + * * + * OUTPUT: * + * context specific HDDEDATA object * + * * + * WARNINGS: * + * NOTE: declared as HDDEDATA CALLBACK which means PASCAL parameters * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +HDDEDATA CALLBACK Instance_Class::dde_callback( + + UINT dde_event, // transaction type + UINT uFmt, // clipboard data format + HCONV , // handle of the conversation + HSZ hsz1, // handle of a string + HSZ hsz2, // handle of a string + HDDEDATA hdata, // handle of a global memory object + DWORD , // transaction-specific data + DWORD // transaction-specific data + ) +{ + + + if (!Instance_Class::callback){ + return (HDDEDATA) NULL; + } + + switch ( dde_event ) { + + case XTYP_REGISTER: + case XTYP_UNREGISTER: + + return (HDDEDATA) NULL; + + case XTYP_ADVDATA: + return (HDDEDATA) DDE_FACK; + + case XTYP_XACT_COMPLETE: + + return (HDDEDATA) NULL; + + case XTYP_DISCONNECT: + + Instance_Class::callback( NULL, DDE_ADVISE_DISCONNECT); + return (HDDEDATA) NULL; + + case XTYP_CONNECT: { + + char buffer[32]; + + DdeQueryString (Instance_Class::id_inst, hsz2, buffer, sizeof (buffer), 0) ; + + if (0 != strcmp (buffer, Instance_Class::ascii_name)) { + return (HDDEDATA) NULL; + } + + DdeQueryString (Instance_Class::id_inst, hsz1, buffer, sizeof (buffer), 0) ; + + if (0 != strcmp (buffer, "POKE TOPIC")) { + return (HDDEDATA) NULL; + } + + Instance_Class::callback( NULL, DDE_ADVISE_CONNECT); + return (HDDEDATA) TRUE; + } + + case XTYP_POKE: + + if (Instance_Class::process_pokes == FALSE ) { + return (HDDEDATA) DDE_FNOTPROCESSED; // processing disabled + } else { + + char buffer[32]; + + DdeQueryString (Instance_Class::id_inst, hsz1, buffer, sizeof (buffer), 0) ; + + if (0 != strcmp (buffer, "POKE TOPIC")) { + return (HDDEDATA) DDE_FNOTPROCESSED; + } else if (uFmt == CF_TEXT) { // make sure it's CF_TEXT + + BOOL processed; + BYTE FAR *pdata; + DWORD dw_length; + + if ( (pdata = DdeAccessData( hdata, &dw_length)) == NULL ) { + return (HDDEDATA) DDE_FNOTPROCESSED; + } + + processed = Instance_Class::callback((LPBYTE) pdata, dw_length); + + DdeUnaccessData( hdata ); + + if (processed == TRUE) { + return (HDDEDATA) DDE_FACK; + } else { + return (HDDEDATA) NULL; + } + + } + } + + default: + return (HDDEDATA) NULL; + } +} diff --git a/DDE.H b/DDE.H new file mode 100644 index 0000000..88d2e24 --- /dev/null +++ b/DDE.H @@ -0,0 +1,175 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Dynamic Data Encapsulation * + * * + * File Name : DDE.H * + * * + * Programmer : Steve Wetherill * + * * + * Start Date : June 1, 1996 * + * * + * Last Update : June 8, 1996 [SW] * + * * + *-------------------------------------------------------------------------* + * * + * This is the DDE (Instance_Class) which provides a simple CLIENT/SERVER * + * DDE model for data transactions between Windows applications. * + * This is a fairly naieve implementation allowing only one client/server * + * per Instance_Class object. * + * * + * Typical uses for this class are: * + * * + * i. Robust verification of whether an application is running * + * ii. Data transfer between applications * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* +***************************** Class defines ***************************** +*/ + +#ifndef __DDE_H +#define __DDE_H + +#define DDE_ADVISE_CONNECT -1 // advisory "client has connected" +#define DDE_ADVISE_DISCONNECT -2 // advisory "client has disconnected" + +/* +***************************** Class Declaration ***************************** +*/ + +class Instance_Class { + + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + + /*..................................................................... + Constructor: + - takes null terminated ASCII strings names for client and server + .....................................................................*/ + + Instance_Class( // constructor + LPSTR, // null terminated local sever name string + LPSTR // null terminated remote server name string + ); + + /*..................................................................... + Destructor: + .....................................................................*/ + ~Instance_Class(void); // the destructor + + /*..................................................................... + Send data routine: + - sends an unsolicited packet of data to the remote server + .....................................................................*/ + BOOL Poke_Server( LPBYTE, DWORD); + + /*..................................................................... + Send data routine: + - sets up DNS for the server and registers a user callback to handle + incoming data + .....................................................................*/ + BOOL Register_Server( BOOL CALLBACK (*)(LPBYTE, long)); + + /*..................................................................... + Does a trial connect to the remote server. + - used to determine whether server is alive or not (and thus running) + .....................................................................*/ + BOOL Test_Server_Running( HSZ ); + + /*..................................................................... + Enables user callback (disabled by default) + .....................................................................*/ + BOOL Enable_Callback( BOOL ); // enable or disable callback + + /*..................................................................... + Open a connection for sending data to remote server + .....................................................................*/ + BOOL Open_Poke_Connection( HSZ ); + + /*..................................................................... + Close connection with remote server + .....................................................................*/ + BOOL Close_Poke_Connection( void ); + + // + // static members + // + + /*..................................................................... + User callback - called upon receipt of incoming data (static member!) + .....................................................................*/ + static BOOL CALLBACK (*callback) ( + + LPBYTE pointer, // pointer to received data + long length // if >0 length of received data + // if <0 + // -1 == client connect detected + // -2 == client disconnect detected + ); + + /*..................................................................... + DDE callback, called when DDEML has an event for us + .....................................................................*/ + static HDDEDATA CALLBACK dde_callback( + + UINT uType, // transaction type + UINT uFmt, // clipboard data format + HCONV hconv, // handle of the conversation + HSZ hsz1, // handle of a string + HSZ hsz2, // handle of a string + HDDEDATA hdata, // handle of a global memory object + DWORD dwData1, // transaction-specific data + DWORD dwData2 // transaction-specific data + ); + HANDLE instance; // this application's instance + HWND hwnd; // valid window handle + + /*..................................................................... + member variables + .....................................................................*/ + + static DWORD id_inst; // instance identifier set by DdeInitialize + static BOOL process_pokes; // controls response to pokes + static char ascii_name[32]; // name of server + + // + // non-static member variables + // + + HSZ remote_name; // string handle for remote server name + HSZ local_name; // string handle for local server name + HSZ system_topic; // string handle for the "system" topic + HSZ poke_topic; // string handle for poking data to server topic + HSZ poke_item; // string handle for poking data to server item + + HCONV conv_handle; // conversation handle + BOOL dde_error; // error flag + +}; + +#endif + + \ No newline at end of file diff --git a/DEBUG.CPP b/DEBUG.CPP new file mode 100644 index 0000000..6515925 --- /dev/null +++ b/DEBUG.CPP @@ -0,0 +1,690 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\debug.cpv 2.17 16 Oct 1995 16:49:18 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DEBUG.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : July 5, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Self_Regulate -- Regulates the logic timer to result in smooth animation. * + * Debug_Key -- Debug mode keyboard processing. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include +#include +#include +#ifdef CHEAT_KEYS + +extern bool ScreenRecording; + +/*********************************************************************************************** + * Debug_Key -- Debug mode keyboard processing. * + * * + * If debugging is enabled, then this routine will be called for every keystroke that the * + * game doesn't recognize. These extra keys usually perform some debugging function. * + * * + * INPUT: input -- The key code that was pressed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +void Debug_Key(unsigned input) +{ + static int map_x = -1; + static int map_y = -1; + static int map_width = -1; + static int map_height = -1; + + if (!input || input & KN_BUTTON) return; + + /* + ** Processing of normal keystrokes. + */ + if (Debug_Flag) { + + switch (input) { + + case KN_L: + extern int NetMonoMode,NewMonoMode; + if (NetMonoMode) + NetMonoMode = 0; + else + NetMonoMode = 1; + NewMonoMode = 1; + break; + + /* + ** Start saving off screens + */ + case (int)KN_K|(int)KN_CTRL_BIT: + ScreenRecording = true; + break; + + case KN_K: + /* + ** time to create a screen shot using the PCX code (if it works) + */ + { + GraphicBufferClass temp_page( SeenBuff.Get_Width(), + SeenBuff.Get_Height(), + NULL, + SeenBuff.Get_Width() * SeenBuff.Get_Height()); + char filename[30]; + + SeenBuff.Blit(temp_page); + for (int lp = 0; lp < 99; lp ++) { + if (lp < 10) { + sprintf(filename, "scrsht0%d.pcx", lp); + } else { + sprintf(filename, "scrsht%d.pcx", lp); + } + if (access(filename, F_OK) == -1) + break; + } + + Write_PCX_File(filename, temp_page, (unsigned char *)CurrentPalette); + //Map.Place_Random_Crate(); + } + break; + + case KN_P: + Keyboard::Clear(); + while (!Keyboard::Check()) { + Self_Regulate(); + Sound_Callback(); + } + Keyboard::Clear(); + break; + + case KN_O: + { + AircraftClass * air = new AircraftClass(AIRCRAFT_ORCA, PlayerPtr->Class->House); + if (air) { + air->Altitude = 0; + air->Unlimbo(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), DIR_N); + } + } + break; + + case (int)KN_B|(int)KN_ALT_BIT: + { + Debug_Instant_Build ^= 1; + } + break; + case KN_B: + { + AircraftClass * air = new AircraftClass(AIRCRAFT_HELICOPTER, PlayerPtr->Class->House); + if (air) { + air->Altitude = 0; + air->Unlimbo(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), DIR_N); + } + } + break; + + case KN_T: + { + AircraftClass * air = new AircraftClass(AIRCRAFT_TRANSPORT, PlayerPtr->Class->House); + if (air) { + air->Altitude = 0; + air->Unlimbo(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), DIR_N); + } + } + break; + + case KN_GRAVE: + new AnimClass(ANIM_ART_EXP1, Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y())); + Explosion_Damage(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), 250, NULL, WARHEAD_HE); + break; + + case KN_Z: +// new AnimClass(ANIM_LZ_SMOKE, Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y())); + GDI_Ending(); + break; + + case KN_C: + Debug_Cheat = (Debug_Cheat == false); + PlayerPtr->IsRecalcNeeded = true; + PlayerPtr->Add_Nuke_Piece(); + PlayerPtr->Add_Nuke_Piece(); + PlayerPtr->Add_Nuke_Piece(); + + /* + ** This placement might affect any prerequisite requirements for construction + ** lists. Update the buildable options accordingly. + */ + if (!ScenarioInit) { + Map.Recalc(); + for (int index = 0; index < Buildings.Count(); index++) { + Buildings.Ptr(index)->Update_Buildables(); + } + } + break; + + case (int)KN_Z|(int)KN_ALT_BIT: + if (map_x == -1) { + map_x = Map.MapCellX; + map_y = Map.MapCellY; + map_width = Map.MapCellWidth; + map_height = Map.MapCellHeight; + Map.MapCellX = 1; + Map.MapCellY = 1; + Map.MapCellWidth = 62; + Map.MapCellHeight = 62; + } else { + Map.MapCellX = map_x; + Map.MapCellY = map_y; + Map.MapCellWidth = map_width; + Map.MapCellHeight = map_height; + map_x = -1; + map_y = -1; + map_width = -1; + map_height = -1; + } + break; + +#ifdef NEVER + case KN_G: + HouseClass::As_Pointer(HOUSE_GOOD)->Flag_Attach(Map.Click_Cell_Calc(Get_Mouse_X(), Get_Mouse_Y())); + break; + + case KN_N: + HouseClass::As_Pointer(HOUSE_BAD)->Flag_Attach(Map.Click_Cell_Calc(Get_Mouse_X(), Get_Mouse_Y())); + break; +#endif + + case KN_R: + if (CurrentObject.Count()) { + ((TechnoClass *)CurrentObject[0])->IsCloakable = true; + } + break; + + case KN_M: + if (Debug_Flag) { + if (MonoClass::Is_Enabled()) { + MonoClass::Disable(); + } else { + MonoClass::Enable(); + } + } + break; + + case (int)KN_W|(int)KN_ALT_BIT: + PlayerPtr->Flag_To_Win(); + break; + + case (int)KN_L|(int)KN_ALT_BIT: + PlayerPtr->Flag_To_Lose(); + break; + + case KN_F: + Debug_Find_Path ^= 1; + break; + + case KN_DELETE: + if (CurrentObject.Count()) { + Map.Recalc(); + //CurrentObject[0]->Detach_All(); + delete CurrentObject[0]; + } + break; + + case KN_D: + if (Teams.Ptr(0)) { + delete Teams.Ptr(0); + } + break; + + case (int)KN_DELETE|(int)KN_SHIFT_BIT: + if (CurrentObject.Count()) { + Map.Recalc(); + int damage = 50; + CurrentObject[0]->Take_Damage(damage, 0, WARHEAD_SA); + } + break; + + case KN_INSERT: + if (CurrentObject.Count()) { + Map.PendingObject = &CurrentObject[0]->Class_Of(); + if (Map.PendingObject) { + Map.PendingHouse = CurrentObject[0]->Owner(); + Map.PendingObjectPtr = Map.PendingObject->Create_One_Of(HouseClass::As_Pointer(Map.PendingHouse)); + if (Map.PendingObjectPtr) { + Map.Set_Cursor_Pos(); + Map.Set_Cursor_Shape(Map.PendingObject->Occupy_List()); + } + } + } + break; + +#ifdef NEVER + case KN_1: + case KN_2: + case KN_3: + case KN_4: + case KN_5: + case KN_6: + case KN_7: + case KN_8: + case KN_9: + case KN_0: + MonoPage = (input & 0xFF) - KN_1; + MonoPage %= sizeof(MonoArray)/sizeof(MonoArray[0]); + MonoArray[MonoPage].View(); + input = 0; + break; +#endif + +#ifdef NEVER + case ((int)KN_F1 | (int)KN_SHIFT_BIT): + Special.IsBarOn = (Special.IsBarOn == false); + Map.Flag_To_Redraw(true); + break; + + case ((int)KN_F1 | (int)KN_SHIFT_BIT): // quick load/save for debugging + if (!Save_Game(0,"Command & Conquer Save Game File")) { + CCMessageBox().Process("Error saving game!"); + Prog_End(); + exit(EXIT_SUCCESS); + } + break; + + case ((int)KN_F2 | (int)KN_SHIFT_BIT): // quick load/save for debugging + if (!Load_Game(0)) { + CCMessageBox().Process("Error loading game!"); + Prog_End(); + exit(EXIT_SUCCESS); + } + break; + +//#ifdef SCENARIO_EDITOR + case KN_F2: // enable/disable the map editor + Go_Editor(!Debug_Map); + break; +//#endif +#endif + +#ifdef NEVER + case KN_F2: { + Debug_Map++; + Scenario_Editor(); + Debug_Map--; +#ifdef NEVER + COORDINATE coord; + int index; + static COORDINATE _coords[] = { + 0x00010001L, + 0x00800080L, + 0x00810081L, + 0x00010081L, + 0x00810001L, + 0x00800081L, + 0x00800001L, + 0x00010080L, + 0x00810080L, + 0L + }; + index = 0; + while (_coords[index]) { + coord = _coords[index++]; + Mono_Printf("Spillage for %08lX = %d.\r", coord, Coord_Spillage_Number(coord)); + } + Keyboard::Clear(); + Keyboard::Get(); + +#endif + +#ifdef NEVER +#define MAX_RADIUS 10 + COORDINATE coord; + int x,y; + COORDINATE const *ptr; + int input; + int f1,f2; + TurnTrackType const *track; + + #define XCENTER 160 + #define YCENTER 100 + for (;;) { + VisiblePage.Clear(); + + // Draw grid. + { + static int _gridx[] = {0,64,128,192,0,64,128,192,0,64,128,192}; + static int _gridy[] = {0,0,0,0,64,64,64,64,128,128,128,128}; + int index; + + for (index = 0; index < 12; index++) { + LogicPage->Put_Pixel((_gridx[index]+XCENTER)-(32+64),(_gridy[index]+YCENTER)-(32+64), DKGRAY); + } + } + + // Get facing #1. + LogicPage->Print("Facing #1 (0-7)?", 0, 0, WHITE, BLACK); + input = Keyboard::Get(); + if (input == KA_ESC) break; + input -= KA_0; + input = Bound(input, 0, 7); +// input = MAX(input, 0); +// input = MIN(input, 7); + f1 = input; + Int_Print(f1, 100, 0, WHITE, BLACK); + + // Get facing #2. + LogicPage->Print("Facing #2 (0-7)?", 0, 10, WHITE, BLACK); + input = Keyboard::Get(); + if (input == KA_ESC) break; + input -= KA_0; + input = Bound(input, 0, 7); +// input = MAX(input, 0); +// input = MIN(input, 7); + f2 = input; + Int_Print(f2, 100, 10, WHITE, BLACK); + + track = &TrackControl[f1][f2]; + if (track->Track == 0) { + LogicPage->Print("Undefined track.", 0, 30, WHITE, BLACK); + } else { + int index; // Track index counter. + + ptr = TrackPointers[track->Track-1]; + index = 0; + while (ptr[index]) { + coord = Smooth_Turn(NULL, ptr[index], track->Flag); + + x = (int)(coord & 0xFFFF); + y = (int)((coord >> 16) & 0xFFFF); + LogicPage->Put_Pixel(XCENTER + (x>>2), YCENTER + (y>>2), WHITE); + Delay(1); + index++; + } + + } + input = Keyboard::Get(); + if (input == KA_ESC) break; + } + + Map.Flag_To_Redraw(true); +#endif +#ifdef NEVER + FILE *fh; + int index; + COORDINATE coord; + + fh = fopen("diagonal.txt", "wt"); + if (fh) { + + fprintf(fh, "track 2\n"); + coord = 0x0100FF00L; + for (index = 0; index <= 48; index++) { + fprintf(fh, "0x%08lXL\n", coord); + coord = Coord_Move(coord, 32, 11); + } + fprintf(fh, "\n\n"); + + fprintf(fh, "track 1\n"); + coord = 0x01000000L; + for (index = 0; index <= 40; index++) { + fprintf(fh, "0x%08lXL\n", coord); + coord = Coord_Move(coord, 0, 11); + } + fprintf(fh, "\n\n"); + + fclose(fh); + } +#endif +#ifdef NEVER + FILE *fh; + int x,y,radius; + int radsize[MAX_RADIUS+2]; + int count; + + memset(radsize, 0, sizeof(radsize)); + fh = fopen("Range.txt", "wt"); + if (fh) { + fprintf(fh, "int const RadiusOffset[] = {\n"); + + for (radius = 0; radius <= MAX_RADIUS; radius++) { + + fprintf(fh, "\t/* %-2d */\t", radius); + for (y = -MAX_RADIUS; y <= MAX_RADIUS; y++) { + for (x = -MAX_RADIUS; x <= MAX_RADIUS; x++) { + int xd,yd,dist; + + xd = ABS(x); + yd = ABS(y); + if (xd > yd) { + dist = yd/2 + xd; + } else { + dist = xd/2 + yd; + } + if (dist == radius) { + dist = y*MAP_CELL_W + x; + + if (y) { + if (y < 0) { + fprintf(fh, "(-MCW*%d)", ABS(y)); + } else { + fprintf(fh, "(MCW*%d)", ABS(y)); + } + fprintf(fh, "%c%d,", (x<0) ? '-' : '+', ABS(x)); + } else { + fprintf(fh, "%d,", x); + } + radsize[radius]++; + } + } + } + fprintf(fh, "\n"); + } + fprintf(fh, "};\n\n"); + + count = 0; + fprintf(fh, "int const RadiusCount[%d] = {", MAX_RADIUS+1); + for (radius = 0; radius <= MAX_RADIUS; radius++) { + count += radsize[radius]; + fprintf(fh, "%d", count); + if (radius != MAX_RADIUS) { + fprintf(fh, ","); + } + } + fprintf(fh, "};\n"); + fclose(fh); + } +#endif + } + break; +#endif + +#ifdef NEVER + case ((int)KN_F3 | (int)KN_ALT_BIT): // quick load/save for debugging + Debug_Threat = (Debug_Threat == false); + Map.Flag_To_Redraw(true); + break; + +#endif + + case KN_F3: + Debug_Icon = (Debug_Icon == false); + Map.Flag_To_Redraw(true); + break; + + + /* + ** Reveal entire map to player. + */ + case KN_F4: + if (GameToPlay == GAME_NORMAL) { + Debug_Unshroud = (Debug_Unshroud == false); + Map.Flag_To_Redraw(true); + } + break; + + /* + ** Shows sight and fire range in the form of circles emanating from the currently + ** selected unit. The white circle is for sight range, the red circle is for + ** fire range. + */ + case KN_F7: + if (CurrentObject.Count() && CurrentObject[0]->Is_Techno()) { + TechnoTypeClass const & ttype = (TechnoTypeClass const &)CurrentObject[0]->Class_Of(); + int sight = ((int)ttype.SightRange)<<8; + int weapon = 0; + if (ttype.Primary != WEAPON_NONE) weapon = Weapons[ttype.Primary].Range; + Set_Logic_Page(SeenBuff); + COORDINATE center = CurrentObject[0]->Center_Coord(); + COORDINATE center2 = CurrentObject[0]->Fire_Coord(0); + + for (int r = 0; r < 255; r += 10) { + int x,y,x1,y1; + DirType r1 = (DirType)r; + DirType r2 = (DirType)((r+10) & 0xFF); + + if (Map.Coord_To_Pixel(Coord_Move(center, r1, sight), x, y)) { + Map.Coord_To_Pixel(Coord_Move(center, r2, sight), x1, y1); + LogicPage->Draw_Line(x, y+8, x1, y1+8, WHITE); + } + if (Map.Coord_To_Pixel(Coord_Move(center2, r1, weapon), x, y)) { + Map.Coord_To_Pixel(Coord_Move(center2, r2, weapon), x1, y1); + LogicPage->Draw_Line(x, y+8, x1, y1+8, RED); + } + } + } + break; + + case ((int)KN_F4 | (int)KN_CTRL_BIT): + Debug_Unshroud = (Debug_Unshroud == false); + Map.Flag_To_Redraw(true); + break; + +#ifdef NEVER + case KN_F5: + Special.IsShowPath = (Special.IsShowPath == false); + //PlayerPtr->Credits += 1000; + break; + + case KN_F6: + if (Map.In_Radar(XY_Cell(Map.MapCellX+5, Map.MapCellY - 1))) { + Mono_Printf("Arrrggggghhhhh!"); + } else { + Mono_Printf("No Arrrggggghhhhh!"); + } + break; + + case ((int)KN_F9 | (int)KN_CTRL_BIT): + if (HouseClass::As_Pointer(HOUSE_GOOD)) + (HouseClass::As_Pointer(HOUSE_GOOD))->Blowup_All(); + break; + + case ((int)KN_F10 | (int)KN_CTRL_BIT): + if (HouseClass::As_Pointer(HOUSE_BAD)) + (HouseClass::As_Pointer(HOUSE_BAD))->Blowup_All(); + break; +#endif + } + + } +} + + +/*********************************************************************************************** + * Self_Regulate -- Regulates the logic timer to result in smooth animation * + * * + * The self regulation process checks the number of frames displayed * + * per second and from this determines the amount of time to devote * + * to internal logic processing. By adjusting the time allotted to * + * internal processing, smooth animation can be maintained. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: In order for this routine to work properly it MUST be * + * called every display loop. * + * * + * HISTORY: * + * 07/31/1991 JLB : Created. * + * 07/05/1994 JLB : Handles new monochrome system. * + *=============================================================================================*/ +#define UPDATE_INTERVAL TIMER_SECOND +void Self_Regulate(void) +{ + static CountDownTimerClass DebugTimer(BT_SYSTEM); + static ObjectClass * _lastobject = 0; + + if (!DebugTimer.Time()) { + DebugTimer.Set(UPDATE_INTERVAL); + + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + mono->Set_Default_Attribute(2); + + switch (MonoPage) { + case 0: + mono = &MonoArray[0]; + mono->Clear(); + + /* + ** Display the status of the currently selected object. + */ + if (CurrentObject.Count()) { + _lastobject = CurrentObject[0]; + } + if (_lastobject && !_lastobject->IsActive) { + _lastobject = 0; + } + if (_lastobject) { + _lastobject->Debug_Dump(mono); + } + Logic.Debug_Dump(mono); + mono->Set_Cursor(0, 20); + mono->Printf( + "Heap size:%10ld \r" + "Largest: %10ld \r" + "Ttl Free: %10ld \r" + "Frag: %10ld \r", + Heap_Size(MEM_NORMAL), + Ram_Free(MEM_NORMAL), + Total_Ram_Free(MEM_NORMAL), + Total_Ram_Free(MEM_NORMAL)-Ram_Free(MEM_NORMAL) + ); + *MonoClass::Get_Current() = *mono; + break; + } + + MonoArray[MonoPage] = *mono; + } + } +} +#endif diff --git a/DEBUG.H b/DEBUG.H new file mode 100644 index 0000000..3d379b7 --- /dev/null +++ b/DEBUG.H @@ -0,0 +1,42 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#define TXT_NONE_DEBUG 0x3e8 // +#define TXTED_BLANK 0x3e9 // ____ +#define TXTED_UNABLETOREAD 0x3ea // Unable to read scenario! +#define TXTED_FILEEXISTS 0x3eb // File exists. Replace? +#define TXTED_LOWMEM 0x3ec // Insufficient memory! +#define TXTED_EXIT 0x3ed // Exit Scenario Editor? +#define TXT_GENERIC_EXCEPTION 0x3ee // ERROR: Exception was +#define TXT_RADIO_1 0x3ef // hisssss +#define TXT_RADIO_2 0x3f0 // Roger. +#define TXT_RADIO_3 0x3f1 // Come in. +#define TXT_RADIO_4 0x3f2 // Over and out. +#define TXT_RADIO_5 0x3f3 // Requesting transport. +#define TXT_RADIO_6 0x3f4 // I've got a delivery for +#define TXT_RADIO_7 0x3f5 // I'm performing load/unload +#define TXT_RADIO_8 0x3f6 // I'm clear. +#define TXT_RADIO_9 0x3f7 // You are clear to unload. +#define TXT_RADIO_10 0x3f8 // Am unable to comply. +#define TXT_RADIO_11 0x3f9 // I'm starting construction +#define TXT_RADIO_12 0x3fa // I've finished construction. +#define TXT_RADIO_13 0x3fb // Oops, sorry. I might have +#define TXT_RADIO_14 0x3fc // I'm full. May I unload at +#define TXT_RADIO_15 0x3fd // Are you a refinery and are +#define TXT_RADIO_16 0x3fe // Take this kick! You... +#define TXT_RADIO_17 0x3ff // Take this punch! You... diff --git a/DEFINES.H b/DEFINES.H new file mode 100644 index 0000000..6c725be --- /dev/null +++ b/DEFINES.H @@ -0,0 +1,2771 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\defines.h_v 2.19 16 Oct 1995 16:44:54 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DEFINES.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : September 10, 1993 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DEFINES_H +#define DEFINES_H + + +/********************************************************************** +** If defined, then the advanced balancing features will be enabled +** for this version. +*/ +//#define ADVANCED +#define PATCH // Super patch (1.17?) + + +/********************************************************************** +** The demo version of C&C will be built if the following define +** is active. +*/ +//#define DEMO + + +/********************************************************************** +** Define this to allow play of the bonus missions for the Gateway +** bundle deal. +*/ +#define BONUS_MISSIONS + + +/********************************************************************** +** Handle expansion scnearios as a set of single missions with all +** necessary information self contained within the mission file. +*/ +#ifndef DEMO +#define NEWMENU +#endif + + +/********************************************************************** +** If the scenario editor to to be active in this build then uncomment +** the following #define line. +*/ +//#define SCENARIO_EDITOR + + +/********************************************************************** +** This define enables the full set of cheat keys and special +** command line options. +*/ +#define CHEAT_KEYS + + +/********************************************************************** +** If this is defined, the special Virgin limited cheat keys +** are enabled. This allows the "cheat" parameter and then only +** allows the ALT-W to win the mission. +*/ +//#define VIRGIN_CHEAT_KEYS + + +/********************************************************************** +** Optional parameter control for special options. +*/ +//#define PARM_6PLAYER 0x5D9F6F24 // "6" +#define PARM_6PLAYER 0x9CAFC93B // Alternate 6 player keyphrase. + +/* +** Enable the set of limited cheat key options. +*/ +#ifdef VIRGIN_CHEAT_KEYS +#define PARM_PLAYTEST 0xF7DDC227 // "PLAYTEST" +#endif + +/* +** Enable the full set of cheat key options. +*/ +#ifdef CHEAT_KEYS +#ifndef PARM_PLAYTEST +#define PARM_PLAYTEST 0xF7DDC227 // "PLAYTEST" +#endif +#define PARM_CHEATDAVID 0xBE79088C // Cheat keys for David Dettmer +#define PARM_CHEATERIK 0x9F38A19D // Cheat keys for Erik Yeo +#define PARM_EDITORERIK 0xC2AA509B // Map editor for Erik Yeo +#define PARM_CHEATPHIL 0x39D01821 // Cheat keys for Phil Gorrow +#define PARM_CHEATJOE 0xABDD0362 // Cheat keys for Joe Bostic +#define PARM_CHEATBILL 0xB5B63531 // Cheat keys for Bill Randolph +#define PARM_CHEAT_STEVET 0x2E7FE493 // Cheat keys for Steve Tall +#define PARM_EDITORBILL 0x7E7C4CCA // "-EDITOR" +#define PARM_CHEATMIKE 0x00532693 // Mike Lightner +#define PARM_CHEATADAM 0xDFABC23A // Adam Isgreen +#endif + +//#define PARM_CHEAT 0x6F4BE7CA // "CHEAT" +//#define PARM_EDITOR 0x7E7C4CCA // "-EDITOR" + +#define PARM_EASY 0x59E975CE // "EASY" Enables easy mode. +#define PARM_HARD 0xACFE9D13 // "HARD" Enables hard mode. + +#define PARM_INSTALL 0xD95C68A2 // "FROMINSTALL" +#define PARM_TRUENAME 0xB1A34435 // Enables true object names. +#define PARM_3POINT 0x03552894 // Enable three point turns. +#define PARM_SCORE 0x7FDE2C33 // Enables alternate themes. +#define PARM_COMBAT 0xDC57C4B2 // Gives combat advantage to attacker. +#define PARM_TREETARGET 0x00AB6BEF // Allows targeting of trees without key. +#define PARM_BIB 0xF7867BF0 // Disables building bibs. +#define PARM_MCV 0x104DF10F // MCV undeploys rather than sells. +#define PARM_HELIPAD 0x53EBECBC // Helipad can be purchased separately from helicopter. +#define PARM_IQ 0x9E3881B8 // Smart self defense logic enable. +#define PARM_SQUISH 0x4EA2FBDF // Squish images for infantry bodies. +#define PARM_HUMAN 0xACB58F61 // Human generated sound effects. +#define PARM_SCROLLING 0xC084AE82 // Restricts scrolling over the tabs. +//#define PARM_SPECIAL 0xD18129F6 // Enables special mode. +//#define PARM_SPECIAL 0x2E84E394 // #1 +//#define PARM_SPECIAL 0x63CE7584 // #2 +//#define PARM_SPECIAL 0x85F110A5 // #3 +///#define PARM_SPECIAL 0x7F65F13C // #4 +//#define PARM_SPECIAL 0x431F5F61 // #5 +#define PARM_SPECIAL 0x11CA05BB // #6 funpark +//#define PARM_SPECIAL 0xE0F651B9 // #7 +//#define PARM_SPECIAL 0x10B9683D // #8 +//#define PARM_SPECIAL 0xEE1CD37D // #9 + + +/********************************************************************** +** Defines for verifying free disk space +*/ +#define INIT_FREE_DISK_SPACE 1024*4096 //8388608 +#define SAVE_GAME_DISK_SPACE INIT_FREE_DISK_SPACE // (INIT_FREE_DISK_SPACE - (1024*4096)) +//#define SAVE_GAME_DISK_SPACE 100000 + + +/********************************************************************** +** This is the credit threshold that the computer's money must exceed +** in order for structure repair to commence. +*/ +#define REPAIR_THRESHHOLD 1000 + + +//#define GERMAN 1 +//#define FRENCH 1 +//#define JAPANESE 1 + +#define FOREIGN_VERSION_NUMBER 6 + + +/********************************************************************** +** These enumerations are used to implement RTTI. +*/ +typedef enum RTTIType { + RTTI_NONE=0, + RTTI_INFANTRY, + RTTI_INFANTRYTYPE, + RTTI_UNIT, + RTTI_UNITTYPE, + RTTI_AIRCRAFT, + RTTI_AIRCRAFTTYPE, + RTTI_BUILDING, + RTTI_BUILDINGTYPE, + + RTTI_TERRAIN, + RTTI_ABSTRACTTYPE, + RTTI_ANIM, + RTTI_ANIMTYPE, + RTTI_BULLET, + RTTI_BULLETTYPE, + RTTI_OVERLAY, + RTTI_OVERLAYTYPE, + RTTI_SMUDGE, + RTTI_SMUDGETYPE, + RTTI_TEAM, + RTTI_TEMPLATE, + RTTI_TEMPLATETYPE, + RTTI_TERRAINTYPE, + RTTI_OBJECT, + RTTI_SPECIAL +} RTTIType; + + +/********************************************************************** +** This is the size of the speech buffer. This value should be as large +** as the largest speech sample, plus a few bytes for overhead +** (16 bytes is sufficient). +*/ +#define SPEECH_BUFFER_SIZE 50000L + + +/********************************************************************** +** This is the size of the shape buffer. This buffer is used as a staging +** buffer for the shape drawing technology. It MUST be as big as the +** largest shape (uncompressed) that will be drawn. If this value is +** changed, be sure to update the makefile and rebuild all of the shape +** data files. +*/ +#define SHAPE_BUFFER_SIZE 40000L + +// Use this to allow keep track of versions as they affect saved games. +#define VERSION_NUMBER 1 +#define RELEASE_NUMBER 01 + +#define FAME_FILE_NAME "HALLFAME.DAT" + + +/********************************************************************** +** Map controls. The map is composed of square elements called 'cells'. +** All larger elements are build upon these. +*/ + +// Size of the map in cells. The width of the map must be a power +// of two. This is accomplished by setting the width by the number of +// bits it occupies. The number of meta-cells will be a subset of the +// cell width. +#define MAP_CELL_MAX_X_BITS 6 +#define MAP_CELL_MAX_Y_BITS 6 +#define MAP_CELL_X_MASK (~(~0 << MAP_CELL_MAX_X_BITS)) +//#define MAP_CELL_Y_MASK ((~(~0 << MAP_CELL_MAX_Y_BITS)) << MAP_CELL_MAX_Y_BITS) + +// Size of the map in cells. +#define MAP_CELL_W (1<APC or vehicle->Repair facility. + ACTION_SELF, // Self select special case. + ACTION_ATTACK, // Can attack or fire upon it in some fashion. + ACTION_HARVEST, // Special harvest mode. + ACTION_SELECT, // Would change selection to specified object. + ACTION_TOGGLE_SELECT,// Toggles select state of the object. + ACTION_CAPTURE, // The unit will try to capture the object. + ACTION_REPAIR, // The target object should be repaired. + ACTION_SELL, // The target building should be sold back. + ACTION_SELL_UNIT, // The target unit should be sold back. + ACTION_NO_SELL, // No sell or no repair. + ACTION_NO_REPAIR, // No sell or no repair. + ACTION_SABOTAGE, // The unit will try to sabotage/destroy the object. + ACTION_ION, // That target object should be blasted. + ACTION_NUKE_BOMB, // That target object should be blasted. + ACTION_AIR_STRIKE, // That target object should be blasted. + ACTION_GUARD_AREA, // Guard the area/object clicked on. + + ACTION_COUNT +} ActionType; + + +/********************************************************************** +** When a unit gets damaged, the result of the damage is returned as +** this type. It can range from no damage taken to complete destruction. +*/ +typedef enum ResultType { + RESULT_NONE, // No damage was taken by the target. + RESULT_LIGHT, // Some damage was taken, but no state change occurred. + RESULT_HALF, // Damaged to below half strength (only returned on transition). + RESULT_MAJOR, // Damaged down to 1 hit point. + RESULT_DESTROYED, // Damaged to complete destruction. +} ResultType; + + +/********************************************************************** +** These are the special concrete control defines. They enumerate the +** sequence order of the concrete icons in the concrete art file. +*/ +// DEBUG === convert this to be zero based so that a nulled cell is the +// default cell. +enum ConcreteEnum { + C_NONE=-1, + C_LEFT=0, + C_RIGHT=1, + C_RIGHT_UPDOWN=2, + C_LEFT_UPDOWN=3, + C_UP_RIGHT=4, + C_UP_LEFT=5, + C_DOWN_RIGHT=6, + C_DOWN_LEFT=7, + C_RIGHT_DOWN=8, + C_LEFT_DOWN=9, + C_RIGHT_UP=10, + C_LEFT_UP=11, + C_UPDOWN_RIGHT=12, + C_UPDOWN_LEFT=13 +}; + + +/********************************************************************** +** Units that move can move at different speeds. These enumerate the +** different speeds that a unit can move. +*/ +typedef enum MPHType{ + MPH_IMMOBILE=0, + MPH_VERY_SLOW=5, + MPH_KINDA_SLOW=6, + MPH_SLOW=8, + MPH_SLOW_ISH=10, + MPH_MEDIUM_SLOW=12, + MPH_MEDIUM=18, + MPH_MEDIUM_FAST=30, + MPH_MEDIUM_FASTER=35, + MPH_FAST=40, + MPH_ROCKET=60, + MPH_VERY_FAST=100, + MPH_LIGHT_SPEED=255 +} MPHType; + + +/********************************************************************** +** General audio volume is enumerated by these identifiers. Since small +** volume variations are usually unnoticable when specifying the volume +** to play a sample, this enumeration list creates more readable code. +*/ +typedef enum VolType +{ + VOL_OFF=0, + VOL_0=VOL_OFF, + VOL_1=0x19, + VOL_2=0x32, + VOL_3=0x4C, + VOL_4=0x66, + VOL_5=0x80, + VOL_6=0x9A, + VOL_7=0xB4, + VOL_8=0xCC, + VOL_9=0xE6, + VOL_10=0xFF, + VOL_FULL=VOL_10 +} VolType; + + +/********************************************************************** +** The houses that can be played are listed here. Each has their own +** personality and strengths. +*/ +typedef enum HousesType { + HOUSE_NONE=-1, + HOUSE_GOOD, // Global Defense Initiative + HOUSE_BAD, // Brotherhood of Nod + HOUSE_NEUTRAL, // Civilians + HOUSE_JP, // Disaster Containment Team + HOUSE_MULTI1, // Multi-Player house #1 + HOUSE_MULTI2, // Multi-Player house #2 + HOUSE_MULTI3, // Multi-Player house #3 + HOUSE_MULTI4, // Multi-Player house #4 + HOUSE_MULTI5, // Multi-Player house #5 + HOUSE_MULTI6, // Multi-Player house #6 + + HOUSE_COUNT, + HOUSE_FIRST=HOUSE_GOOD +} HousesType; + +inline HousesType operator++(HousesType &, int); + +#define HOUSEF_GOOD (1< 2 players. +*/ +typedef enum ScenarioPlayerEnum +{ + SCEN_PLAYER_NONE = -1, + SCEN_PLAYER_GDI, + SCEN_PLAYER_NOD, + SCEN_PLAYER_JP, + SCEN_PLAYER_2PLAYER, + SCEN_PLAYER_MPLAYER, + SCEN_PLAYER_COUNT, + SCEN_PLAYER_FIRST = 0, +} ScenarioPlayerType; + +inline ScenarioPlayerType operator++(ScenarioPlayerType &, int); + + +/********************************************************************** +** These are the directional parameters for a scenario. +*/ +typedef enum ScenarioDirEnum +{ + SCEN_DIR_NONE = -1, + SCEN_DIR_EAST, + SCEN_DIR_WEST, + SCEN_DIR_COUNT, + SCEN_DIR_FIRST = 0, +} ScenarioDirType; + +inline ScenarioDirType operator++(ScenarioDirType &, int); + + +/********************************************************************** +** These are the random variations of a scenario. +*/ +typedef enum ScenarioVarEnum +{ + SCEN_VAR_NONE = -1, + SCEN_VAR_A, + SCEN_VAR_B, + SCEN_VAR_C, + SCEN_VAR_D, + SCEN_VAR_COUNT, // comes before the Lose value! + SCEN_VAR_LOSE, + SCEN_VAR_FIRST = 0, +} ScenarioVarType; + +inline ScenarioVarType operator++(ScenarioVarType &, int); + + +/********************************************************************** +** The objects to be drawn on the map are grouped into layers. These +** enumerated values specify those layers. The ground layer is sorted +** from back to front. +*/ +typedef enum LayerType { + LAYER_NONE=-1, + LAYER_GROUND, // Touching the ground type object (units & buildings). + LAYER_AIR, // Flying above the ground (explosions & flames). + LAYER_TOP, // Topmost layer (aircraft & bullets). + + LAYER_COUNT, + LAYER_FIRST=0 +} LayerType; + +inline LayerType operator++(LayerType &, int); + + +/********************************************************************** +** This enumerates the various bullet types. These types specify bullet's +** visual and explosive characteristics. +*/ +typedef enum BulletType { + BULLET_NONE=-1, + BULLET_SNIPER, // Sniper bullet. + BULLET_BULLET, // Small arms + BULLET_APDS, // Armor piercing projectile. + BULLET_HE, // High explosive shell. + BULLET_SSM, // Surface to surface small missile type. + BULLET_SSM2, // MLRS missile. + BULLET_SAM, // Fast homing anti-aircraft missile. + BULLET_TOW, // TOW anti-vehicle short range missile. + BULLET_FLAME, // Flame thrower flame. + BULLET_CHEMSPRAY, // Chemical weapon spray. + BULLET_NAPALM, // Napalm bomblet. + BULLET_GRENADE, // Hand tossed grenade. + BULLET_LASER, // Laser beam from obelisk + BULLET_NUKE_UP, // Nuclear Missile on its way down + BULLET_NUKE_DOWN, // Nuclear Missile on its way up + BULLET_HONEST_JOHN, // SSM with napalm warhead. + BULLET_SPREADFIRE, // Chain gun bullets. + BULLET_HEADBUTT, // Stegosaurus, Triceratops head butt + BULLET_TREXBITE, // Tyrannosaurus Rex's bite - especially bad for infantry + + BULLET_COUNT, + BULLET_FIRST=0 +} BulletType; + +inline BulletType operator++(BulletType &, int); + + +/********************************************************************** +** All game buildings (structures) are enumerated here. This includes +** civilian structures as well. +*/ +typedef enum StructType { + STRUCT_NONE=-1, + STRUCT_WEAP, + STRUCT_GTOWER, + STRUCT_ATOWER, + STRUCT_OBELISK, + STRUCT_RADAR, + STRUCT_TURRET, + STRUCT_CONST, + STRUCT_REFINERY, + STRUCT_STORAGE, + STRUCT_HELIPAD, + STRUCT_SAM, + STRUCT_AIRSTRIP, + STRUCT_POWER, + STRUCT_ADVANCED_POWER, + STRUCT_HOSPITAL, + STRUCT_BARRACKS, + STRUCT_TANKER, + STRUCT_REPAIR, + STRUCT_BIO_LAB, + STRUCT_HAND, + STRUCT_TEMPLE, + STRUCT_EYE, + STRUCT_MISSION, + + /* + ** All buildings that are never used as a prerequisite + ** for construction, follow this point. Typically, this is + ** limited to civilian structures. + */ + STRUCT_V01, + STRUCT_V02, + STRUCT_V03, + STRUCT_V04, + STRUCT_V05, + STRUCT_V06, + STRUCT_V07, + STRUCT_V08, + STRUCT_V09, + STRUCT_V10, + STRUCT_V11, + STRUCT_V12, + STRUCT_V13, + STRUCT_V14, + STRUCT_V15, + STRUCT_V16, + STRUCT_V17, + STRUCT_V18, + STRUCT_PUMP, + STRUCT_V20, + STRUCT_V21, + STRUCT_V22, + STRUCT_V23, + STRUCT_V24, + STRUCT_V25, + STRUCT_V26, + STRUCT_V27, + STRUCT_V28, + STRUCT_V29, + STRUCT_V30, + STRUCT_V31, + STRUCT_V32, + STRUCT_V33, + STRUCT_V34, + STRUCT_V35, + STRUCT_V36, + STRUCT_V37, +#ifdef OBSOLETE + STRUCT_ROAD, +#endif + STRUCT_SANDBAG_WALL, + STRUCT_CYCLONE_WALL, + STRUCT_BRICK_WALL, + STRUCT_BARBWIRE_WALL, + STRUCT_WOOD_WALL, + + STRUCT_COUNT, + STRUCT_FIRST=0 +} StructType; + +inline StructType operator++(StructType &, int); + +#define STRUCTF_NONE 0L +#define STRUCTF_ADVANCED_POWER (1L << STRUCT_ADVANCED_POWER) +#define STRUCTF_REPAIR (1L << STRUCT_REPAIR) +#define STRUCTF_EYE (1L << STRUCT_EYE) +#define STRUCTF_TEMPLE (1L << STRUCT_TEMPLE) +#define STRUCTF_HAND (1L << STRUCT_HAND) +#define STRUCTF_BIO_LAB (1L << STRUCT_BIO_LAB) +#define STRUCTF_OBELISK (1L << STRUCT_OBELISK) +#define STRUCTF_ATOWER (1L << STRUCT_ATOWER) +#define STRUCTF_WEAP (1L << STRUCT_WEAP) +#define STRUCTF_GTOWER (1L << STRUCT_GTOWER) +#define STRUCTF_RADAR (1L << STRUCT_RADAR) +#define STRUCTF_TURRET (1L << STRUCT_TURRET) +#define STRUCTF_CIV1 (1L << STRUCT_CIV1) +#define STRUCTF_CIV2 (1L << STRUCT_CIV2) +#define STRUCTF_CIV3 (1L << STRUCT_CIV3) +#define STRUCTF_CONST (1L << STRUCT_CONST) +#define STRUCTF_REFINERY (1L << STRUCT_REFINERY) +#define STRUCTF_STORAGE (1L << STRUCT_STORAGE) +#define STRUCTF_HELIPAD (1L << STRUCT_HELIPAD) +#define STRUCTF_SAM (1L << STRUCT_SAM) +#define STRUCTF_AIRSTRIP (1L << STRUCT_AIRSTRIP) +#define STRUCTF_POWER (1L << STRUCT_POWER) +#define STRUCTF_HOSPITAL (1L << STRUCT_HOSPITAL) +#define STRUCTF_BARRACKS (1L << STRUCT_BARRACKS) +#define STRUCTF_TANKER (1L << STRUCT_TANKER) +#define STRUCTF_MISSION (1L << STRUCT_MISSION) + + +/********************************************************************** +** The overlays are enumerated here. An overlay functions similarly to +** a transparent icon. It is placed over the terrain but usually falls +** "under" buildings, trees, and units. +*/ +typedef enum OverlayType { + OVERLAY_NONE=-1, + OVERLAY_CONCRETE, // Concrete. + OVERLAY_SANDBAG_WALL, // Piled sandbags. + OVERLAY_CYCLONE_WALL, // Chain-link fence. + OVERLAY_BRICK_WALL, // Solid concrete wall. + OVERLAY_BARBWIRE_WALL, // Barbed-wire wall. + OVERLAY_WOOD_WALL, // Wooden fence. + OVERLAY_TIBERIUM1, // Tiberium patch. + OVERLAY_TIBERIUM2, // Tiberium patch. + OVERLAY_TIBERIUM3, // Tiberium patch. + OVERLAY_TIBERIUM4, // Tiberium patch. + OVERLAY_TIBERIUM5, // Tiberium patch. + OVERLAY_TIBERIUM6, // Tiberium patch. + OVERLAY_TIBERIUM7, // Tiberium patch. + OVERLAY_TIBERIUM8, // Tiberium patch. + OVERLAY_TIBERIUM9, // Tiberium patch. + OVERLAY_TIBERIUM10, // Tiberium patch. + OVERLAY_TIBERIUM11, // Tiberium patch. + OVERLAY_TIBERIUM12, // Tiberium patch. + OVERLAY_ROAD, // Road/concrete piece. + OVERLAY_SQUISH, // Squish mark for overran infantry. + OVERLAY_V12, // Haystacks + OVERLAY_V13, // Haystack + OVERLAY_V14, // Wheat field + OVERLAY_V15, // Fallow field + OVERLAY_V16, // Corn field + OVERLAY_V17, // Celery field + OVERLAY_V18, // Potato field + OVERLAY_FLAG_SPOT, // Flag start location. + OVERLAY_WOOD_CRATE, // Wooden goodie crate. + OVERLAY_STEEL_CRATE, // Steel goodie crate. + + OVERLAY_COUNT, + OVERLAY_FIRST=0 +} OverlayType; + +inline OverlayType operator++(OverlayType &, int); + + +/********************************************************************** +** This specifies the infantry in the game. The "E" designation is +** similar to the army classification of enlisted soldiers. +*/ +typedef enum InfantryType{ + INFANTRY_NONE=-1, + INFANTRY_E1, // Mini-gun armed. + INFANTRY_E2, // Grenade thrower. + INFANTRY_E3, // Rocket launcher. + INFANTRY_E4, // Flame thrower equipped. + INFANTRY_E5, // Chemical thrower equipped. + INFANTRY_E7, // Engineer. + INFANTRY_RAMBO, // Commando. + + INFANTRY_C1, // Civilian + INFANTRY_C2, // Civilian + INFANTRY_C3, // Civilian + INFANTRY_C4, // Civilian + INFANTRY_C5, // Civilian + INFANTRY_C6, // Civilian + INFANTRY_C7, // Civilian + INFANTRY_C8, // Civilian + INFANTRY_C9, // Civilian + INFANTRY_C10, // Nikumba + INFANTRY_MOEBIUS, // Dr. Moebius + INFANTRY_DELPHI, // Agent "Delphi" + INFANTRY_CHAN, // Dr. Chan + + INFANTRY_COUNT, + INFANTRY_FIRST=0 +} InfantryType; + +inline InfantryType operator++(InfantryType &, int); + + +/********************************************************************** +** The game units are enumerated here. These include not only traditional +** vehicles, but also hovercraft and gunboats. +*/ +typedef enum UnitType{ + UNIT_NONE=-1, + UNIT_HTANK, // Heavy tank (Mammoth). + UNIT_MTANK, // Medium tank (M1). + UNIT_LTANK, // Light tank ('Bradly'). + UNIT_STANK, // Stealth tank (Romulan). + UNIT_FTANK, // Flame thrower tank. + UNIT_VICE, // Visceroid + UNIT_APC, // APC. + UNIT_MLRS, // MLRS rocket launcher. + UNIT_JEEP, // 4x4 jeep replacement. + UNIT_BUGGY, // Rat patrol dune buggy type. + UNIT_HARVESTER, // Resource gathering vehicle. + UNIT_ARTY, // Artillery unit. + UNIT_MSAM, // Anti-Aircraft vehicle. + UNIT_HOVER, // Hovercraft. + UNIT_MHQ, // Mobile Head Quarters. + UNIT_GUNBOAT, // Gunboat + UNIT_MCV, // Mobile construction vehicle. + UNIT_BIKE, // Nod recon motor-bike. + UNIT_TRIC, // Triceratops + UNIT_TREX, // Tyranosaurus Rex + UNIT_RAPT, // Velociraptor + UNIT_STEG, // Stegasaurus + + UNIT_COUNT, + UNIT_FIRST=0 +} UnitType; + +inline UnitType operator++(UnitType &, int); + +#define UNITF_HTANK (1L<id) + + +#endif diff --git a/DESCDLG.CPP b/DESCDLG.CPP new file mode 100644 index 0000000..7a08ef5 --- /dev/null +++ b/DESCDLG.CPP @@ -0,0 +1,184 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\descdlg.cpv 2.17 16 Oct 1995 16:49:44 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DESCDLG.CPP * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 26, 1995 * + * * + * Last Update : Jan 26, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * DescriptionClass::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "descdlg.h" + + +/*********************************************************************************************** + * DescriptionClass::Process -- Handles all the options graphic interface. * + * * + * This dialog uses an edit box to "fill-out" a description. * + * * + * INPUT: char *string - return answer here. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 12/31/1994 MML : Created. * + *=============================================================================================*/ +void DescriptionClass::Process(char *string) +{ + /* + ----------------------------------------------------------------- + Set up the window. Window x-coords are in bytes not pixels. + ----------------------------------------------------------------- + */ + Set_Window(WINDOW_EDITOR, OPTION_X, OPTION_Y, OPTION_WIDTH, OPTION_HEIGHT); + Set_Logic_Page(SeenBuff); + + /* + ----------------------------------------------------------------------- + Create Buttons. Button coords are in pixels, but are window-relative. + ----------------------------------------------------------------------- + */ + TextButtonClass optionsbtn( + BUTTON_OPTIONS, + TXT_OK, + TPF_6PT_GRAD, + 0, + BUTTON_Y); + + TextButtonClass cancelbtn( + BUTTON_CANCEL, + TXT_CANCEL, + TPF_6PT_GRAD, + 0, + BUTTON_Y); + + cancelbtn.X = OPTION_X + ((OPTION_WIDTH - optionsbtn.Width)/3)*2; + optionsbtn.X = OPTION_X + ((OPTION_WIDTH - optionsbtn.Width)/3); + optionsbtn.Add_Tail(cancelbtn); + + EditClass edit( + BUTTON_EDIT, + string, + 31, + TPF_6PT_GRAD, + 0, + EDIT_Y + ,EDIT_W); + + edit.Set_Focus(); + edit.X = OPTION_X + (OPTION_WIDTH - edit.Width)/2, + optionsbtn.Add_Tail(edit); + + /* + ** This causes left mouse button clicking within the confines of the dialog to + ** be ignored if it wasn't recognized by any other button or slider. + */ + GadgetClass dialog(OPTION_X, OPTION_Y, OPTION_WIDTH, OPTION_HEIGHT, GadgetClass::LEFTPRESS); + optionsbtn.Add_Tail(dialog); + + /* + ** This causes a right click anywhere or a left click outside the dialog region + ** to be equivalent to clicking on the return to options dialog. + */ + ControlClass background(BUTTON_OPTIONS, 0, 0, SeenBuff.Get_Width(), SeenBuff.Get_Height(), GadgetClass::LEFTPRESS|GadgetClass::RIGHTPRESS); + optionsbtn.Add_Tail(background); + + /* + ------------------- Main Processing Loop -------------------- + */ + bool display = true; + bool process = true; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=TRUE; + } + + /* + -------------- Invoke game callback ------------- + */ + Call_Back(); + + /* + -------------- Refresh display if needed -------------- + */ + if (display) { + + Window_Hide_Mouse(WINDOW_EDITOR); + + /* + --------- Draw the background ----------- + */ + Window_Box (WINDOW_EDITOR, BOXSTYLE_GREEN_BORDER); // has border, raised up + Draw_Caption(TXT_MISSION_DESCRIPTION, OPTION_X, OPTION_Y, OPTION_WIDTH); + + /* + --------- Draw the titles ----------- + */ + optionsbtn.Draw_All(); + Window_Show_Mouse(); + display = false; + } + + /* + -------------- Get user input --------------- + */ + KeyNumType input = optionsbtn.Input(); + + /* + -------------- Process Input ---------------- + */ + switch (input) { + + case KN_RETURN: + case BUTTON_OPTIONS|KN_BUTTON: + strtrim(string); + process = false; + break; + + case KN_ESC: + case BUTTON_CANCEL|KN_BUTTON: + string[0]= NULL; + strtrim(string); + process = false; + break; + + case BUTTON_EDIT|KN_BUTTON: + break; + } + } +} + diff --git a/DESCDLG.H b/DESCDLG.H new file mode 100644 index 0000000..985505b --- /dev/null +++ b/DESCDLG.H @@ -0,0 +1,69 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\descdlg.h_v 2.18 16 Oct 1995 16:47:26 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DESCDLG.H * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 26, 1995 * + * * + * Last Update : Jan 26, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------*/ + +#ifndef DESCDLG_H +#define DESCDLG_H + +#include "gadget.h" + +class DescriptionClass +{ + private: + + enum DescriptionClassEnum { + OPTION_WIDTH=216, // Width of dialog box. + OPTION_HEIGHT=122, // Height of dialog box. + OPTION_X=(((320 - OPTION_WIDTH) / 2) & ~7), + OPTION_Y=((200 - OPTION_HEIGHT) / 2), + TEXT_X=OPTION_X+32, // Title's x pos + TEXT_Y=OPTION_Y+32, // Add 11 for each following line + BUTTON_OPTIONS=1, // Button number for "Ok" + BUTTON_CANCEL, + BUTTON_EDIT, + BUTTON_X=OPTION_X+63, // Options button x pos + BUTTON_Y=OPTION_Y+102, // Options button y pos + EDIT_Y =OPTION_Y+50, + EDIT_W =180 //204, + }; + + public: + DescriptionClass(void) {}; + void Process(char *string); +}; + +#endif + + diff --git a/DIAL8.CPP b/DIAL8.CPP new file mode 100644 index 0000000..ea068ad --- /dev/null +++ b/DIAL8.CPP @@ -0,0 +1,317 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\dial8.cpv 2.18 16 Oct 1995 16:51:32 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DIAL8.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : February 6, 1995 * + * * + * Last Update : February 6, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Dial8Class::Action -- action routine for Dial8Class * + * Dial8Class::Dial8Class -- constructor for the facing dial * + * Dial8Class::Draw_Me -- render routine for Dial8Class * + * Dial8Class::Get_Direction -- retrieves direction (0-255) of dial * + * Dial8Class::Set_Direction -- sets current direction (0-255) of dial * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * Dial8Class::Dial8Class -- constructor for the facing dial * + * * + * INPUT: * + * id button ID * + * x,y,w,h dimensions in window-relative pixels * + * dir numerical initial facing value (0-255); this is the * + * value returned by WWLIB Desired_Facing8() * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/16/1994 BR : Created. * + *=========================================================================*/ +Dial8Class::Dial8Class(int id, int x, int y, int w, int h, DirType dir) : + ControlClass(id, x, y, w, h, LEFTPRESS | LEFTHELD | LEFTRELEASE, true) +{ + /* + ** Center coordinates. + */ + FaceX = X + (Width / 2); + FaceY = Y + (Height / 2); + + /* + ** Init directions. + */ + Direction = dir; // 0 - 255 + Facing = Dir_Facing(Direction); // 0 - 7 + OldFacing = Facing; // 0 - 7 + + /* + ** Compute the drawing dimensions: a 45-degree angle intersects a unity- + ** radius circle at (.707,.707). Make the decorations 8/10 of the radius, + ** and the line extend to 6/10 of the radius. Use Width/2 for x-radius, + ** Height/2 for y-radius. + */ + FacePoint[0][0] = FaceX; + FacePoint[0][1] = FaceY - (h * 8 / 2) / 10; + + FacePoint[1][0] = FaceX + (w * 7 * 8 / 2) / 100; + FacePoint[1][1] = FaceY - (h * 7 * 8 / 2) / 100; + + FacePoint[2][0] = FaceX + (w * 8 / 2) / 10; + FacePoint[2][1] = FaceY; + + FacePoint[3][0] = FaceX + (w * 7 * 8 / 2) / 100; + FacePoint[3][1] = FaceY + (h * 7 * 8 / 2) / 100; + + FacePoint[4][0] = FaceX; + FacePoint[4][1] = FaceY + (h * 8 / 2) / 10; + + FacePoint[5][0] = FaceX - (w * 7 * 8 / 2) / 100; + FacePoint[5][1] = FaceY + (h * 7 * 8 / 2) / 100; + + FacePoint[6][0] = FaceX - (w * 8 / 2) / 10; + FacePoint[6][1] = FaceY; + + FacePoint[7][0] = FaceX - (w * 7 * 8 / 2) / 100; + FacePoint[7][1] = FaceY - (h * 7 * 8 / 2) / 100; + + FaceLine[0][0] = FaceX; + FaceLine[0][1] = FaceY - (h * 6 / 2) / 10; + + FaceLine[1][0] = FaceX + (w * 7 * 6 / 2) / 100; + FaceLine[1][1] = FaceY - (h * 7 * 6 / 2) / 100; + + FaceLine[2][0] = FaceX + (w * 6 / 2) / 10; + FaceLine[2][1] = FaceY; + + FaceLine[3][0] = FaceX + (w * 7 * 6 / 2) / 100; + FaceLine[3][1] = FaceY + (h * 7 * 6 / 2) / 100; + + FaceLine[4][0] = FaceX; + FaceLine[4][1] = FaceY + (h * 6 / 2) / 10; + + FaceLine[5][0] = FaceX - (w * 7 * 6 / 2) / 100; + FaceLine[5][1] = FaceY + (h * 7 * 6 / 2) / 100; + + FaceLine[6][0] = FaceX - (w * 6 / 2) / 10; + FaceLine[6][1] = FaceY; + + FaceLine[7][0] = FaceX - (w * 7 * 6 / 2) / 100; + FaceLine[7][1] = FaceY - (h * 7 * 6 / 2) / 100; +} + + +/*************************************************************************** + * Dial8Class::Action -- activation function for Dial8Class * + * * + * INPUT: * + * flags the reason we're being called * + * key the KN_number that was pressed * + * * + * OUTPUT: * + * true = event was processed, false = event not processed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/06/1995 BR : Created. * + *=========================================================================*/ +int Dial8Class::Action(unsigned flags, KeyNumType &key) +{ + static int is_sel = 0; + + /* + ** We might end up clearing the event bits. Make sure that the sticky + ** process is properly updated anyway. + */ + Sticky_Process(flags); + + if (flags & LEFTPRESS) { + is_sel = 1; + } + + /* + ** If left mouse is clicked or held, and the dial has changed its direction, + ** invoke the parent Action routine: + ** GadgetClass::Action handles Sticky processing, & sets IsToRepaint if any + ** flag bits are set. + ** ControlClass::Action handles Peer_To_Peer notification, and substitues + ** 'key' with the button ID if any flags are set, or 0 if no flags are set + */ + if (flags & LEFTPRESS || ((flags & LEFTHELD) && is_sel)) { + /* + ** Get new dial position (0-255) + */ + Direction = (DirType)Desired_Facing8(FaceX, FaceY, Get_Mouse_X(), Get_Mouse_Y()); + + /* + ** Convert to Facing value (0-7). + */ + Facing = Dir_Facing(Direction); + + /* + ** If it's moved, redraw. + */ + if (Facing!=OldFacing) { + OldFacing = Facing; + ControlClass::Action(flags,key); + return(true); + + } else { + + /* + ** Dial hasn't moved; kill the event & return + */ + key = KN_NONE; + ControlClass::Action(0,key); + return(true); + } + + } else { + + /* + ** Otherwise, no events have occurred; kill the event if it's a LEFTRELEASE, + ** and return + */ + if (flags & LEFTRELEASE) { + key = KN_NONE; + is_sel = 0; + } + return(ControlClass::Action(0,key)); + } +} + + +/*************************************************************************** + * Dial8Class::Draw_Me -- custom render routine for Dial8Class * + * * + * INPUT: * + * forced true = draw regardless of the current redraw flag state* + * * + * OUTPUT: * + * true = gadget was redrawn, false = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/06/1995 BR : Created. * + *=========================================================================*/ +int Dial8Class::Draw_Me(int forced) +{ + /* + ** Redraw if parent indicates a redraw is needed + */ + if (ControlClass::Draw_Me(forced)) { + /* + ** Hide the mouse. + */ + + if (LogicPage == &SeenBuff) { + Hide_Mouse(); + } + + /* + ** Draw background & decorations. + */ + Draw_Box(X, Y, Width, Height, BOXSTYLE_GREEN_DOWN, true); + for (int i=0; i<8; i++) { + Draw_Box(FacePoint[i][0] - 1, FacePoint[i][1] -1, 3, 3, BOXSTYLE_GREEN_RAISED, false); + } + + /* + ** Draw the hand & its shadow. + */ + LogicPage->Draw_Line(FaceX+1, FaceY+1, FaceLine[Facing][0]+1, FaceLine[Facing][1]+1,CC_GREEN_SHADOW); + LogicPage->Draw_Line(FaceX, FaceY, FaceLine[Facing][0], FaceLine[Facing][1],CC_LIGHT_GREEN); + + /* + ** Restore the mouse. + */ + if (LogicPage == &SeenBuff) { + Show_Mouse(); + } + + return(true); + } + + return(false); +} + + +/*************************************************************************** + * Dial8Class::Get_Direction -- retrieves direction (0-255) of dial * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * DirType dial is pointing to * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/17/1994 BR : Created. * + *=========================================================================*/ +DirType Dial8Class::Get_Direction(void) const +{ + return(Direction); +} + + +/*************************************************************************** + * Dial8Class::Set_Direction -- sets current direction (0-255) of dial * + * * + * INPUT: * + * DirType to set dial to * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/17/1994 BR : Created. * + *=========================================================================*/ +void Dial8Class::Set_Direction(DirType dir) +{ + Direction = dir; + Facing = Dir_Facing(Direction); + OldFacing = Facing; + Flag_To_Redraw(); +} diff --git a/DIAL8.H b/DIAL8.H new file mode 100644 index 0000000..b9b5d43 --- /dev/null +++ b/DIAL8.H @@ -0,0 +1,76 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\dial8.h_v 2.18 16 Oct 1995 16:47:28 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DIAL8.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : 02/06/95 * + * * + * Last Update : February 6, 1995 [BR] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DIAL8_H +#define DIAL8_H + +class Dial8Class : public ControlClass +{ + public: + /* + ** Constructor/Destructor + */ + Dial8Class(int id, int x, int y, int w, int h, DirType dir); + + /* + ** Get/Set the direction the dial is currently pointing + */ + DirType Get_Direction(void) const; + void Set_Direction(DirType dir); + + /* + ** Overloaded draw routine + */ + virtual int Draw_Me(int forced = false); + + protected: + /* + ** Overloaded event processing routine + */ + virtual int Action(unsigned flags, KeyNumType &key); + + private: + int FaceX; // x-coord of center of face + int FaceY; // y-coord of center of face + int FacePoint[8][2]; // coords of the little dial decorations + int FaceLine[8][2]; // coords for drawing the dial hand + DirType Direction; // 0-255 numerical direction of dial + FacingType Facing; // numerical facing direction of dial (0 - 7) + FacingType OldFacing; // previous Facing value + +}; + +#endif + diff --git a/DIALOG.CPP b/DIALOG.CPP new file mode 100644 index 0000000..da9e371 --- /dev/null +++ b/DIALOG.CPP @@ -0,0 +1,790 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\dialog.cpv 2.17 16 Oct 1995 16:51:50 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DIALOG.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : May 18, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Clip_Text_Print -- Prints text with clipping and support. * + * Dialog_Box -- draws a dialog background box * + * Display_Place_Building -- Displays the "place building" dialog box. * + * Display_Select_Target -- Displays the "choose target" prompt. * + * Display_Status -- Display the player scenario status box. * + * Draw_Box -- Displays a highlighted box. * + * Fancy_Text_Print -- Prints text with a drop shadow. * + * Redraw_Needed -- Determine if sidebar needs to be redrawn. * + * Render_Bar_Graph -- Renders a specified bargraph. * + * Simple_Text_Print -- Prints text with a drop shadow. * + * Window_Box -- Draws a fancy box over the specified window. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * Dialog_Box -- draws a dialog background box * + * * + * INPUT: * + * x,y,w,h the usual * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/26/1995 BR : Created. * + *=============================================================================================*/ +void Dialog_Box(int x, int y, int w, int h) +{ + Draw_Box( x, y, w, h, BOXSTYLE_GREEN_BORDER, true); +} + + +/*********************************************************************************************** + * Draw_Box -- Displays a highlighted box. * + * * + * This will draw a highlighted box to the logicpage. It can * + * optionally fill the box with a color as well. This is a low level * + * function and thus, it doesn't do any graphic mode color adjustments. * + * * + * INPUT: x,y -- Upper left corner of the box to be drawn (pixels). * + * * + * w,h -- Width and height of box (in pixels). * + * * + * up -- Is the box rendered in the "up" stated? * + * * + * filled-- Is the box to be filled. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1991 JLB : Created. * + * 05/30/1992 JLB : Embedded color codes. * + * 07/31/1992 JLB : Depressed option added. * + *=============================================================================================*/ +extern void CC_Texture_Fill (void const *shapefile, int shapenum, int xpos, int ypos, int width, int height); + +void Draw_Box(int x, int y, int w, int h, BoxStyleEnum up, bool filled) +{ + static BoxStyleType const ButtonColors[BOXSTYLE_COUNT] = { + + //Filler, Shadow, Hilite, Corner colors + + { LTGREY, WHITE, DKGREY, LTGREY}, // 0 Button is down. + { LTGREY, DKGREY, WHITE, LTGREY}, // 1 Button is up w/border. + { LTBLUE, BLUE, LTCYAN, LTBLUE}, // 2 Raised blue. + { DKGREY, WHITE, BLACK, DKGREY}, // 3 Button is disabled down. + { DKGREY, BLACK, WHITE, LTGREY}, // 4 Button is disabled up. + { LTGREY, DKGREY, WHITE, LTGREY}, // 5 Button is up w/arrows. + //{ CC_GREEN_BKGD, CC_LIGHT_GREEN, CC_GREEN_SHADOW, CC_GREEN_CORNERS }, // 6 Button is down. + //{ CC_GREEN_BKGD, CC_GREEN_SHADOW, CC_LIGHT_GREEN, CC_GREEN_CORNERS }, // 7 Button is up w/border. + { CC_GREEN_BKGD, 14, 12, 13 }, // 6 Button is down. + { CC_GREEN_BKGD, 12, 14, 13 }, // 7 Button is up w/border. + { DKGREY, WHITE, BLACK, DKGREY}, // 8 Button is disabled down. + { DKGREY, BLACK, LTGREY, DKGREY}, // 9 Button is disabled up. + //{ BLACK, CC_GREEN_BOX, CC_GREEN_BOX, BLACK}, // 10 List box. + //{ BLACK, CC_GREEN_BOX, CC_GREEN_BOX, BLACK}, // 11 Menu box. + { BLACK, 14, 14, BLACK}, // 10 List box. + { BLACK, 14, 14, BLACK}, // 11 Menu box. + }; + + w--; + h--; + BoxStyleType const &style = ButtonColors[up]; + + if (filled) { + if (style.Filler == CC_GREEN_BKGD){ + CC_Texture_Fill (MixFileClass::Retrieve("BTEXTURE.SHP"), InMainLoop, x, y, w, h); + }else{ + LogicPage->Fill_Rect( x, y, x+w, y+h, style.Filler); + } + } + + switch ( up ) { + case ( BOXSTYLE_GREEN_BOX ): + LogicPage->Draw_Rect(x, y, x+w, y+h, style.Highlight); + break; + + case ( BOXSTYLE_GREEN_BORDER ): + LogicPage->Draw_Rect(x+1, y+1, x+w-1, y+h-1, style.Highlight); + break; + + default: + LogicPage->Draw_Line(x, y+h, x+w, y+h, style.Shadow); + LogicPage->Draw_Line(x+w, y, x+w, y+h, style.Shadow); + + LogicPage->Draw_Line(x, y, x+w, y, style.Highlight); + LogicPage->Draw_Line(x, y, x, y+h, style.Highlight); + + LogicPage->Put_Pixel(x, y+h, style.Corner); + LogicPage->Put_Pixel(x+w, y, style.Corner); + break; + } +} + + +/*********************************************************************************************** + * Format_Window_String -- Separates a String into Lines. * + * This function will take a long string and break it up into lines * + * which are not longer then the window width. Any character < ' ' is * + * considered a new line marker and will be replaced by a NULL. * + * * + * INPUT: char *String - string to be formated. * + * int maxlinelen - Max length of any line in pixels. * + * * + * OUTPUT: int - number of lines string is. * + * * + * WARNINGS: The string passed in will be modified - NULLs will be put * + * into each position that will be a new line. * + * * + * HISTORY: * + * 03/27/1992 SB : Created. * + * 05/18/1995 JLB : Greatly revised for new font system. * + *=============================================================================================*/ +int Format_Window_String(char * string, int maxlinelen, int & width, int & height) +{ + int linelen; + int lines = 0; + width = 0; + height = 0; + + // In no string was passed in, then there are no lines. + if (!string) return(0); + + // While there are more letters left divide the line up. + while (*string) { + linelen = 0; + height += FontHeight + FontYSpacing; + lines++; + + // While the current line is less then the max length... + while (linelen < maxlinelen && *string != '\r' && *string != '\0') { + linelen += Char_Pixel_Width(*string++); + } + + // if the line is to long... + if (linelen >= maxlinelen) { + + /* + ** Back up to an appropriate location to break. + */ + while (*string != ' ' && *string != '\r' && *string != '\0') { + linelen -= Char_Pixel_Width(*string--); + } + + } + + /* + ** Record the largest width of the worst case string. + */ + if (linelen > width) { + width = linelen; + } + + /* + ** Force a break at the end of the line. + */ + if (*string) { + *string++ = '\r'; + } + } + return(lines); +} + + +/*********************************************************************************************** + * Window_Box -- Draws a fancy box over the specified window. * + * * + * This routine will draw a fancy (shaded) box over the specified * + * window. This is the effect used to give the polished look to * + * screen rectangles without having to use art. * + * * + * INPUT: window -- Specified window to fill and border. * + * * + * style -- The style to render the window. * + * * + * OUTPUT: none * + * * + * WARNINGS: The rendering is done to the LogicPage. * + * * + * HISTORY: * + * 03/03/1992 JLB : Created. * + * 07/31/1992 JLB : Cool raised border effect. * + * 06/08/1994 JLB : Takes appropriate enumeration parameters. * + *=============================================================================================*/ +void Window_Box(WindowNumberType window, BoxStyleEnum style) +{ + int x,y,w,h; // Window dimensions. + int border; // Width of border. + + static int _border[BOXSTYLE_COUNT][2] = { + {0,0}, // 0 Simple beveled edge. + {2,4}, // 1 Wide raised border. + {1,1}, // 2 Thick beveled edge. + {2,1}, // 3 Thin raised border. + {0,0}, // 4 Simple beveled edge. + {20,0}, // 5 Simple beveled edge. + {0,0}, // 6 Simple beveled edge. + {2,4}, // 7 Wide raised border. + {0,0}, // 8 Simple beveled edge. + {20,0}, // 9 Simple beveled edge. + {0,1} // 10 Simple 1 pixel box. + }; + + x = WindowList[window][WINDOWX]<<3; + y = WindowList[window][WINDOWY]; + w = WindowList[window][WINDOWWIDTH]<<3; + h = WindowList[window][WINDOWHEIGHT]; + + /* + ** If it is to be rendered to the seenpage, then + ** hide the mouse. + */ + if (LogicPage == (&SeenBuff)) Conditional_Hide_Mouse(x,y,x+w,y+h); + + Draw_Box(x, y, w, h, style, true); + border = _border[style][1]; + + /* + ** Draw the second border if requested. + */ + if (border) { + Draw_Box(x+border, y+border, w-(border<<1), h-(border<<1), style, false); + } + + /* + ** Restore the mouse if it has been hidden and return. + */ + if (LogicPage == &SeenBuff) Conditional_Show_Mouse(); +} + + +/*********************************************************************************************** + * Simple_Text_Print -- Prints text with a drop shadow. * + * * + * This routine functions like Text_Print, but will render a drop * + * shadow (in black). * + * * + * INPUT: text -- Pointer to text to render. * + * * + * x,y -- Pixel coordinate for to print text. * + * * + * fore -- Foreground color. * + * * + * back -- Background color. * + * * + * flag -- Text print control flags. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1991 JLB : Created. * + * 10/26/94 JLB : Handles font X spacing in a more friendly manner. * + *=============================================================================================*/ +void Simple_Text_Print(char const *text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag) +{ + static int yspace=0; // Y spacing adjustment for font. + static int xspace=0; // Spacing adjustment for font. + void const * font=0; // Font to use. + +////////////////#if (0) + static unsigned char _textfontpal[16][16] = { + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 27, 26, 25, 24 }, + { 0,135, 0, 0, 0, 0, 0, 0, 0, 0, 0,136, 136,135,119, 2 }, + { 0,159, 0, 0, 0, 0, 0, 0, 0, 0, 0,142, 143,159,41 ,167 }, + + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0,157, 0, 0, 0, 0, 0, 0, 0, 0, 0,180, 180,157,158, 5 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0,179, 0, 0, 0, 0, 0, 0, 0, 0, 0,180, 180,179,178,176 }, + + { 0,123, 0, 0, 0, 0, 0, 0, 0, 0, 0,122, 122,123,125,127 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0,203, 0, 0, 0, 0, 0, 0, 0, 0, 0,204, 204,203,202,201 }, + { 0, 1, 4,166, 41, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + + { 0,203, 0, 0, 0, 0, 0, 0, 0, 0, 0,204, 204,203,202,201 }, + { 0,203, 0, 0, 0, 0, 0, 0, 0, 0, 0,204, 204,203,202,201 }, + + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + }; + static unsigned char _textpalmedium[16] = { + 0, 25, 119,41, 0, 158,0, 178, 125,0, 202,0, 0, 0, 0, 0 + }; + + static unsigned char _textpalbright[16] = { + 0, 24, 2, 4, 0, 5, 0, 176, 127,0, 201,0, 0, 0, 0, 0 + }; +///////////////////////#endif //(0) + + int point; // Requested font size. + int shadow; // Requested shadow value. + unsigned char fontpalette[16]; // Working font palette array. + memset(&fontpalette[0], back, 16); + + if ((flag & 0xf) == TPF_VCR) { + fontpalette[3] = 12; + fontpalette[9] = 15; + fontpalette[10] = 200; + fontpalette[11] = 201; + fontpalette[12] = 202; + fontpalette[13] = 203; + fontpalette[14] = 204; + fontpalette[15] = 205; + } + + char *tempstr = NULL; + + if (text){ + /* + ** remove any 0xff characters from the string + */ + tempstr = new char [strlen (text)+1]; + char *tempptr = tempstr; + + for ( int i=0 ; i>1; + break; + + case TPF_RIGHT: + x -= String_Pixel_Width(tempstr); + break; + + default: + break; + } + + if ((unsigned)x < SeenBuff.Get_Width() && (unsigned)y < SeenBuff.Get_Height()) { + LogicPage->Print(tempstr, x, y, fore, back); + } + } + if (tempstr){ + delete [] tempstr; + } + +} + + +/*********************************************************************************************** + * Fancy_Text_Print -- Prints text with a drop shadow. * + * * + * This routine functions like Text_Print, but will render a drop * + * shadow (in black). * + * * + * INPUT: text -- Text number to print. * + * * + * x,y -- Pixel coordinate for to print text. * + * * + * fore -- Foreground color. * + * * + * back -- Background color. * + * * + * flag -- Text print control flags. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is much slower than normal text print and * + * if rendered to the SEENPAGE, the intermediate rendering * + * steps could be visible. * + * * + * HISTORY: * + * 11/29/1994 JLB : Created * + *=============================================================================================*/ +void Fancy_Text_Print(int text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...) +{ + char buffer[512]; // Working staging buffer. + va_list arg; // Argument list var. + + /* + ** If the text number is valid, then process it. + */ + if (text != TXT_NONE) { + va_start(arg, flag); + + /* + ** The text string must be locked since the vsprintf function doesn't know + ** how to handle EMS pointers. + */ + char const * tptr = Text_String(text); + vsprintf(buffer, tptr, arg); + va_end(arg); + + Simple_Text_Print(buffer, x, y, fore, back, flag); + } else { + + /* + ** Just the flags are to be changed, since the text number is TXT_NONE. + */ + Simple_Text_Print((char const *)0, x, y, fore, back, flag); + } +} + + +/*********************************************************************************************** + * Fancy_Text_Print -- Prints text with a drop shadow. * + * * + * This routine functions like Text_Print, but will render a drop * + * shadow (in black). * + * * + * INPUT: text -- Pointer to text to render. * + * * + * x,y -- Pixel coordinate for to print text. * + * * + * fore -- Foreground color. * + * * + * back -- Background color. * + * * + * flag -- Text print control flags. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is much slower than normal text print and * + * if rendered to the SEENPAGE, the intermediate rendering * + * steps could be visible. * + * * + * HISTORY: * + * 12/24/1991 JLB : Created. * + * 10/26/94 JLB : Handles font X spacing in a more friendly manner. * + * 11/29/1994 JLB : Separated actual draw action. * + *=============================================================================================*/ +void Fancy_Text_Print(char const *text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...) +{ + char buffer[512]; // Working staging buffer. + va_list arg; // Argument list var. + + /* + ** If there is a valid text string pointer then build the final string into the + ** working buffer before sending it to the simple string printing routine. + */ + if (text) { + + /* + ** Since vsprintf doesn't know about EMS pointers, be sure to surround this + ** call with locking code. + */ + va_start(arg, flag); + vsprintf(buffer, text, arg); + va_end(arg); + + Simple_Text_Print(buffer, x, y, fore, back, flag); + } else { + + /* + ** Just the flags are desired to be changed, so call the simple print routine with + ** a NULL text pointer. + */ + Simple_Text_Print((char const *)0, x, y, fore, back, flag); + } +} + + +/*********************************************************************************************** + * Clip_Text_Print -- Prints text with clipping and support. * + * * + * Use this routine to print text that that should be clipped at an arbitrary right margin * + * as well as possibly recognizing characters. Typical users of this routine would * + * be list boxes. * + * * + * INPUT: text -- Reference to the text to print. * + * * + * x,y -- Pixel coordinate of the upper left corner of the text position. * + * * + * fore -- The foreground color to use. * + * * + * back -- The background color to use. * + * * + * flag -- The text print flags to use. * + * * + * width -- The maximum pixel width to draw the text. Extra characters beyond this * + * point will not be printed. * + * * + * tabs -- Optional pointer to a series of pixel tabstop positions. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +void Conquer_Clip_Text_Print(char const *text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, unsigned width, int const * tabs) +{ + char buffer[512]; + + if (text) { + strcpy(buffer, text); + + /* + ** Set the font and spacing characteristics according to the flag + ** value passed in. + */ + Simple_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, flag); + + char * source = &buffer[0]; + unsigned offset = 0; + int processing = true; + while (processing && offset < width) { + char * ptr = strchr(source, '\t'); + + /* + ** Zap the tab character. It will be processed later. + */ + if (ptr) { + *ptr = '\0'; + } + + if (*source) { + + /* + ** Scan forward until the end of the string is reached or the + ** maximum width, whichever comes first. + */ + int w = 0; + char * bptr = source; + do { + w += Char_Pixel_Width(*bptr++); + } while(*bptr && offset+w < width); + + /* + ** If the maximum width has been exceeded, then remove the last + ** character and signal that further processing is not necessary. + */ + if (offset+w >= width) { + bptr--; + w -= Char_Pixel_Width(*bptr); + *bptr = '\0'; + processing = 0; + } + + /* + ** Print this text block and advance the offset accordingly. + */ + Simple_Text_Print(source, x+offset, y, fore, back, flag); + offset += w; + } + + /* + ** If a was the terminator for this text block, then advance + ** to the next tabstop. + */ + if (ptr) { + if (tabs) { + while (offset > *tabs) { + tabs++; + } + offset = *tabs; + } else { + offset = ((offset+1 / 50) + 1) * 50; + } + source = ptr+1; + } else { + break; + } + } + } +} diff --git a/DISPLAY.CPP b/DISPLAY.CPP new file mode 100644 index 0000000..a531ef4 --- /dev/null +++ b/DISPLAY.CPP @@ -0,0 +1,3753 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\display.cpv 2.16 16 Oct 1995 16:48:24 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DISPLAY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : August 24, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * DisplayClass::AI -- Handles the maintenance tasks for the map display. * + * DisplayClass::Calculated_Cell -- Fetch a map cell based on specified method. * + * DisplayClass::Cell_Object -- Determines what has been clicked on. * + * DisplayClass::Cell_Shadow -- Determine what shadow icon to use for the cell. * + * DisplayClass::Click_Cell_Calc -- Determines cell from screen X & Y. * + * DisplayClass::Coord_To_Pixel -- Determines X and Y pixel coordinates. * + * DisplayClass::Cursor_Mark -- Set or resets the cursor display flag bits. * + * DisplayClass::DisplayClass -- Default constructor for display class. * + * DisplayClass::Draw_It -- Draws the tactical map. * + * DisplayClass::Flag_To_Redraw -- Flags the display so that it will be redrawn as soon as poss* + * DisplayClass::Get_Occupy_Dimensions -- computes width & height of the given occupy list * + * DisplayClass::Init_Clear -- Clears the display to a known state. * + * DisplayClass::Init_IO -- Creates the map's button list * + * DisplayClass::Init_Theater -- Theater-specific initialization * + * DisplayClass::Map_Cell -- Mark specified cell as having been mapped. * + * DisplayClass::Mouse_Left_Held -- Handles the left button held down. * + * DisplayClass::Mouse_Left_Press -- Handles the left mouse button press. * + * DisplayClass::Mouse_Left_Release -- Handles the left mouse button release. * + * DisplayClass::Mouse_Left_Up -- Handles the left mouse "cruising" over the map. * + * DisplayClass::Mouse_Right_Press -- Handles the right mouse button press. * + * DisplayClass::Next_Object -- Searches for next object on display. * + * DisplayClass::One_Time -- Performs any special one time initializations. * + * DisplayClass::Passes_Proximity_Check -- Determines if building placement is near friendly sq* + * DisplayClass::Pixel_To_Coord -- converts screen coord to COORDINATE * + * DisplayClass::Read_INI -- Reads map control data from INI file. * + * DisplayClass::Redraw_Icons -- Draws all terrain icons necessary. * + * DisplayClass::Redraw_Shadow -- Draw the shadow overlay. * + * DisplayClass::Refresh_Band -- Causes all cells under the rubber band to be redrawn. * + * DisplayClass::Refresh_Cells -- Redraws all cells in list. * + * DisplayClass::Remove -- Removes a game object from the rendering system. * + * DisplayClass::Repair_Mode_Control -- Controls the repair mode. * + * DisplayClass::Scroll_Map -- Scroll the tactical map in desired direction. * + * DisplayClass::Select_These -- All selectable objects in region are selected. * + * DisplayClass::Sell_Mode_Control -- Controls the sell mode. * + * DisplayClass::Set_Cursor_Pos -- Controls the display and animation of the tac cursor. * + * DisplayClass::Set_Cursor_Shape -- Changes the shape of the terrain square cursor. * + * DisplayClass::Set_View_Dimensions -- Sets the tactical display screen coordinates. * + * DisplayClass::Submit -- Adds a game object to the map rendering system. * + * DisplayClass::TacticalClass::Action -- Processes input for the tactical map. * + * DisplayClass::Text_Overlap_List -- Creates cell overlap list for specified text string. * + * DisplayClass::Write_INI -- Writes map data into INI file. * + * DisplayClass::Set_Tactical_Position -- Sets the tactical view position. * + * DisplayClass::Center_Map -- Centers the map about the currently selected objects * + * DisplayClass::Prev_Object -- Searches for the previous object on the map. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "function.h" + +/* +** These layer control elements are used to group the displayable objects +** so that proper overlap can be obtained. +*/ +LayerClass DisplayClass::Layer[LAYER_COUNT]; + +/* +** Fading tables +*/ +unsigned char DisplayClass::FadingBrighten[256]; +unsigned char DisplayClass::FadingShade[256]; +unsigned char DisplayClass::FadingLight[256]; +unsigned char DisplayClass::RemapTables[HOUSE_COUNT][3][256]; +unsigned char DisplayClass::FadingGreen[256]; +unsigned char DisplayClass::FadingYellow[256]; +unsigned char DisplayClass::FadingRed[256]; +unsigned char DisplayClass::TranslucentTable[(MAGIC_COL_COUNT+1)*256]; +unsigned char DisplayClass::WhiteTranslucentTable[(1+1)*256]; +unsigned char DisplayClass::MouseTranslucentTable[(4+1)*256]; +void const * DisplayClass::TransIconset; +unsigned char DisplayClass::UnitShadow[(USHADOW_COL_COUNT+1)*256]; +unsigned char DisplayClass::SpecialGhost[2*256]; + +void const * DisplayClass::ShadowShapes; +unsigned char DisplayClass::ShadowTrans[(SHADOW_COL_COUNT+1)*256]; + +/* +** Bit array of cell redraw flags +*/ +BooleanVectorClass DisplayClass::CellRedraw; + +/* +** The main button that intercepts user input to the map +*/ +DisplayClass::TacticalClass DisplayClass::TacButton; + +/* +** Define "_RETRIEVE" if the palette morphing tables are part of the loaded data. If this +** is undefined, then the files will be created. +*/ +#define _RETRIEVE + + +static int const TEX_X = 0; +static int const TEX_Y = 6; +static int const TEX_W = 14; + +extern MixFileClass *TheaterIcons; + +/*********************************************************************************************** + * DisplayClass::DisplayClass -- Default constructor for display class. * + * * + * This constructor for the display class just initializes some of the display settings. * + * Most settings are initialized with the correct values at the time that the Init function * + * is called. There are some cases where default values are wise and this routine fills * + * those particular ones in. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/06/1994 JLB : Created. * + *=============================================================================================*/ +DisplayClass::DisplayClass(void) +{ + TacticalCoord = 0; + ShadowShapes = 0; + TransIconset = 0; + ZoneCell = 0; + ZoneOffset = 0; + CursorSize = 0; + ProximityCheck = false; + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + IsRepairMode = false; + IsTargettingMode = false; + IsToRedraw = true; + IsRubberBand = false; + IsTentative = false; + IsSellMode = false; +} + + +/*********************************************************************************************** + * DisplayClass::One_Time -- Performs any special one time initializations. * + * * + * This routine is called from the game initialization process. It is to perform any one * + * time initializations necessary for the map display system. It allocates the staging * + * buffer needed for the radar map. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine must be called ONCE and only once. * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 05/31/1994 JLB : Handles layer system now. * + * 06/02/1994 JLB : Takes care of misc display tables and data allocation. * + *=============================================================================================*/ +void DisplayClass::One_Time(void) +{ + Set_View_Dimensions(0, Map.Get_Tab_Height()); + + MapClass::One_Time(); + + /* + ** Init the CellRedraw bit array. Do not do this in the constructor, since the + ** BooleanVector may not have been constructed yet. + */ + CellRedraw.Resize(MAP_CELL_TOTAL); + + for (LayerType layer = LAYER_FIRST; layer < LAYER_COUNT; layer++) { + Layer[layer].One_Time(); + } + + /* + ** Load the generic transparent icon set. + */ + TransIconset = MixFileClass::Retrieve("TRANS.ICN"); + + ShadowShapes = MixFileClass::Retrieve("SHADOW.SHP"); + + Set_View_Dimensions(0, Map.Get_Tab_Height()); + + /* + ** Allocate and initialize the remap tables needed for each "house". + */ + HousesType hindex; + int fade; + + for (fade = 0; fade < 3; fade++) { + for (hindex = HOUSE_FIRST; hindex < HOUSE_COUNT; hindex++) { + int color; + + switch (fade) { + case 0: + for (color = 0; color < 256; color++) { + RemapTables[hindex][fade][color] = color; + } + break; + + case 1: + Mem_Copy(FadingLight, RemapTables[hindex][fade], 256); + break; + + case 2: + Mem_Copy(FadingShade, RemapTables[hindex][fade], 256); + break; + } + Mem_Copy(&RemapTables[hindex][fade][((int)hindex+11)*16], &RemapTables[hindex][fade][(0+11)*16], 16); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Init_Clear -- clears the display to a known state * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void DisplayClass::Init_Clear(void) +{ + MapClass::Init_Clear(); + + /* + ** Clear any object being placed + */ + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + CursorSize = 0; + IsTargettingMode = false; + IsRepairMode = false; + IsRubberBand = false; + IsTentative = false; + IsSellMode = false; + + /* + ** Empty all the display's layers + */ + for (LayerType layer = LAYER_FIRST; layer < LAYER_COUNT; layer++) { + Layer[layer].Init(); + } +} + + +/*********************************************************************************************** + * DisplayClass::Init_IO -- clears & re-builds the map's button list * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void DisplayClass::Init_IO(void) +{ + MapClass::Init_IO(); + + /* + ** Re-attach our buttons to the main map button list, only in non-edit mode. + */ + if (!Debug_Map) { + TacButton.Zap(); + Add_A_Button(TacButton); + } +} + + +/*********************************************************************************************** + * DisplayClass::Init_Theater -- Performs theater-specific initialization (mixfiles, etc) * + * * + * INPUT: * + * theater new theater * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void DisplayClass::Init_Theater(TheaterType theater) +{ + char fullname[16]; + char iconname[16]; +#ifndef _RETRIEVE + static TLucentType const MouseCols[4] = { + {BLACK, BLACK, 110, 0}, + {WHITE, WHITE, 110, 0}, + {LTGREY, LTGREY, 110, 0}, + {DKGREY, DKGREY, 110, 0} + }; + static TLucentType const MagicCols[MAGIC_COL_COUNT] = { + {32,32,110,0}, + {33,33,110,0}, + {34,34,110,0}, + {35,35,110,0}, + {36,36,110,0}, + {37,37,110,0}, + {38,38,110,0}, + {39,39,110,0}, + {BLACK, BLACK, 200, 0}, + {WHITE, BLACK, 40, 0}, + {LTGREY, BLACK, 80, 0}, + {DKGREY, BLACK, 140, 0} + }; + static TLucentType const WhiteCols[1] = { + {1, WHITE, 80, 0} + }; + static TLucentType const ShadowCols[SHADOW_COL_COUNT] = { + {WHITE+1, BLACK,130,0}, + {WHITE, BLACK,170,0}, + {LTGRAY, BLACK,250,0}, + {DKGRAY, BLACK,250,0} + }; + static TLucentType const UShadowCols[USHADOW_COL_COUNT] = { + {LTGREEN, BLACK,130,0} + }; +#endif + + /* + ---------------------- Invoke parent's init routine ---------------------- + */ + MapClass::Init_Theater(theater); + + /* + ** Save the new theater value + */ + Theater = theater; + +#ifndef DEMO + /* + ** Unload old mixfiles, and cache the new ones + */ + sprintf(fullname, "%s.MIX", Theaters[Theater].Root); + if (Theater != LastTheater){ + if (TheaterData) { + delete TheaterData; + } + TheaterData = new MixFileClass(fullname); + TheaterData->Cache(); + } + +#endif + /* + ** Register the hi-res icons mix file now since it is theater specific + */ + sprintf(fullname, "%s.MIX", Theaters[Theater].Root); + strcpy (iconname, fullname); + strcpy (&iconname[4], "ICNH.MIX"); + if (Theater != LastTheater){ + if (TheaterIcons) { + delete TheaterIcons; + } + TheaterIcons = new MixFileClass(iconname); + TheaterIcons->Cache(); + } + + + + /* + ** Load the custom palette associated with this theater. + ** The fading palettes will have to be generated as well. + */ + sprintf(fullname, "%s.PAL", Theaters[theater].Root); + void const * ptr = MixFileClass::Retrieve(fullname); + Mem_Copy((void *)ptr, GamePalette, 768); + + + Mem_Copy(GamePalette, OriginalPalette, 768); + +#ifndef _RETRIEVE + /* + ** Make sure that remapping doesn't occur on the colors that cycle. + */ + memset(&GamePalette[CYCLE_COLOR_START*3], 0x3F, CYCLE_COLOR_COUNT*3); +#endif + + +#ifdef _RETRIEVE + CCFileClass(Fading_Table_Name("GREEN", theater)).Read(FadingGreen, sizeof(FadingGreen)); +#else + Build_Fading_Table(GamePalette, FadingGreen, GREEN, 110); + CCFileClass(Fading_Table_Name("GREEN", theater)).Write(FadingGreen, sizeof(FadingGreen)); +#endif + if (theater == THEATER_DESERT) { + FadingGreen[196] = 160; + } + +#ifdef _RETRIEVE + CCFileClass(Fading_Table_Name("YELLOW", theater)).Read(FadingYellow, sizeof(FadingYellow)); +#else + Build_Fading_Table(GamePalette, FadingYellow, YELLOW, 140); + CCFileClass(Fading_Table_Name("YELLOW", theater)).Write(FadingYellow, sizeof(FadingYellow)); +#endif + +#ifdef _RETRIEVE + CCFileClass(Fading_Table_Name("RED", theater)).Read(FadingRed, sizeof(FadingRed)); +#else + Build_Fading_Table(GamePalette, FadingRed, RED, 140); + CCFileClass(Fading_Table_Name("RED", theater)).Write(FadingRed, sizeof(FadingRed)); +#endif + +#ifdef _RETRIEVE + CCFileClass(Fading_Table_Name("MOUSE", theater)).Read(MouseTranslucentTable, sizeof(MouseTranslucentTable)); +#else + Build_Translucent_Table(GamePalette, &MouseCols[0], 4, MouseTranslucentTable); + CCFileClass(Fading_Table_Name("MOUSE", theater)).Write(MouseTranslucentTable, sizeof(MouseTranslucentTable)); +#endif + +// MouseDrawPtr = MouseTranslucentTable; +// MouseDrawPtr2 = Add_Long_To_Pointer(MouseTranslucentTable, 256L); +// MouseDrawVal = 1; +// MouseDrawFlags = (int)SHAPE_GHOST; + +#ifdef _RETRIEVE + CCFileClass(Fading_Table_Name("TRANS", theater)).Read(TranslucentTable, sizeof(TranslucentTable)); +#else + Build_Translucent_Table(GamePalette, &MagicCols[0], MAGIC_COL_COUNT, TranslucentTable); + CCFileClass(Fading_Table_Name("TRANS", theater)).Write(TranslucentTable, sizeof(TranslucentTable)); +#endif + +#ifdef _RETRIEVE + CCFileClass(Fading_Table_Name("WHITE", theater)).Read(WhiteTranslucentTable, sizeof(WhiteTranslucentTable)); +#else + Build_Translucent_Table(GamePalette, &WhiteCols[0], 1, WhiteTranslucentTable); + CCFileClass(Fading_Table_Name("WHITE", theater)).Write(WhiteTranslucentTable, sizeof(WhiteTranslucentTable)); +#endif + +#ifdef _RETRIEVE + CCFileClass(Fading_Table_Name("SHADOW", theater)).Read(ShadowTrans, sizeof(ShadowTrans)); +#else + Build_Translucent_Table(GamePalette, &ShadowCols[0], SHADOW_COL_COUNT, ShadowTrans); + CCFileClass(Fading_Table_Name("SHADOW", theater)).Write(ShadowTrans, sizeof(ShadowTrans)); +#endif + +#ifdef _RETRIEVE + CCFileClass(Fading_Table_Name("UNITS", theater)).Read(UnitShadow, sizeof(UnitShadow)); +#else + Conquer_Build_Translucent_Table(GamePalette, &UShadowCols[0], USHADOW_COL_COUNT, UnitShadow); + CCFileClass(Fading_Table_Name("UNITS", theater)).Write(UnitShadow, sizeof(UnitShadow)); +#endif + +#ifdef _RETRIEVE + CCFileClass(Fading_Table_Name("SHADE", theater)).Read(FadingShade, sizeof(FadingShade)); +#else + Conquer_Build_Fading_Table(GamePalette, FadingShade, BLACK, 150); + CCFileClass(Fading_Table_Name("SHADE", theater)).Write(FadingShade, sizeof(FadingShade)); +#endif + +#ifdef _RETRIEVE + CCFileClass(Fading_Table_Name("LIGHT", theater)).Read(FadingLight, sizeof(FadingLight)); +#else + Conquer_Build_Fading_Table(GamePalette, FadingLight, WHITE, 85); + CCFileClass(Fading_Table_Name("LIGHT", theater)).Write(FadingLight, sizeof(FadingLight)); +#endif + + /* + ** Create the shadow color used by aircraft. + */ + Conquer_Build_Fading_Table(GamePalette, &SpecialGhost[256], BLACK, 100); + for (int index = 0; index < 256; index++) { + SpecialGhost[index] = 0; + } + + Build_Fading_Table(GamePalette, FadingBrighten, WHITE, 25); + + +#ifndef _RETRIEVE + /* + ** Restore the palette since it was mangled while building the fading tables. + */ + sprintf(fullname, "%s.PAL", Theaters[theater].Root); + ptr = MixFileClass::Retrieve(fullname); + Mem_Copy((void *)ptr, GamePalette, 768); + Mem_Copy(GamePalette, OriginalPalette, 768); +#endif + + /* + ** Adjust the palette according to the visual control option settings. + */ + Options.Fixup_Palette(); +} + + +/*********************************************************************************************** + * DisplayClass::Text_Overlap_List -- Creates cell overlap list for specified text string. * + * * + * This routine is used to create an overlap list that specifies all the cells that are * + * covered by the specified text string. This overlap list is used to handle map refresh * + * logic. * + * * + * INPUT: text -- Pointer to the text that would appear on the map and must have an * + * overlap list generated. * + * * + * x,y -- The coordinates that the text would appear (upper left corner). * + * * + * OUTPUT: Returns with a pointer to an overlap list that covers all cells "under" the text * + * if were displayed at the coordinates specified. The list is actually a series of * + * offsets from the display's upper left corner cell number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/06/1994 JLB : Created. * + * 12/07/1994 JLB : Sidebar fixup. * + * 08/13/1995 JLB : Optimized for variable sized help text. * + *=============================================================================================*/ +short const * DisplayClass::Text_Overlap_List(char const * text, int x, int y, int lines) +{ + static short _list[30]; + + if (text) { + short * ptr = &_list[0]; + int len = String_Pixel_Width(text)+CELL_PIXEL_W; + int right = TacPixelX + Lepton_To_Pixel(TacLeptonWidth); + + /* + ** If the help text would spill into the sidebar, then flag this fact, but + ** shorten the apparent length so that the icon list calculation will + ** function correctly. + */ + if (x+len >= TacPixelX+Lepton_To_Pixel(TacLeptonWidth)) { + len = right-x; + *ptr++ = REFRESH_SIDEBAR; + } + + /* + ** Build the list of overlap cell offset values according to the text + ** coordinate and the length. + */ + int height = (((FontHeight * lines) + 23) / 24) * 24; + + if (x <= right) { + CELL ul = Click_Cell_Calc(x, y-1); + CELL lr = Click_Cell_Calc(x+len-1, Bound(y+height, TacPixelY, SeenBuff.Get_Height() - 1)); + + if (ul == -1) ul = Click_Cell_Calc(x, y); +// if (lr == -1) lr = Click_Cell_Calc(x+len, y); + + if (ul != -1 && lr != -1) { + for (int yy = Cell_Y(ul); yy <= Cell_Y(lr); yy++) { + for (int xx = Cell_X(ul); xx <= Cell_X(lr); xx++) { + *ptr++ = XY_Cell(xx, yy) - Coord_Cell(TacticalCoord); + } + } + } + } + + *ptr = REFRESH_EOL; + } + return(_list); +} + + +/*********************************************************************************************** + * DisplayClass::Set_View_Dimensions -- Sets the tactical display screen coordinates. * + * * + * Use this routine to set the tactical map screen coordinates and dimensions. This routine * + * is typically used when the screen size or position changes as a result of the sidebar * + * changing position or appearance. * + * * + * INPUT: x,y -- The X and Y pixel position on the screen for the tactical map upper left * + * corner. * + * * + * width -- The width of the tactical display (in pixels). If this parameter is * + * omitted, then the width will be as wide as the screen will allow. * + * * + * height-- The height of the tactial display (in pixels). If this parameter is * + * omitted, then the width wil be as wide as the screen will allow. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/06/1994 JLB : Created. * + * 06/27/1995 JLB : Adjusts tactical map position if necessary. * + *=============================================================================================*/ +void DisplayClass::Set_View_Dimensions(int x, int y, int width, int height) +{ + if (width == -1) { + width = SeenBuff.Get_Width() - x; + } + TacLeptonWidth = Pixel_To_Lepton(width); + + if (height == -1) { + height = SeenBuff.Get_Height() - y; + } + TacLeptonHeight = Pixel_To_Lepton(height); + + /* + ** Adjust the tactical cell if it is now in an invalid position + ** because of the changed dimensions. + */ + int xx = Coord_X(TacticalCoord) - (MapCellX * CELL_LEPTON_W); + int yy = Coord_Y(TacticalCoord) - (MapCellY * CELL_LEPTON_H); + + Confine_Rect(&xx, &yy, TacLeptonWidth, TacLeptonHeight, MapCellWidth * CELL_LEPTON_W, MapCellHeight * CELL_LEPTON_H); + + Set_Tactical_Position(XY_Coord(xx + (MapCellX * CELL_LEPTON_W), yy + (MapCellY * CELL_LEPTON_H))); + + TacPixelX = x; + TacPixelY = y; + WindowList[WINDOW_TACTICAL][WINDOWX] = x >> 3; + WindowList[WINDOW_TACTICAL][WINDOWY] = y; + WindowList[WINDOW_TACTICAL][WINDOWWIDTH] = width >> 3; + WindowList[WINDOW_TACTICAL][WINDOWHEIGHT] = height; + if (Window == WINDOW_TACTICAL) { + Change_Window(0); + Change_Window(Window); + } + IsToRedraw = true; + Flag_To_Redraw(false); + + TacButton.X = TacPixelX; + TacButton.Y = TacPixelY; + TacButton.Width = Lepton_To_Pixel(TacLeptonWidth); + TacButton.Height = Lepton_To_Pixel(TacLeptonHeight); +} + + +/*********************************************************************************************** + * DisplayClass::Set_Cursor_Shape -- Changes the shape of the terrain square cursor. * + * * + * This routine is used to set up the terrain cursor according to the size of the object * + * that is to be placed down. The terrain cursor looks like an arbitrary collection of * + * hatched square overlays. Typical use is when placing buildings. * + * * + * INPUT: list -- A pointer to the list that contains offsets to the cells that are to * + * be marked. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1994 JLB : Created. * + * 06/26/1995 JLB : Puts placement cursor into static buffer. * + *=============================================================================================*/ +void DisplayClass::Set_Cursor_Shape(short const * list) +{ + if (CursorSize) { + Cursor_Mark(ZoneCell+ZoneOffset, false); + } + + ZoneOffset = 0; + + if (list) { + int w,h; + static short _list[50]; + + memcpy(_list, list, sizeof(_list)); + CursorSize = _list; + Get_Occupy_Dimensions (w, h, CursorSize); + ZoneOffset = -(((h/2)*MAP_CELL_W)+(w/2)); + Cursor_Mark(ZoneCell+ZoneOffset, true); + } else { + CursorSize = 0; + } +} + + +/*********************************************************************************************** + * DisplayClass::Passes_Proximity_Check -- Determines if building placement is near friendly sq* + * * + * This routine is used by the building placement cursor logic to determine whether the * + * at the current cursor position if the building would be adjacent to another friendly * + * building. In cases where this is not true, then the building cannot be placed at all. * + * This determination is returned by the function. * + * * + * INPUT: object -- The building object that the current placement system is examining. * + * * + * OUTPUT: bool; Can the pending building object be placed at the present cursor location * + * checking only for proximity to friendly buildings? If this isn't for a * + * building type object, then this routine always returns true. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/06/1994 JLB : Created. * + * 06/07/1994 JLB : Handles concrete check. * + *=============================================================================================*/ +bool DisplayClass::Passes_Proximity_Check(ObjectTypeClass const *object) +{ + short const *ptr; + + /* + ** In editor mode, the proximity check always passes. + */ + if (Debug_Map) { + return(true); + } + + if (!object || !CursorSize || object->What_Am_I() != RTTI_BUILDINGTYPE) { + return(true); + } + + /* + ** Scan through all cells that the building foundation would cover. If any adjacent + ** cells to these are of friendly persuasion, then consider the proximity check to + ** have been a success. + */ + ptr = CursorSize; + while (*ptr != REFRESH_EOL) { + CELL cell = ZoneCell + ZoneOffset + *ptr++; + + for (FacingType facing = FACING_N; facing < FACING_COUNT; facing++) { + CELL newcell = Adjacent_Cell(cell, facing); + + if (!In_Radar(cell)) return(false); + + TechnoClass * base = (*this)[newcell].Cell_Techno(); + + /* + ** The special cell ownership flag allows building adjacent + ** to friendly walls and bibs even though there is no official + ** building located there. + */ + if ((*this)[newcell].Owner == PendingHouse) { + return(true); + } + + if (base && base->What_Am_I() == RTTI_BUILDING && base->House->Class->House == PendingHouse) { + return(true); + } + } + } + return(false); +} + + +/*********************************************************************************************** + * DisplayClass::Set_Cursor_Pos -- Controls the display and animation of the tac cursor. * + * * + * This routine controls the location, display, and animation of the * + * tactical map cursor. * + * * + * INPUT: pos -- Position to move the cursor do. If -1 is passed then * + * the cursor will just be hidden. If the position * + * passed is the same as the last position passed in, * + * then animation could occur (based on timers). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1991 JLB : Created. * + * 06/02/1994 JLB : Converted to member function. * + * 06/08/1994 JLB : If position is -1, then follow mouse. * + * 02/28/1995 JLB : Forces placement cursor to fit on map. * + *=============================================================================================*/ +CELL DisplayClass::Set_Cursor_Pos(CELL pos) +{ + CELL prevpos; // Last position of cursor (for jump-back reasons). + + /* + ** Follow the mouse position if no cell number is provided. + */ + if (pos == -1) { + pos = Click_Cell_Calc(Get_Mouse_X(), Get_Mouse_Y()); + } + + if (!CursorSize) { + prevpos = ZoneCell; + ZoneCell = pos; + return(prevpos); + } + + /* + ** Adjusts the position so that the placement cursor is never partway off the + ** tactical map. + */ + int w,h; + Get_Occupy_Dimensions (w, h, CursorSize); + + int x = Cell_X(pos + ZoneOffset); + int y = Cell_Y(pos + ZoneOffset); + + if (x < Coord_XCell(TacticalCoord)) x = Coord_XCell(TacticalCoord); +// if (x < TacMapX) x = TacMapX; + if (y < Coord_YCell(TacticalCoord)) y = Coord_YCell(TacticalCoord); +// if (y < TacMapY) y = TacMapY; + if (x+w >= Coord_XCell(TacticalCoord) + Lepton_To_Cell(TacLeptonWidth)) x = Coord_XCell(TacticalCoord)+Lepton_To_Cell(TacLeptonWidth)-w; +// if (x+w >= TacMapX+TacWidth) x = TacMapX+TacWidth-w; + if (y+h >= Coord_YCell(TacticalCoord) + Lepton_To_Cell(TacLeptonHeight)) x = Coord_YCell(TacticalCoord)+Lepton_To_Cell(TacLeptonHeight)-h; +// if (y+h >= TacMapY+TacHeight) y = TacMapY+TacHeight-h; + pos = XY_Cell(x, y) - ZoneOffset; + + /* + ** This checks to see if NO animation or drawing is to occur and, if so, + ** exits. + */ + if (pos == ZoneCell) return(pos); + + prevpos = ZoneCell; + + /* + ** If the cursor is visible, then handle the graphic update. + ** Otherwise, just update the global position of the cursor. + */ + if (CursorSize) { + + /* + ** Erase the old cursor (if it exists) AND the cursor is moving. + */ + if (pos != ZoneCell && ZoneCell != -1) { + Cursor_Mark(ZoneCell+ZoneOffset, false); + } + + /* + ** Render the cursor (could just be animation). + */ + if (pos != -1) { + Cursor_Mark(pos+ZoneOffset, true); + } + } + ZoneCell = pos; + ProximityCheck = Passes_Proximity_Check(PendingObject); + + return(prevpos); +} + + +/*********************************************************************************************** + * DisplayClass::Get_Occupy_Dimensions -- computes width & height of the given occupy list * + * * + * INPUT: * + * w ptr to fill in with height * + * h ptr to fill in with width * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/31/1995 BRR : Created. * + *=============================================================================================*/ +void DisplayClass::Get_Occupy_Dimensions(int & w, int & h, short const *list) +{ + int min_x = MAP_CELL_W; + int max_x = -MAP_CELL_W; + int min_y = MAP_CELL_H; + int max_y = -MAP_CELL_H; + int x,y; + + w = 0; + h = 0; + + if (!list) { + /* + ** Loop through all cell offsets, accumulating max & min x- & y-coords + */ + while (*list != REFRESH_EOL) { + /* + ** Compute x & y coords of the current cell offset. We can't use Cell_X() + ** & Cell_Y(), because they use shifts to compute the values, and if the + ** offset is negative we'll get a bogus coordinate! + */ + x = (*list) % MAP_CELL_W; + y = (*list) / MAP_CELL_H; + + max_x = MAX(max_x, x); + min_x = MIN(min_x, x); + max_y = MAX(max_y, y); + min_y = MIN(min_y, y); + + list++; + } + + w = MAX(1, max_x - min_x + 1); + h = MAX(1, max_y - min_y + 1); + } +} + + +/*********************************************************************************************** + * DisplayClass::Cursor_Mark -- Set or resets the cursor display flag bits. * + * * + * This routine will clear or set the cursor display bits on the map. * + * If the bit is set, then the cursor will be rendered on that map * + * icon. * + * * + * INPUT: pos -- Position of the upper left corner of the cursor. * + * * + * on -- Should the bit be turned on? * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure that every call to set the bits is matched by a * + * corresponding call to clear the bits. * + * * + * HISTORY: * + * 09/04/1991 JLB : Created. * + * 06/02/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void DisplayClass::Cursor_Mark(CELL pos, bool on) +{ + CELL const *ptr; + CellClass *cellptr; + + if (pos == -1) return; + + /* + ** For every cell in the CursorSize list, invoke its Redraw_Objects and + ** toggle its IsCursorHere flag + */ + ptr = CursorSize; + while (*ptr != REFRESH_EOL) { + CELL cell = pos + *ptr++; + if (In_Radar(cell)) { + cellptr = &(*this)[cell]; + cellptr->Redraw_Objects(); + if (on) { + cellptr->IsCursorHere = true; + } else { + cellptr->IsCursorHere = false; + } + } + } + + /* + ** For every cell in the PendingObjectPtr's Overlap_List, invoke its + ** Redraw_Objects routine. + */ + if (PendingObjectPtr) { + ptr = PendingObjectPtr->Overlap_List(); + while (*ptr != REFRESH_EOL) { + CELL cell = pos + *ptr++; + if (In_Radar(cell)) { + cellptr = &(*this)[cell]; + cellptr->Redraw_Objects(); + } + } + } +} + + +/*********************************************************************************************** + * DisplayClass::AI -- Handles the maintenance tasks for the map display. * + * * + * This routine is called once per game display frame (15 times per second). It handles * + * the mouse shape tracking and map scrolling as necessary. * + * * + * INPUT: input -- The next key just fetched from the input queue. * + * * + * x,y -- Mouse coordinates. * + * * + * OUTPUT: Modifies the input code if necessary. When the input code is consumed, it gets * + * set to 0. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1994 JLB : Created. * + * 06/02/1994 JLB : Filters mouse click input. * + * 06/07/1994 JLB : Fixed so template click will behave right. * + * 10/14/1994 JLB : Changing cursor shape over target. * + * 12/31/1994 JLB : Takes mouse coordinates as parameters. * + * 06/27/1995 JLB : Breaks out of rubber band mode if mouse leaves map. * + *=============================================================================================*/ +void DisplayClass::AI(KeyNumType & input, int x, int y) +{ + if ( + IsRubberBand && + (Get_Mouse_X() < TacPixelX || + Get_Mouse_Y() < TacPixelY || + Get_Mouse_X() >= (TacPixelX + Lepton_To_Pixel(TacLeptonWidth)) || + Get_Mouse_Y() >= (TacPixelY + Lepton_To_Pixel(TacLeptonHeight)))) { + Mouse_Left_Release(-1, Get_Mouse_X(), Get_Mouse_Y(), NULL, ACTION_NONE); + } + + MapClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * DisplayClass::Submit -- Adds a game object to the map rendering system. * + * * + * This routine is used to add an arbitrary (but tangible) game object to the map. It will * + * be rendered (made visible) once it is submitted to this function. This function builds * + * the list of game objects that get rendered each frame as necessary. It is possible to * + * submit the game object to different rendering layers. All objects in a layer get drawn * + * at the same time. Using this layer method it becomes possible to have objects "below" * + * other objects. * + * * + * INPUT: object -- Pointer to the object to add. * + * * + * layer -- The layer to add the object to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 05/31/1994 JLB : Improved layer system. * + * 05/31/1994 JLB : Sorts object position if this is for the ground layer. * + *=============================================================================================*/ +void DisplayClass::Submit(ObjectClass const * object, LayerType layer) +{ + if (object) { + Layer[layer].Submit(object, (layer == LAYER_GROUND)); + } +} + + +/*********************************************************************************************** + * DisplayClass::Remove -- Removes a game object from the rendering system. * + * * + * Every object that is to disappear from the map must be removed from the rendering * + * system. * + * * + * INPUT: object -- The object to remove. * + * * + * layer -- The layer to remove it from. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 05/31/1994 JLB : Improved layer system. * + *=============================================================================================*/ +void DisplayClass::Remove(ObjectClass const * object, LayerType layer) +{ + if (object) { + Layer[layer].Delete((ObjectClass *)object); + } +} + + +/*********************************************************************************************** + * DisplayClass::Click_Cell_Calc -- Determines cell from screen X & Y. * + * * + * This routine is used to determine the cell that is located at the * + * screen pixel coordinates given. Typical use is when the player * + * clicks with the mouse on the tactical map. * + * * + * INPUT: x,y -- Screen pixel coordinates. * + * * + * OUTPUT: Returns with cell that is under the coordinates specified. * + * If the coordinate specified is outside of the tactical * + * map, then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +CELL DisplayClass::Click_Cell_Calc(int x, int y) +{ + x -= TacPixelX; + x = Pixel_To_Lepton(x); + y -= TacPixelY; + y = Pixel_To_Lepton(y); + + if ((unsigned)x < TacLeptonWidth && + (unsigned)y < TacLeptonHeight) { + + COORDINATE tcoord = XY_Coord(Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(TacticalCoord))), Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(TacticalCoord)))); + + return(Coord_Cell(Coord_Add(tcoord, XY_Coord(x, y)))); + } + return(-1); +} + + +/*********************************************************************************************** + * DisplayClass::Read_INI -- Reads map control data from INI file. * + * * + * This routine is used to read the map control data from the INI * + * file. * + * * + * INPUT: buffer -- Pointer to the loaded INI file data. * + * * + * OUTPUT: none * + * * + * WARNINGS: The TriggerClass INI data must have been read before calling this function. * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Read_INI(char *buffer) +{ + char name[16]; + int len; // Length of data in buffer. + char *tbuffer; // Accumulation buffer of Trigger names. + char *trigsection = "CellTriggers"; + char buf[20]; // trigger name for a cell + int cell; + int i; + + /* + ** Read the map dimensions. + */ + Set_Map_Dimensions(WWGetPrivateProfileInt("MAP", "X", 1, buffer), + WWGetPrivateProfileInt("MAP", "Y", 1, buffer), + WWGetPrivateProfileInt("MAP", "Width", MAP_CELL_W-2, buffer), + WWGetPrivateProfileInt("MAP", "Height", MAP_CELL_H-2, buffer)); + + /* + ** The theater is determined at this point. There is specific data that + ** is custom to this data. Load the custom data (as it related to terrain) + ** at this point. + */ + WWGetPrivateProfileString("MAP", "Theater", Theaters[THEATER_DESERT].Name, name, 13, buffer); + Theater = Theater_From_Name(name); + + /* + ** Remove any old theater specific uncompressed shapes + */ + if (Theater != LastTheater){ + Reset_Theater_Shapes(); + } + + /* + ** Now that the theater is known, init the entire map hierarchy + */ + Init(Theater); + + /* + ** Special initializations occur when the theater is known. + */ + TerrainTypeClass::Init(Theater); + TemplateTypeClass::Init(Theater); + OverlayTypeClass::Init(Theater); + UnitTypeClass::Init(Theater); + InfantryTypeClass::Init(Theater); + BuildingTypeClass::Init(Theater); + BulletTypeClass::Init(Theater); + AnimTypeClass::Init(Theater); + AircraftTypeClass::Init(Theater); + SmudgeTypeClass::Init(Theater); + + LastTheater = Theater; + + /* + ** Read the Waypoint entries. + */ + for (i = 0; i < WAYPT_COUNT; i++) { + sprintf(buf,"%d",i); + Waypoint[i] = WWGetPrivateProfileInt ("Waypoints",buf,-1,buffer); + if (Waypoint[i] != -1) { + (*this)[Waypoint[i]].IsWaypoint = 1; + } + } + + /* + ** Set the starting position (do this after Init(), which clears the cells' + ** IsWaypoint flags). + */ + if (Waypoint[WAYPT_HOME] == -1) { + Waypoint[WAYPT_HOME] = XY_Cell(MapCellX, MapCellY); + } + Set_Tactical_Position(Cell_Coord(Waypoint[WAYPT_HOME])&0xFF00FF00L); + Views[0] = Views[1] = Views[2] = Views[3] = Waypoint[WAYPT_HOME]; + + /* + ** Read the cell trigger names, and assign TriggerClass pointers + */ + len = strlen(buffer) + 2; // len is the length of the INI data + tbuffer = buffer + len; // tbuffer is after the INI data + + /* + ** Read all entry names into 'tbuffer'. + */ + WWGetPrivateProfileString(trigsection, NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); + + /* + ** Loop through all CellTrigger entries. + */ + while (*tbuffer != '\0') { + + /* + ** Get a cell trigger assignment. + */ + WWGetPrivateProfileString(trigsection, tbuffer, NULL, buf, sizeof(buf) - 1, buffer); + + /* + ** Get cell # from entry name. + */ + cell = atoi(tbuffer); + if (cell > 0 && cell < MAP_CELL_TOTAL && !(*this)[cell].IsTrigger) { + + /* + ** Assign trigger pointer using trigger name. + */ + CellTriggers[cell] = TriggerClass::As_Pointer(buf); + if (CellTriggers[cell]) { + (*this)[cell].IsTrigger = 1; + if (CellTriggers[cell]) { + CellTriggers[cell]->AttachCount++; + } + } + } + + /* + ** Step to next entry name. + */ + tbuffer += strlen(tbuffer) + 1; + } +} + + +/*********************************************************************************************** + * DisplayClass::Write_INI -- Writes map data into INI file. * + * * + * This routine is used to write the map control data into the INI * + * file. The scenario editor uses this when creating the scenario * + * startup file. * + * * + * INPUT: buffer -- Pointer to INI file data. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Write_INI(char *buffer) +{ + char entry[20]; + + /* + ** Save the map parameters. + */ + WWWritePrivateProfileString("MAP", "Theater", Theaters[Theater].Name, buffer); + WWWritePrivateProfileInt("MAP", "X", MapCellX, buffer); + WWWritePrivateProfileInt("MAP", "Y", MapCellY, buffer); + WWWritePrivateProfileInt("MAP", "Width", MapCellWidth, buffer); + WWWritePrivateProfileInt("MAP", "Height", MapCellHeight, buffer); + + /* + ** Save the Waypoint entries. + */ + for (int i = 0; i < WAYPT_COUNT; i++) { + sprintf(entry,"%d",i); + WWWritePrivateProfileInt ("Waypoints",entry,Waypoint[i],buffer); + } + + /* + ** Erase the CellTriggers section. + */ + WWWritePrivateProfileString("CellTriggers",NULL,NULL,buffer); + + /* + ** Save the cell's triggers. + */ + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if ((*this)[cell].IsTrigger) { + + /* + ** Get cell trigger pointer. + */ + TriggerClass const * trig = CellTriggers[cell]; + + /* + ** Generate entry name. + */ + sprintf(entry,"%d",cell); + + /* + ** Save entry. + */ + WWWritePrivateProfileString("CellTriggers", entry, trig->Get_Name(), buffer); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Scroll_Map -- Scroll the tactical map in desired direction. * + * * + * This routine is used to scroll the tactical map view in the desired * + * direction. It can also be used to determine if scrolling would be * + * legal without actually performing any scrolling action. * + * * + * INPUT: facing -- The direction to scroll the tactical map. * + * * + * distance -- The distance in leptons to scroll the map. * + * * + * really -- Should the map actually be scrolled? If false, * + * then only the legality of a scroll is checked. * + * * + * OUTPUT: bool; Would scrolling in the desired direction be possible? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 05/20/1994 JLB : Converted to member function. * + * 08/09/1995 JLB : Added distance parameter. * + * 08/10/1995 JLB : Any direction scrolling. * + *=============================================================================================*/ +bool DisplayClass::Scroll_Map(DirType facing, int & distance, bool really) +{ + /* + ** If the distance is invalid then no further checking is required. Bail + ** with a no-can-do flag. + */ + if (distance == 0) return(false); + FacingType crude = Dir_Facing(facing); + + if (Coord_X(TacticalCoord) == Cell_To_Lepton(MapCellX) && crude != FACING_W) { + if (crude == FACING_SW) facing = DIR_S; + if (crude == FACING_NW) facing = DIR_N; + } + if (Coord_Y(TacticalCoord) == Cell_To_Lepton(MapCellY) && crude != FACING_N) { + if (crude == FACING_NW) facing = DIR_W; + if (crude == FACING_NE) facing = DIR_E; + } + if (Coord_X(TacticalCoord) + TacLeptonWidth == Cell_To_Lepton(MapCellX+MapCellWidth) && crude != FACING_E) { + if (crude == FACING_NE) facing = DIR_N; + if (crude == FACING_SE) facing = DIR_S; + } + if (Coord_Y(TacticalCoord) + TacLeptonHeight == Cell_To_Lepton(MapCellY+MapCellHeight) && crude != FACING_S) { + if (crude == FACING_SE) facing = DIR_E; + if (crude == FACING_SW) facing = DIR_W; + } + + /* + ** Determine the coordinate that it wants to scroll to. + */ + COORDINATE coord = Coord_Move(TacticalCoord, facing, distance); + + /* + ** Clip the new coordinate to the edges of the game world. + */ + int xx = Coord_X(coord) - Cell_To_Lepton(MapCellX); + int yy = Coord_Y(coord) - Cell_To_Lepton(MapCellY); + bool shifted = Confine_Rect(&xx, &yy, TacLeptonWidth, TacLeptonHeight, Cell_To_Lepton(MapCellWidth), Cell_To_Lepton(MapCellHeight)); + if (xx < 0) { + xx = 0; + shifted = true; + } + if (yy < 0) { + yy = 0; + shifted = true; + } + coord = XY_Coord(xx + Cell_To_Lepton(MapCellX), yy + Cell_To_Lepton(MapCellY)); + + /* + ** If the desired scroll was bound by the edge of the map, then adjust the distance to more accurately + ** reflect the actual distance moved. + */ + if (shifted) { + distance = Distance(TacticalCoord, coord); + } + + /* + ** If the new coordinate is the same as the old, then no scrolling would occur. + */ + if (!distance || coord == TacticalCoord) return(false); + + /* + ** Since the new coordinate is different than the old one, possibly adjust the real + ** tactical map accordingly. + */ + if (really) { + Set_Tactical_Position(coord); + IsToRedraw = true; + Flag_To_Redraw(false); + } + return(true); +} + + +/*********************************************************************************************** + * DisplayClass::Refresh_Cells -- Redraws all cells in list. * + * * + * This routine is used to flag all cells in the specified list for * + * redrawing. * + * * + * INPUT: cell -- The origin cell that the list is offset from. * + * * + * list -- Pointer to a list of offsets from the origin cell. * + * Each cell so specified is flagged for redraw. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is rather slow (by definition). * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + * 08/01/1994 JLB : Simplified. * + *=============================================================================================*/ +void DisplayClass::Refresh_Cells(CELL cell, short const *list) +{ + if (*list == REFRESH_SIDEBAR) { + list++; + } + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if (In_Radar(newcell)) { + (*this)[newcell].Redraw_Objects(); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Cell_Shadow -- Determine what shadow icon to use for the cell. * + * * + * This routine will examine the specified cell and adjacent cells to * + * determine what shadow icon to use. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: Returns with the shadow icon to use. -2= all black. * + * -1= map cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/01/1994 JLB : Created. * + * 04/04/1994 JLB : Revamped for new shadow icon method. * + * 04/30/1994 JLB : Converted to member function. * + *=============================================================================================*/ +int DisplayClass::Cell_Shadow(CELL cell) +{ + int index; + int value = -1; + CellClass *cellptr; + static char const CardShadow[16] = {-2,0,1,2,3,-1,4,-1,5,6,-1,-1,7,-1,-1,-1}; + static char const DiagShadow[16] = {-2,8,9,-1,10,-1,-1,-1,11,-1,-1,-1,-1,-1,-1,-1}; + + /* + ** Don't map cells that are at the top or bottom edge. This solves + ** problem of accessing cells off the top or bottom of the map and into + ** who-knows-what memory. + */ + if ((unsigned)(Cell_Y(cell)-1) > MAP_CELL_H-2) return(-2); + + cellptr = &(*this)[cell]; + if (!cellptr->IsMapped) { + + /* + ** Check the cardinal directions first. This will either result + ** in a solution or the flag to check the diagonals. + */ + index = 0; + cellptr--; + if (cellptr->IsMapped) index |= 0x08; + cellptr += MAP_CELL_W+1; + if (cellptr->IsMapped) index |= 0x04; + cellptr -= MAP_CELL_W-1; + if (cellptr->IsMapped) index |= 0x02; + cellptr -= MAP_CELL_W+1; + if (cellptr->IsMapped) index |= 0x01; + value = CardShadow[index]; + + /* + ** The diagonals must be checked, since the cardinal directions + ** did not yield a valid result. + */ + if (value == -2) { + index = 0; + cellptr--; + if (cellptr->IsMapped) index |= 0x08; + cellptr += MAP_CELL_W*2; + if (cellptr->IsMapped) index |= 0x04; + cellptr += 2; + if (cellptr->IsMapped) index |= 0x02; + cellptr -= MAP_CELL_W*2; + if (cellptr->IsMapped) index |= 0x01; + value = DiagShadow[index]; + } + + /* + ** Randomizer should go here. Add sets in multiples of 12. + */ + + } + return(value); +} + + +/*********************************************************************************************** + * DisplayClass::Map_Cell -- Mark specified cell as having been mapped. * + * * + * This routine maps the specified cell. The cell must not already * + * have been mapped and the mapping player must be the human. * + * This routine will update any adjacent cell map icon as appropriate. * + * * + * INPUT: cell -- The cell to be mapped. * + * * + * house -- The player that is doing the mapping. * + * * + * OUTPUT: bool; Was action taken to map this cell? * + * * + * WARNINGS: none. * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + * 05/24/1994 JLB : Takes pointer to HouseClass. * + *=============================================================================================*/ +bool DisplayClass::Map_Cell(CELL cell, HouseClass * house) +{ + if (house != PlayerPtr || cell >= (CELL)Size) return(false); + + /* + ** Don't bother remapping this cell if it is already mapped. + */ + if ((*this)[cell].IsMapped) { + return(false); + } + + /* + ** Mark the cell as being mapped. + */ + (*this)[cell].IsMapped = true; + (*this)[cell].IsVisible = true; + (*this)[cell].Redraw_Objects(); + + /* + ** Check out all adjacent cells to see if they need + ** to be mapped as well. This is necessary because of the + ** "unique" method of showing shadowed cells. Many combinations + ** are not allowed, and to fix this, just map the cells until + ** all is ok. + */ + for (FacingType dir = FACING_FIRST; dir < FACING_COUNT; dir++) { + int shadow; + CELL c; + + c = Adjacent_Cell(cell, dir); + + if (c != cell && !(*this)[c].IsMapped) { + shadow = Cell_Shadow(c); + + /* + ** Either map the cell or mark it to be refreshed. It + ** will probably change form if it isn't actually mapped. + */ + if (shadow == -1) { + Map_Cell(c, house); + } else { + if (shadow != -2) { + (*this)[c].IsVisible = true; + (*this)[c].Redraw_Objects(); + } + } + } + } + + TechnoClass * tech = (*this)[cell].Cell_Techno(); + if (tech) { + tech->Revealed(house); + } + return(true); +} + + +/*********************************************************************************************** + * DisplayClass::Coord_To_Pixel -- Determines X and Y pixel coordinates. * + * * + * This is the routine that figures out the location on the screen for * + * a specified coordinate. It is one of the fundamental routines * + * necessary for rendering the game objects. It performs some quick * + * tests to see if the coordinate is in a visible region and returns * + * this check as a boolean value. * + * * + * INPUT: coord -- The coordinate to check. * + * * + * x,y -- Reference to the pixel coordinates that this * + * coordinate would be when rendered. * + * * + * OUTPUT: bool; Is this coordinate in a visible portion of the map? * + * * + * WARNINGS: If the coordinate is not in a visible portion of the * + * map, then this X and Y parameters are not set. * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + * 12/15/1994 JLB : Converted to member function. * + * 01/07/1995 JLB : Uses inline functions to extract coord components. * + * 08/09/1995 JLB : Uses new coordinate system. * + *=============================================================================================*/ +#define EDGE_ZONE (CELL_LEPTON_W*2) +bool DisplayClass::Coord_To_Pixel(COORDINATE coord, int &x, int &y) +{ + if (coord) { + int xtac = Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(TacticalCoord))); + int xoff = Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(coord))); + + xoff = (xoff+EDGE_ZONE) - xtac; + if ((unsigned)xoff <= TacLeptonWidth + EDGE_ZONE*2) { + int ytac = Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(TacticalCoord))); + int yoff = Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(coord))); + + yoff = (yoff+EDGE_ZONE) - ytac; + if ((unsigned)yoff <= TacLeptonHeight + EDGE_ZONE*2) { + x = Lepton_To_Pixel(xoff)-CELL_PIXEL_W*2; + y = Lepton_To_Pixel(yoff)-CELL_PIXEL_H*2; + return(true); + } + } + } + return(false); +} + + +/*********************************************************************************************** + * DisplayClass::Push_Onto_TacMap -- Moves x & y coords to being on tactical map * + * * + * This routine expects a line to be drawn between SOURCE & DEST, so it pushes the coords to * + * be within the region bounded by TacMapX,Y - + TacMapW,H. * + * * + * INPUT: source, dest -- References to the coordinates to check. * + * * + * * + * OUTPUT: bool; Are these coordinates in a visible portion of the map? * + * Returns true if the pushed source & dest are visible, but if neither are * + * within the map, then it returns false. * + * * + * * + * HISTORY: * + * 03/27/1995 BWG : Created. * + *=============================================================================================*/ +bool DisplayClass::Push_Onto_TacMap(COORDINATE &source, COORDINATE &dest) +{ + if (!source || !dest) return(false); + + int x1 = Coord_X(source); + int y1 = Coord_Y(source); + int x2 = Coord_X(dest); + int y2 = Coord_Y(dest); + int left = Coord_X(TacticalCoord); + int right = Coord_X(TacticalCoord) + TacLeptonWidth; + int top = Coord_Y(TacticalCoord); + int bottom = Coord_Y(TacticalCoord) + TacLeptonHeight; + + if (x1 < left && x2 < left) return(false); + if (x1 > right && x2 > right) return(false); + if (y1 < top && y2 < top) return(false); + if (y1 > bottom && y2 > bottom) return(false); + + x1 = Bound(x1, left, right); + x2 = Bound(x2, left, right); + y1 = Bound(y1, top, bottom); + y2 = Bound(y2, top, bottom); + + source = XY_Coord(x1, y1); + dest = XY_Coord(x2, y2); + return(true); +} + + +/*********************************************************************************************** + * DisplayClass::Cell_Object -- Determines what has been clicked on. * + * * + * This routine is used to determine what the player has clicked on. * + * It is passed the cell that the click was on and it then examines * + * the cell and returns with a pointer to the object that is there. * + * * + * INPUT: cell -- The cell that has been clicked upon. * + * * + * x,y -- Optional offsets from the upper left corner of the cell to be used in * + * determining exactly which object in the cell is desired. * + * * + * OUTPUT: Returns with a pointer to the object that is "clickable" in * + * the specified cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * DisplayClass::Cell_Object(CELL cell, int x, int y) +{ + return(*this)[cell].Cell_Object(x, y); +} + + +/*********************************************************************************************** + * DisplayClass::Draw_It -- Draws the tactical map. * + * * + * This will draw the tactical map at the recorded position. This * + * routine is used whenever the tactical map moves or needs to be * + * completely redrawn. It will handle making the necessary adjustments * + * to accomodate a moving cursor. * + * * + * INPUT: forced -- bool; force redraw of the entire display? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/15/1991 JLB : Created. (benchmark = 292) * + * 04/15/1991 JLB : Added _cell2meta[] reference array (206) * + * 04/15/1991 JLB : Added actual map reference for terrain (207) * + * 04/16/1991 JLB : _cell2meta converted to int (194) * + * 04/16/1991 JLB : References actual CellIcon[] array (204) * + * 04/16/1991 JLB : Cell size increased to 16 x 16 (167) * + * 04/17/1991 JLB : Cell based tactical map rendering (165) * + * 04/22/1991 JLB : Uses Draw_Stamp() for icon rendering (426) * + * 04/22/1991 JLB : Draw_Stamp uses LogicPage now (276) * + * 04/23/1991 JLB : Map active location cursor (334) * + * 05/02/1991 JLB : Added smoothing and 3 icons sets (431) * + * 05/22/1991 JLB : Broken into Draw_Map() and Refresh_Map(). * + * 09/14/1991 JLB : Uses Refresh_Cell when new cells scroll onto display. * + * 05/12/1992 JLB : Destination page support. * + * 02/14/1994 JLB : Revamped. * + * 05/01/1994 JLB : Converted to member function. * + * 12/15/1994 JLB : Updated to work with display heirarchy. * + * 12/24/1994 JLB : Examines redraw bit intelligently. * + * 12/24/1994 JLB : Combined with old Refresh_Map() function. * + * 01/10/1995 JLB : Rubber band drawing. * + *=============================================================================================*/ + void DisplayClass::Draw_It(bool forced) +{ + int x,y; // Working cell index values. + + MapClass::Draw_It(forced); + + if (IsToRedraw || forced) { + IsToRedraw = false; + + /* + ** In rubber band mode, mark all cells under the "rubber band" to be + ** redrawn. + */ + Refresh_Band(); + + /* + ** If the multiplayer message system is displaying one or more messages, + ** flag all cells covered by the messages to redraw. This will prevent + ** messages from smearing the map if it scrolls. + */ + int num = Messages.Num_Messages(); + if (num) { + for (CELL cell = Coord_Cell(TacticalCoord); cell < Coord_Cell(TacticalCoord) + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { + (*this)[cell].Redraw_Objects(); + } + for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W; + cell < Coord_Cell(TacticalCoord) + MAP_CELL_W + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { + (*this)[cell].Redraw_Objects(); + } + if (num > 1) { + for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W*2; + cell < Coord_Cell(TacticalCoord) + MAP_CELL_W*2 + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { + (*this)[cell].Redraw_Objects(); + } + } + if (num > 3) { + for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W*3; + cell < Coord_Cell(TacticalCoord) + MAP_CELL_W*3 + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { + (*this)[cell].Redraw_Objects(); + } + } + if (num > 4) { + for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W*4; + cell < Coord_Cell(TacticalCoord) + MAP_CELL_W*4 + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { + (*this)[cell].Redraw_Objects(); + } + } + } + + /* + ** Check for a movement of the tactical map. If there has been some + ** movement, then part (or all) of the icons must be redrawn. + */ + if (Lepton_To_Pixel(Coord_X(DesiredTacticalCoord)) != Lepton_To_Pixel(Coord_X(TacticalCoord)) || + Lepton_To_Pixel(Coord_Y(DesiredTacticalCoord)) != Lepton_To_Pixel(Coord_Y(TacticalCoord))) { + + int xmod = Lepton_To_Pixel(Coord_X(DesiredTacticalCoord)); + int ymod = Lepton_To_Pixel(Coord_Y(DesiredTacticalCoord)); + + int oldx = Lepton_To_Pixel(Coord_X(TacticalCoord))-xmod; // Old relative offset. + int oldy = Lepton_To_Pixel(Coord_Y(TacticalCoord))-ymod; + + int oldw = Lepton_To_Pixel(TacLeptonWidth)-ABS(oldx); // Replicable width. + int oldh = Lepton_To_Pixel(TacLeptonHeight)-ABS(oldy); // Replicable height. + + if (oldw < 1) forced = true; + if (oldh < 1) forced = true; + + /* + ** Work out which map edges need to be redrawn + */ + BOOL redraw_right = (oldx < 0) ? TRUE : FALSE; //Right hand edge + BOOL redraw_left = (oldx > 0) ? TRUE : FALSE; //Left hand edge + BOOL redraw_bottom= (oldy < 0) ? TRUE : FALSE; //Bottom edge + BOOL redraw_top = (oldy > 0) ? TRUE : FALSE; //Top edge + + +//Colour_Debug(2); + /* + ** Blit any replicable block to avoid having to drawstamp. + */ + CachedIconsDrawn=0; + UnCachedIconsDrawn=0; + if (!forced && (oldw != Lepton_To_Pixel(TacLeptonWidth) || oldh != Lepton_To_Pixel(TacLeptonHeight))) { + Set_Cursor_Pos(-1); + /* + ** If hid page is in video memory then we may nned to blit from the seen page to + ** avoid blitting an overlapped region. + */ + if (HidPage.Get_IsDirectDraw() && !OverlappedVideoBlits){ + Hide_Mouse(); + SeenBuff.Blit(HidPage, + ((oldx < 0) ? -oldx : 0) +TacPixelX, + ((oldy < 0) ? -oldy : 0) +TacPixelY, + ((oldx < 0) ? 0 : oldx) +TacPixelX, + ((oldy < 0) ? 0 : oldy) +TacPixelY, + oldw, + oldh); + Show_Mouse(); + }else{ + HidPage.Blit(HidPage, + ((oldx < 0) ? -oldx : 0) +TacPixelX, + ((oldy < 0) ? -oldy : 0) +TacPixelY, + ((oldx < 0) ? 0 : oldx) +TacPixelX, + ((oldy < 0) ? 0 : oldy) +TacPixelY, + oldw, + oldh); + } + } else { + forced = true; + } + + if (oldx < 0) oldx = 0; + if (oldy < 0) oldy = 0; + + /* + ** Record new map position for future reference. + */ + ScenarioInit++; + Set_Tactical_Position(DesiredTacticalCoord); + ScenarioInit--; + + if (!forced) { + + /* + ** + ** Set the 'redraw stamp' bit for any cells that could not be copied. + ** + */ + int startx = -Lepton_To_Pixel(Coord_XLepton(TacticalCoord)); + int starty = -Lepton_To_Pixel(Coord_YLepton(TacticalCoord)); + oldw -= 24; + oldh -= 24; + + if (abs(oldx) < 0x25 && abs(oldy) < 0x25){ + + /* + ** The width of the area we redraw depends on the scroll speed + */ + int extra_x = (abs(oldx)>=16) ? 2 : 1; + int extra_y = (abs(oldy)>=16) ? 2 : 1; + + /* + ** Flag the cells across the top of the visible area if required + */ + if (redraw_top){ + for (y = starty; y <= starty+CELL_PIXEL_H*extra_y; y += CELL_PIXEL_H) { + for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) (*this)[c].Redraw_Objects(true); + } + } + } + + /* + ** Flag the cells across the bottom of the visible area if required + */ + if (redraw_bottom){ + for (y = Lepton_To_Pixel(TacLeptonHeight)-CELL_PIXEL_H*(1+extra_y); y <= Lepton_To_Pixel(TacLeptonHeight)+CELL_PIXEL_H*3; y += CELL_PIXEL_H) { + for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) (*this)[c].Redraw_Objects(true); + } + } + } + + /* + ** Flag the cells down the left of the visible area if required + */ + if (redraw_left){ + for (x = startx; x <= startx + CELL_PIXEL_W*extra_x; x += CELL_PIXEL_W) { + for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) (*this)[c].Redraw_Objects(true); + } + } + } + + /* + ** Flag the cells down the right of the visible area if required + */ + if (redraw_right){ + for (x = Lepton_To_Pixel(TacLeptonWidth)-CELL_PIXEL_W*(extra_x+1); x <= Lepton_To_Pixel(TacLeptonWidth)+CELL_PIXEL_W*3; x += CELL_PIXEL_W) { + for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) (*this)[c].Redraw_Objects(true); + } + } + } + + }else{ + + /* + ** Set the 'redraw stamp' bit for any cells that could not be copied. + */ + int startx = -Lepton_To_Pixel(Coord_XLepton(TacticalCoord)); + int starty = -Lepton_To_Pixel(Coord_YLepton(TacticalCoord)); + oldw -= 24; + oldh -= 24; + for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) { + for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) { + if (x <= oldx || x >= oldx+oldw || y <= oldy || y >= oldy+oldh) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) { + (*this)[c].Redraw_Objects(true); + } + } + } + } + } + } + + } else { + + /* + ** Set the tactical coordinate just in case the desired tactical has changed but + ** not enough to result in any visible map change. This is likely to occur with very + ** slow scroll rates. + */ + ScenarioInit++; + if (DesiredTacticalCoord != TacticalCoord) { + Set_Tactical_Position(DesiredTacticalCoord); + } + ScenarioInit--; + } + + /* + ** If the entire tactical map is forced to be redrawn, then set all the redraw flags + ** and let the normal processing take care of the rest. + */ + if (forced) { + CellRedraw.Set(); + } + +//Colour_Debug(3); + /* + ** The first order of business is to redraw all the underlying icons that are + ** flagged to be redrawn. + */ + //Redraw_Icons(CELL_BLIT_ONLY); + Redraw_Icons(0); + + /* + ** Once the icons are drawn, duplicate the bottom line of the screen into the phantom + ** area one line below the screen. This causes the predator effect to work on any + ** shape drawn at the bottom of the screen. + */ +//Colour_Debug(4); +#ifdef FIX_ME_LATER +// HidPage.Blit(HidPage, 0, HidPage.Get_Height()-1, 0, HidPage.Get_Height(), HidPage.Get_Width(), 1, false); +#endif //FIX_ME_LATER + if (HidPage.Lock()){ + + //Redraw_Icons(CELL_DRAW_ONLY); + + /* + ** Redraw the game objects layer by layer. The layer drawing occurs on the ground layer + ** first and then followed by all the layers in increasing altituded. + */ + for (LayerType layer = LAYER_GROUND; layer < LAYER_COUNT; layer++) { + for (int index = 0; index < Layer[layer].Count(); index++) { + Layer[layer][index]->Render(forced); + } + } + + /* + ** Finally, redraw the shadow overlay as necessary. + */ +//Colour_Debug(5); + Redraw_Shadow(); + } + + Redraw_Shadow_Rects(); + + HidPage.Unlock(); + +//Colour_Debug(8); + /* + ** Draw the rubber band over the top of it all. + */ + if (IsRubberBand) { + LogicPage->Draw_Rect(BandX+TacPixelX, BandY+TacPixelY, NewX+TacPixelX, NewY+TacPixelY, WHITE); + } + /* + ** Clear the redraw flags so that normal redraw flag setting can resume. + */ + CellRedraw.Reset(); +//Colour_Debug(0); + +#ifdef SCENARIO_EDITOR + /* + ** If we're placing an object (PendingObject is non-NULL), and that object + ** is NOT an icon, smudge, or overlay, draw it here. + ** Terrain, Buildings & Aircraft aren't drawn at the cell's center coord; + ** they're drawn at the upper left coord, so I have to AND the coord value + ** with 0xFF00FF00 to strip off the lepton coordinates, but leave the + ** cell coordinates. + */ + if (Debug_Map && PendingObjectPtr) { + PendingObjectPtr->Coord = PendingObjectPtr->Class_Of().Coord_Fixup(Cell_Coord(ZoneCell + ZoneOffset)); + PendingObjectPtr->Render(true); + } +#endif + } +} + + +/*********************************************************************************************** + * DisplayClass::Redraw_Icons -- Draws all terrain icons necessary. * + * * + * This routine will redraw all of the terrain icons that are flagged * + * to be redrawn. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none. * + * * + * HISTORY: * + * 02/14/1994 JLB : Created. * + * 05/01/1994 JLB : Converted to member function. * + * 06/20/1994 JLB : Uses cell drawing support function. * + * 12/06/1994 JLB : Scans tactical view in separate row/colum loops * + * 12/24/1994 JLB : Uses the cell bit flag array to determine what to redraw. * + *=============================================================================================*/ +void DisplayClass::Redraw_Icons(int draw_flags) +{ + IsShadowPresent = false; + for (int y = -Coord_YLepton(TacticalCoord); y <= TacLeptonHeight; y += CELL_LEPTON_H) { + for (int x = -Coord_XLepton(TacticalCoord); x <= TacLeptonWidth; x += CELL_LEPTON_W) { + COORDINATE coord = Coord_Add(TacticalCoord, XY_Coord(x, y)); + CELL cell = Coord_Cell(coord); + coord = Cell_Coord(cell) & 0xFF00FF00L; + + /* + ** Only cells flagged to be redraw are examined. + */ + if (In_View(cell) && Is_Cell_Flagged(cell)) { + int xpixel; + int ypixel; + + if (Coord_To_Pixel(coord, xpixel, ypixel)) { + CellClass * cellptr = &(*this)[Coord_Cell(coord)]; + + /* + ** If there is a portion of the underlying icon that could be visible, + ** then draw it. Also draw the cell if the shroud is off. + */ + if (cellptr->IsVisible || Debug_Unshroud) { + cellptr->Draw_It(xpixel, ypixel, draw_flags); + } + + /* + ** If any cell is not fully mapped, then flag it so that the shadow drawing + ** process will occur. Only draw the shadow if Debug_Unshroud is false. + */ + if (!cellptr->IsMapped && !Debug_Unshroud) { + IsShadowPresent = true; + } + } + } + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Redraw_Shadow -- Draw the shadow overlay. * + * * + * This routine is called after all other tactical map rendering takes place. It draws * + * the shadow map over the tactical map. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + * 08/06/1995 JLB : Clips the fill rect if necessary. * + *=============================================================================================*/ +void DisplayClass::Redraw_Shadow(void) +{ + if (IsShadowPresent) { + for (int y = -Coord_YLepton(TacticalCoord); y <= TacLeptonHeight; y += CELL_LEPTON_H) { + for (int x = -Coord_XLepton(TacticalCoord); x <= TacLeptonWidth; x += CELL_LEPTON_W) { + COORDINATE coord = Coord_Add(TacticalCoord, XY_Coord(x, y)); + CELL cell = Coord_Cell(coord); + coord = Cell_Coord(cell) & 0xFF00FF00; + + /* + ** Only cells flagged to be redraw are examined. + */ + if (In_View(cell) && Is_Cell_Flagged(cell)) { + int xpixel; + int ypixel; + + if (Coord_To_Pixel(coord, xpixel, ypixel)) { + CellClass * cellptr = &(*this)[Coord_Cell(coord)]; + + if (!cellptr->IsMapped) { + if (cellptr->IsVisible) { + int shadow = Cell_Shadow(cell); + if (shadow >= 0) { + CC_Draw_Shape(ShadowShapes, shadow, xpixel, ypixel, WINDOW_TACTICAL, SHAPE_GHOST, NULL, ShadowTrans); + } + } + } + } + } + } + } + } +} + +/*********************************************************************************************** + * DisplayClass::Redraw_Shadow -- Draw the shadow overlay. * + * * + * This routine is called after all other tactical map rendering takes place. It draws * + * the shadow map over the tactical map. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + * 08/06/1995 JLB : Clips the fill rect if necessary. * + *=============================================================================================*/ +void DisplayClass::Redraw_Shadow_Rects(void) +{ + if (IsShadowPresent) { + for (int y = -Coord_YLepton(TacticalCoord); y <= TacLeptonHeight; y += CELL_LEPTON_H) { + for (int x = -Coord_XLepton(TacticalCoord); x <= TacLeptonWidth; x += CELL_LEPTON_W) { + COORDINATE coord = Coord_Add(TacticalCoord, XY_Coord(x, y)); + CELL cell = Coord_Cell(coord); + coord = Cell_Coord(cell) & 0xFF00FF00; + + /* + ** Only cells flagged to be redraw are examined. + */ + if (In_View(cell) && Is_Cell_Flagged(cell)) { + int xpixel; + int ypixel; + + if (Coord_To_Pixel(coord, xpixel, ypixel)) { + CellClass * cellptr = &(*this)[Coord_Cell(coord)]; + + if (!cellptr->IsMapped) { + if (!cellptr->IsVisible) { + int ww = CELL_PIXEL_W; + int hh = CELL_PIXEL_H; + + if (Clip_Rect(&xpixel, &ypixel, &ww, &hh, Lepton_To_Pixel(TacLeptonWidth), Lepton_To_Pixel(TacLeptonHeight)) >= 0) { + LogicPage->Fill_Rect(TacPixelX+xpixel, TacPixelY+ypixel, TacPixelX+xpixel+ww-1, TacPixelY+ypixel+hh-1, BLACK); + } + } + } + } + } + } + } + } +} + +/*********************************************************************************************** + * DisplayClass::Next_Object -- Searches for next object on display. * + * * + * This utility routine is used to find the "next" object from the object specified. This * + * is typically used when is pressed and the current object shifts. * + * * + * INPUT: object -- The current object to base the "next" calculation off of. * + * * + * OUTPUT: Returns with a pointer to the next object. If there is no objects available, * + * then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * DisplayClass::Next_Object(ObjectClass * object) +{ + ObjectClass * firstobj = 0; + bool foundmatch = false; + + if (!object) { + foundmatch = true; + } + for (unsigned uindex = 0; uindex < Layer[LAYER_GROUND].Count(); uindex++) { + ObjectClass * obj = Layer[LAYER_GROUND][uindex]; + + /* + ** Verify that the object can be selected by and is owned by the player. + */ + if (obj && obj->Is_Techno() && ((TechnoClass *)obj)->IsDiscoveredByPlayer && obj->Class_Of().IsSelectable && obj->Owner() == PlayerPtr->Class->House) { + if (!firstobj) firstobj = obj; + if (foundmatch) return(obj); + if (object == obj) foundmatch = true; + } + } + return(firstobj); +} + + +/*********************************************************************************************** + * DisplayClass::Prev_Object -- Searches for the previous object on the map. * + * * + * This routine will search for the previous object. Previous is defined as the one listed * + * before the specified object in the ground layer. If there is no specified object, then * + * the last object in the ground layer is returned. * + * * + * INPUT: object -- Pointer to the object that "previous" is to be defined from. * + * * + * OUTPUT: Returns with a pointer to the object previous to the specified one. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/24/1995 JLB : Created. * + *=============================================================================================*/ +ObjectClass * DisplayClass::Prev_Object(ObjectClass * object) +{ + ObjectClass * firstobj = 0; + bool foundmatch = false; + + if (!object) { + foundmatch = true; + } + for (int uindex = Layer[LAYER_GROUND].Count()-1; uindex >= 0; uindex--) { + ObjectClass * obj = Layer[LAYER_GROUND][uindex]; + + /* + ** Verify that the object can be selected by and is owned by the player. + */ + if (obj && obj->Is_Techno() && ((TechnoClass *)obj)->IsDiscoveredByPlayer && obj->Class_Of().IsSelectable && obj->Owner() == PlayerPtr->Class->House) { + if (!firstobj) firstobj = obj; + if (foundmatch) return(obj); + if (object == obj) foundmatch = true; + } + } + return(firstobj); +} + + +/*********************************************************************************************** + * DisplayClass::Pixel_To_Coord -- converts screen coord to COORDINATE * + * * + * INPUT: * + * x,y pixel coordinates to convert * + * * + * OUTPUT: * + * COORDINATE of pixel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/09/1994 BR : Created. * + * 12/06/1994 JLB : Uses map dimension variables in display class. * + * 12/10/1994 JLB : Uses union to speed building coordinate value. * + *=============================================================================================*/ +COORDINATE DisplayClass::Pixel_To_Coord(int x, int y) +{ + /* + ** Normalize the pixel coorindates to be relative to the upper left corner + ** of the tactical map. The coordinates are expressed in leptons. + */ + x -= TacPixelX; + x = Pixel_To_Lepton(x); + y -= TacPixelY; + y = Pixel_To_Lepton(y); + + /* + ** If pixel coordinate is over the tactical map, then translate it into a coordinate + ** value. If not, then just return with NULL. + */ + if ((unsigned)x < TacLeptonWidth && (unsigned)y < TacLeptonHeight) { + return(Coord_Add(TacticalCoord, XY_Coord(x, y))); + } + return(0); +} + + +/*********************************************************************************************** + * DisplayClass::Calculated_Cell -- Fetch a map cell based on specified method. * + * * + * Find a cell meeting the specified requirements. This function is * + * used for scenario reinforcements. * + * * + * INPUT: dir -- Method of picking a map cell. * + * * + * house -- The house to base calculation on. * + * * + * OUTPUT: Returns with the calculated cell. If 0, then this indicates * + * that no legal cell was found. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 04/11/1994 JLB : Revamped. * + * 05/18/1994 JLB : Converted to member function. * + *=============================================================================================*/ +CELL DisplayClass::Calculated_Cell(SourceType dir, HousesType house) +{ + CELL cell = 0; // Working cell number. + + while (cell == 0) { + int x,y; + int index; + + /* + ** Select a candidate cell based on the desired method. + */ + switch (dir) { + + /* + ** Looks for the northern most straight path shipping lane and returns + ** the cell of one of the ends. + */ + case SOURCE_SHIPPING: + for (y = 0; y < MapCellHeight; y++) { + for (x = 0; x < MapCellWidth; x++) { + if ((*this)[XY_Cell(MapCellX+x, MapCellY+y)].Land_Type() != LAND_WATER) break; + } + if (x == MapCellWidth) { + return(XY_Cell(MapCellX+MapCellWidth, MapCellY+y)); + } + } + return(0); + + /* + ** Select a map edge. + */ + case SOURCE_NORTH: + index = Random_Pick(1, MapCellWidth); + for (x = 0; x < MapCellWidth; x++) { + cell = XY_Cell(MapCellX+((x+index)%MapCellWidth), MapCellY-1); + if ((*this)[cell].Is_Generally_Clear() && (*this)[cell+MAP_CELL_W].Is_Generally_Clear()) break; + } + if (x == MapCellWidth) return(0); + break; + + case SOURCE_EAST: + index = Random_Pick(1, MapCellHeight); + for (y = 0; y < MapCellHeight; y++) { + cell = XY_Cell(MapCellX+MapCellWidth, MapCellY+((y+index)%MapCellHeight)); + if ((*this)[cell].Is_Generally_Clear() && (*this)[cell-1].Is_Generally_Clear()) break; + } + if (y == MapCellHeight) return(0); + break; + + case SOURCE_SOUTH: + index = Random_Pick(1, MapCellWidth); + for (x = 0; x < MapCellWidth; x++) { + cell = XY_Cell(MapCellX+((x+index)%MapCellWidth), MapCellY+MapCellHeight); + if ((*this)[cell].Is_Generally_Clear() && (*this)[cell-MAP_CELL_W].Is_Generally_Clear()) break; + } + if (x == MapCellWidth) return(0); + break; + + case SOURCE_WEST: + index = Random_Pick(1, MapCellHeight); + for (y = 0; y < MapCellHeight; y++) { + cell = XY_Cell(MapCellX-1, MapCellY+((y+index)%MapCellHeight)); + if ((*this)[cell].Is_Generally_Clear() && (*this)[cell+1].Is_Generally_Clear()) break; + } + if (y == MapCellHeight) return(0); + break; + + /* + ** Drop in at a random location. + */ + case SOURCE_AIR: + cell = Waypoint[WAYPT_REINF]; + if (cell < 1) { + cell = Coord_Cell(TacticalCoord); + return(cell); + } else { + if ((*this)[cell].Cell_Techno()) { + for (int radius = 1; radius < 7; radius++) { + CELL newcell = Coord_Cell(Coord_Scatter(Cell_Coord(cell), radius << 8, true)); + if (In_Radar(newcell) && !(*this)[newcell].Cell_Techno()) { + cell = newcell; + break; + } + } + } + } + break; + + /* + ** Dramatic entry point is somewhere on the visible screen as defined + ** by the current tactical map position. + */ + case SOURCE_VISIBLE: + cell = XY_Cell(Coord_XCell(TacticalCoord)+Random_Pick(0, Lepton_To_Cell(TacLeptonWidth)-1), Coord_YCell(TacticalCoord)+Random_Pick(0, Lepton_To_Cell(TacLeptonHeight)-1)); + if (house == PlayerPtr->Class->House && !In_Radar(cell)) { + cell = 0; + } + break; + + /* + ** Drop off near friendly base or near a friendly unit or + ** just randomly if all else fails. + */ + case SOURCE_ENEMYBASE: + case SOURCE_HOMEBASE: + return(0); + + /* + ** Find an unoccupied beach cell. + */ + case SOURCE_BEACH: { + CELL cells[MAP_CELL_W]; + CELL alternate[MAP_CELL_W]; + unsigned counter=0; + + for (x = 0; x < MapCellWidth; x++) { + CELL newcell = 0; + + if ((*this)[XY_Cell(x + MapCellX, MapCellHeight + MapCellY)].Land_Type() != LAND_WATER) continue; + if ((*this)[XY_Cell(x + MapCellX, MapCellHeight + MapCellY-1)].Land_Type() != LAND_WATER) continue; + for (y = MapCellHeight; y >= 0; y--) { + newcell = XY_Cell(x + MapCellX, y + MapCellY); + if ((*this)[newcell].Cell_Techno()) { + break; + } + if ((*this)[newcell].Land_Type() != LAND_WATER) { + break; + } + } + LandType land = (*this)[newcell].Land_Type(); + if (( land == LAND_BEACH || land == LAND_CLEAR || land == LAND_ROAD) && + !(*this)[newcell].Cell_Techno() && + !(*this)[newcell].Cell_Terrain() && + !(*this)[newcell-MAP_CELL_W].Cell_Techno() && + !(*this)[newcell-MAP_CELL_W].Cell_Terrain() && + !(*this)[newcell-(MAP_CELL_W*2)].Cell_Terrain() && + !(*this)[newcell-(MAP_CELL_W*2)].Cell_Techno()) { + + cells[counter++] = newcell; + if (counter >= (sizeof(cells) / sizeof(cells[0]))) { + break; + } + } + } + + /* + ** Fixup entry list so that it doesn't come close to blocking terrain or other + ** units. + */ + int counter2 = 0; + for (int index = 1; index < counter-1; index++) { + if (Cell_X(cells[index-1])+1 == Cell_X(cells[index]) && Cell_X(cells[index+1])-1 == Cell_X(cells[index])) { + alternate[counter2++] = cells[index]; + } + } + + CELL cell = 0; + if (counter2) { + if (counter2 < 4) { + cell = alternate[counter2-1]; + } else { + cell = alternate[counter2 - (counter2 / 4)]; + } + } else { + if (counter) { + if (counter < 4) { + cell = cells[counter-1]; + } else { + cell = cells[counter - (counter / 4)]; + } + } + } + if (cell) { + if (Map.Theater == THEATER_DESERT) { + cell += MAP_CELL_W; + } + } + return(cell); + } + + case SOURCE_OCEAN: + cell = XY_Cell(Random_Pick(0, MapCellWidth-2)+MapCellX, MapCellHeight+MapCellY); + break; + + default: + return(0); + } + + /* + ** The selected edge cell must be unoccupied and if this is for + ** the player, then it must be on an accessible map cell. + */ + cell &= 0x0FFF; + if (cell && (*this)[cell].Cell_Techno()) { + cell = 0; + } + } + return(cell); +} + + +/*********************************************************************************************** + * DisplayClass::Select_These -- All selectable objects in region are selected. * + * * + * Use this routine to simultaneously select all objects within the coordinate region * + * specified. This routine is used by the multi-select rubber band handler. * + * * + * INPUT: coord1 -- Coordinate of one corner of the selection region. * + * * + * coord2 -- The opposite corner of the selection region. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 04/25/1995 JLB : Limited to non-building type. * + *=============================================================================================*/ +void DisplayClass::Select_These(COORDINATE coord1, COORDINATE coord2) +{ + COORDINATE tcoord = TacticalCoord; //Cell_Coord(TacticalCell) & 0xFF00FF00L; + + coord1 = Coord_Add(tcoord, coord1); + coord2 = Coord_Add(tcoord, coord2); + int x1 = Coord_X(coord1); + int x2 = Coord_X(coord2); + int y1 = Coord_Y(coord1); + int y2 = Coord_Y(coord2); + + /* + ** Ensure that coordinate number one represents the upper left corner + ** and coordinate number two represents the lower right corner. + */ + if (x1 > x2) { + int temp = x1; + x1 = x2; + x2 = temp; + } + if (y1 > y2) { + int temp = y1; + y1 = y2; + y2 = temp; + } + + /* + ** Sweep through all ground layer objects and select the ones within the + ** bounding box. + */ + Unselect_All(); + AllowVoice = true; + for (int index = 0; index < Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * obj = Layer[LAYER_GROUND][index]; + COORDINATE ocoord = obj->Center_Coord(); + int x = Coord_X(ocoord); + int y = Coord_Y(ocoord); + + /* + ** Only try to select objects that are owned by the player, are allowed to be + ** selected, and are within the bouding box. + */ + if ( obj->Owner() == PlayerPtr->Class->House && + obj->Class_Of().IsSelectable && + obj->What_Am_I() != RTTI_BUILDING && + x >= x1 && x <= x2 && y >= y1 && y <= y2) { + + if (obj->Select()) { + AllowVoice = false; + } + } + } + AllowVoice = true; +} + + +/*********************************************************************************************** + * DisplayClass::Refresh_Band -- Causes all cells under the rubber band to be redrawn. * + * * + * Use this routine to flag all cells that are covered in some fashion by the multi-unit * + * select "rubber band" to be redrawn. This is necessary whenever the rubber band changes * + * size or is being removed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Refresh_Band(void) +{ + if (IsRubberBand) { + + /* + ** In rubber band mode, mark all cells under the "rubber band" to be + ** redrawn. + */ + int x1 = BandX+TacPixelX; + int y1 = BandY+TacPixelY; + int x2 = NewX+TacPixelX; + int y2 = NewY+TacPixelY; + + if (x1 > x2) { + int temp = x1; + x1 = x2; + x2 = temp; + } + if (y1 > y2) { + int temp = y1; + y1 = y2; + y2 = temp; + } + + CELL cell; + for (int y = y1; y <= y2+CELL_PIXEL_H; y += CELL_PIXEL_H) { + cell = Click_Cell_Calc(x1, Bound(y, 0, TacPixelY+Lepton_To_Pixel(TacLeptonHeight))); + if (cell != -1) (*this)[cell].Redraw_Objects(); + + cell = Click_Cell_Calc(x2, Bound(y, 0, TacPixelY+Lepton_To_Pixel(TacLeptonHeight))); + if (cell != -1) (*this)[cell].Redraw_Objects(); + } + + for (int x = x1; x <= x2+CELL_PIXEL_W; x += CELL_PIXEL_W) { + cell = Click_Cell_Calc(Bound(x, 0, TacPixelX+Lepton_To_Pixel(TacLeptonWidth)), y1); + if (cell != -1) (*this)[cell].Redraw_Objects(); + + cell = Click_Cell_Calc(Bound(x, 0, TacPixelX+Lepton_To_Pixel(TacLeptonWidth)), y2); + if (cell != -1) (*this)[cell].Redraw_Objects(); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::TacticalClass::Action -- Processes input for the tactical map. * + * * + * This routine handles the input directed at the tactical map. Since input, in this * + * regard, includes even the presence of the mouse over the tactical map, this routine * + * is called nearly every game frame. It handles adjusting the mouse shape as well as * + * giving orders to units. * + * * + * INPUT: flags -- The gadget event flags that triggered the call to this function. * + * * + * key -- A reference to the keyboard event (if any). * + * * + * OUTPUT: bool; Should processing be aborted on any succeeding buttons in the chain? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/17/1995 JLB : Created. * + *=============================================================================================*/ +int DisplayClass::TacticalClass::Action(unsigned flags, KeyNumType & key) +{ + int x,y; // Sub cell pixel coordinates. + bool shadow; + ObjectClass *object = 0; + ActionType action = ACTION_NONE; // Action possible with currently selected object. + + /* + ** Set some working variables that depend on the mouse position. For the press + ** or release event, special mouse queuing storage variables are used. Other + ** events must use the current mouse position globals. + */ + bool edge = false; + if (flags & (LEFTPRESS|LEFTRELEASE|RIGHTPRESS|RIGHTRELEASE)) { + x = _Kbd->MouseQX; + y = _Kbd->MouseQY; + } else { + x = Get_Mouse_X(); + y = Get_Mouse_Y(); + + if (x == 0 || y == 199 || x == 319) edge = true; + } + COORDINATE coord = Map.Pixel_To_Coord(x, y); + CELL cell = Coord_Cell(coord); +// CELL cell = Map.Click_Cell_Calc(x, y); + if (coord) { + shadow = (!Map[cell].IsVisible && !Debug_Unshroud); + x -= Map.TacPixelX; + y -= Map.TacPixelY; + + /* + ** Cause any displayed cursor to move along with the mouse cursor. + */ + if (cell != Map.ZoneCell) { + Map.Set_Cursor_Pos(cell); + } + + /* + ** Determine the object that the mouse is currently over. + */ + if (!shadow) { +// int xxx = x + Lepton_To_Pixel(Coord_XLepton(Map.TacticalCoord)); +// int yyy = y + Lepton_To_Pixel(Coord_YLepton(Map.TacticalCoord)); +// object = Map.Cell_Object(cell, xxx % CELL_PIXEL_W, yyy % CELL_PIXEL_H); + object = Map.Close_Object(coord); + + /* + ** Special case check to ignore cloaked object if not owned by the player. + */ +// if (object && object->Is_Techno() && !((TechnoClass *)object)->IsOwnedByPlayer && ((TechnoClass *)object)->Cloak == CLOAKED) { +// object = NULL; +// } + } + + /* + ** If there is a currently selected object, then the action to perform if + ** the left mouse button were clicked must be determined. + */ + if (CurrentObject.Count()) { + if (object) { + action = CurrentObject[0]->What_Action(object); + } else { + action = CurrentObject[0]->What_Action(cell); + } + } else { + if (object && object->Class_Of().IsSelectable) { + action = ACTION_SELECT; + } + + if (Map.IsRepairMode) { + if (object && object->Owner() == PlayerPtr->Class->House && object->Can_Repair()) { + action = ACTION_REPAIR; + } else { + action = ACTION_NO_REPAIR; + } + } + + if (Map.IsSellMode) { + if (object && object->Owner() == PlayerPtr->Class->House && object->Can_Demolish()) { + if (object->What_Am_I() == RTTI_BUILDING) { + action = ACTION_SELL; + } else { + action = ACTION_SELL_UNIT; + } + } else { + + /* + ** Check to see if the cursor is over an owned wall. + */ + if (Map[cell].Overlay != OVERLAY_NONE && + OverlayTypeClass::As_Reference(Map[cell].Overlay).IsWall && + Map[cell].Owner == PlayerPtr->Class->House) { + action = ACTION_SELL; + } else { + action = ACTION_NO_SELL; + } + } + } + + if (Map.IsTargettingMode == SPC_ION_CANNON) { + action = ACTION_ION; + } + + if (Map.IsTargettingMode == SPC_NUCLEAR_BOMB) { + action = ACTION_NUKE_BOMB; + } + + if (Map.IsTargettingMode == SPC_AIR_STRIKE) { + action = ACTION_AIR_STRIKE; + } + + if (Map.PendingObject) { + action = ACTION_NONE; + } + } + + /* + ** Move any cursor displayed. + */ + if (cell != Map.ZoneCell) { + Map.Set_Cursor_Pos(cell); + } + + /* + ** A right mouse button press cancels the current action or selection. + */ + if (flags & RIGHTPRESS) { + Map.Mouse_Right_Press(); + } + + /* + ** Make sure that if the mouse button has been released and the map doesn't know about it, + ** then it must be informed. Do this by faking a mouse release event. + */ + if ((flags & LEFTUP) && Map.IsRubberBand) { + flags |= LEFTRELEASE; + } + + /* + ** When the mouse buttons aren't pressed, only the mouse cursor shape is processed. + ** The shape changes depending on what object the mouse is currently over and what + ** object is currently selected. + */ + if (!edge) { + if (flags & LEFTUP) { + Map.Mouse_Left_Up(shadow, object, action); + } + } + + /* + ** Normal actions occur when the mouse button is released. The press event is + ** intercepted and possible rubber-band mode is flagged. + */ + if (flags & LEFTRELEASE) { + Map.Mouse_Left_Release(cell, x, y, object, action); + } + + /* + ** When the mouse is first pressed on the map, then record the mouse + ** position so that a proper check before going into rubber band + ** mode can be made. Rubber band mode starts when the mouse is + ** held down and moved a certain minimum distance. + */ + if (!edge && (flags & LEFTPRESS)) { + Map.Mouse_Left_Press(x, y); + } + + /* + ** While the mouse is being held down, determine if rubber band mode should + ** start. If rubber band mode is already active, then update the size + ** and flag the map to redraw it. + */ + if (flags & LEFTHELD) { + Map.Mouse_Left_Held(x, y); + } + } + + return(GadgetClass::Action(0, key)); +} + + +/*********************************************************************************************** + * DisplayClass::Mouse_Right_Press -- Handles the right mouse button press. * + * * + * This routine is called when the right mouse button is pressed. This action is supposed * + * to cancel whatever mode or process is active. If there is nothing to cancel, then it * + * will default to unselecting any units that might be currently selected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/24/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Mouse_Right_Press(void) +{ + if (PendingObjectPtr && PendingObjectPtr->Is_Techno()) { + //PendingObjectPtr->Transmit_Message(RADIO_OVER_OUT); + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + Set_Cursor_Shape(0); + } else { + if (IsRepairMode) { + IsRepairMode = false; + } else { + if (IsSellMode) { + IsSellMode = false; + } else { + if (IsTargettingMode) { + IsTargettingMode = false; + } else { + Unselect_All(); + } + } + } + } + Set_Default_Mouse(MOUSE_NORMAL, false); +} + + +/*********************************************************************************************** + * DisplayClass::Mouse_Left_Up -- Handles the left mouse "cruising" over the map. * + * * + * This routine is called continuously while the mouse is over the tactical map but there * + * are no mouse buttons pressed. Typically, this adjusts the mouse shape and the pop-up * + * help text. * + * * + * INPUT: shadow -- Is the mouse hovering over shadowed terrain? * + * * + * object -- Pointer to the object that the mouse is currently over (may be NULL). * + * * + * action -- This is the action that the currently selected object (if any) will * + * perform if the left mouse button were clicked at this location. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/24/1995 JLB : Created. * + * 07/05/1995 JLB : Removed pop up help text for shadow and terrain after #3. * + *=============================================================================================*/ +void DisplayClass::Mouse_Left_Up(bool shadow, ObjectClass * object, ActionType action, bool wwsmall) +{ + IsTentative = false; + + /* + ** Don't allow selection of an object that is located in shadowed terrain. + ** In fact, just show the normal move cursor in order to keep the shadowed + ** terrain a mystery. + */ + if (shadow) { + switch (action) { + case ACTION_GUARD_AREA: + Set_Default_Mouse(MOUSE_AREA_GUARD, wwsmall); + break; + + case ACTION_NONE: + Set_Default_Mouse(MOUSE_NORMAL, wwsmall); + break; + + case ACTION_NO_SELL: + case ACTION_SELL: + case ACTION_SELL_UNIT: + Set_Default_Mouse(MOUSE_NO_SELL_BACK, wwsmall); + break; + + case ACTION_NO_REPAIR: + case ACTION_REPAIR: + Set_Default_Mouse(MOUSE_NO_REPAIR, wwsmall); + break; + + case ACTION_ION: + Set_Default_Mouse(MOUSE_ION_CANNON, wwsmall); + break; + + case ACTION_NUKE_BOMB: + Set_Default_Mouse(MOUSE_NUCLEAR_BOMB, wwsmall); + break; + + case ACTION_AIR_STRIKE: + Set_Default_Mouse(MOUSE_AIR_STRIKE, wwsmall); + break; + + case ACTION_NOMOVE: + if (CurrentObject.Count() && CurrentObject[0]->What_Am_I() == RTTI_AIRCRAFT) { + Set_Default_Mouse(MOUSE_NO_MOVE, wwsmall); + break; + } + // Fall into next case for non aircraft object types. + + default: + Set_Default_Mouse(MOUSE_CAN_MOVE, wwsmall); + break; + + } + } else { + + /* + ** Change the mouse shape according to the default action that will occur + ** if the mouse button were clicked at this location. + */ + switch (action) { + case ACTION_TOGGLE_SELECT: + case ACTION_SELECT: + Set_Default_Mouse(MOUSE_CAN_SELECT, wwsmall); + break; + + case ACTION_MOVE: + Set_Default_Mouse(MOUSE_CAN_MOVE, wwsmall); + break; + + case ACTION_GUARD_AREA: + Set_Default_Mouse(MOUSE_AREA_GUARD, wwsmall); + break; + + case ACTION_HARVEST: + case ACTION_ATTACK: + Set_Default_Mouse(MOUSE_CAN_ATTACK, wwsmall); + break; + + case ACTION_SABOTAGE: + Set_Default_Mouse(MOUSE_DEMOLITIONS, wwsmall); + break; + + case ACTION_ENTER: + case ACTION_CAPTURE: + Set_Default_Mouse(MOUSE_ENTER, wwsmall); + break; + + case ACTION_NOMOVE: + Set_Default_Mouse(MOUSE_NO_MOVE, wwsmall); + break; + + case ACTION_NO_SELL: + Set_Default_Mouse(MOUSE_NO_SELL_BACK, wwsmall); + break; + + case ACTION_NO_REPAIR: + Set_Default_Mouse(MOUSE_NO_REPAIR, wwsmall); + break; + + case ACTION_SELF: + Set_Default_Mouse(MOUSE_DEPLOY, wwsmall); + break; + + case ACTION_REPAIR: + Set_Default_Mouse(MOUSE_REPAIR, wwsmall); + break; + + case ACTION_SELL_UNIT: + Set_Default_Mouse(MOUSE_SELL_UNIT, wwsmall); + break; + + case ACTION_SELL: + Set_Default_Mouse(MOUSE_SELL_BACK, wwsmall); + break; + + case ACTION_ION: + Set_Default_Mouse(MOUSE_ION_CANNON, wwsmall); + break; + + case ACTION_NUKE_BOMB: + Set_Default_Mouse(MOUSE_NUCLEAR_BOMB, wwsmall); + break; + + case ACTION_AIR_STRIKE: + Set_Default_Mouse(MOUSE_AIR_STRIKE, wwsmall); + break; + + default: + Set_Default_Mouse(MOUSE_NORMAL, wwsmall); + break; + } + } + + /* + ** Give a generic help message when over shadow terrain. + */ + if (shadow) { + if (Scenario < 4) { + Help_Text(TXT_SHADOW); + } else { + Help_Text(TXT_NONE); + } + } else { + + /* + ** If the mouse is held over objects on the map, then help text may + ** pop up that tells what the object is. This call informs the help + ** system of the text name for the object under the mouse. + */ + if (object) { + int text; + int color = LTGREY; + + /* + ** Fetch the appropriate background color for help text. + */ + if (PlayerPtr->Is_Ally(object)) { + color = CC_GREEN; + } else { + if (object->Owner() == HOUSE_NONE || object->Owner() == HOUSE_NEUTRAL) { + color = LTGREY; + } else { + color = PINK; + } + } + + /* + ** Fetch the name of the object. If it is an enemy object, then + ** the exact identity is glossed over with a generic text. + */ + text = object->Full_Name(); + if (object->Is_Techno() && !((TechnoTypeClass const &)object->Class_Of()).IsNominal) { + + if (!PlayerPtr->Is_Ally(object)) { + switch (object->What_Am_I()) { + case RTTI_INFANTRY: + text = TXT_ENEMY_SOLDIER; + break; + + case RTTI_UNIT: + text = TXT_ENEMY_VEHICLE; + break; + + case RTTI_BUILDING: + if ( *((BuildingClass*)object) != STRUCT_MISSION) { + text = TXT_ENEMY_STRUCTURE; + } + break; + } + } + } + + if (Scenario > 3 || object->What_Am_I() != RTTI_TERRAIN) { + Help_Text(text, -1, -1, color); + } else { + Help_Text(TXT_NONE); + } + } else { + Help_Text(TXT_NONE); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Mouse_Left_Release -- Handles the left mouse button release. * + * * + * This routine is called when the left mouse button is released over the tactical map. * + * The release event is the workhorse of the game. Most actions occur at the moment of * + * mouse release. * + * * + * INPUT: cell -- The cell that the mouse is over. * + * * + * x,y -- The mouse pixel coordinate. * + * * + * object -- Pointer to the object that the mouse is over. * + * * + * action -- The action that the currently selected object (if any) will * + * perform. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/24/1995 JLB : Created. * + * 03/27/1995 JLB : Handles sell and repair actions. * + *=============================================================================================*/ +void DisplayClass::Mouse_Left_Release(CELL cell, int x, int y, ObjectClass * object, ActionType action, bool wwsmall) +{ + if (PendingObjectPtr) { + + /* + ** Try to place the pending object onto the map. + */ + if (ProximityCheck) { + OutList.Add(EventClass(EventClass::PLACE, PendingObjectPtr->What_Am_I(), cell + ZoneOffset)); + } else { + Speak(VOX_DEPLOY); + } + + } else { + + if (IsRubberBand) { + Refresh_Band(); + Select_These(XYPixel_Coord(BandX, BandY), XYPixel_Coord(x, y)); + + Set_Default_Mouse(MOUSE_NORMAL, wwsmall); +#ifdef NEVER + if (CurrentObject.Count()) { + if (CurrentObject[0]->Can_Player_Fire()) { + Set_Default_Mouse(MOUSE_CAN_ATTACK, wwsmall); + } else { + Set_Default_Mouse(MOUSE_NORMAL, wwsmall); + } + } else { + Set_Default_Mouse(MOUSE_NORMAL, wwsmall); + } +#endif + + IsRubberBand = false; + IsTentative = false; + Map.DisplayClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); + + } else { + + /* + ** Toggle the select state of the object. + */ + if (action == ACTION_TOGGLE_SELECT) { + if (!object || !CurrentObject.Count() || CurrentObject[0]->Owner() != PlayerPtr->Class->House) { + action = ACTION_SELECT; + } else { + if (object->IsSelected) { + object->Unselect(); + } else { + object->Select(); + } + } + } + + /* + ** Selection of other object action. + */ + if (action == ACTION_SELECT || (action == ACTION_NONE && object && object->Class_Of().IsSelectable && !object->IsSelected)) { + Unselect_All(); + object->Select(); + Set_Default_Mouse(MOUSE_NORMAL, wwsmall); +#ifdef NEVER + if (object->Can_Player_Fire()) { + Set_Default_Mouse(MOUSE_CAN_ATTACK, wwsmall); + } else { + Set_Default_Mouse(MOUSE_NORMAL, wwsmall); + } +#endif + } + + /* + ** If an action was detected as possible, then pass this action event + ** to all selected objects. + */ + if (action != ACTION_NONE && action != ACTION_SELECT) { + + /* + ** Pass the action to all the selected objects. But first, redetermine + ** what action that object should perform. This, seemingly redundant + ** process, is necessary since multiple objects could be selected and each + ** might perform a different action when the click occurs. + */ + bool doflash = true; + AllowVoice = true; + for (int index = 0; index < CurrentObject.Count(); index++) { + ObjectClass * tobject = CurrentObject[index]; + if (object) { + tobject->Active_Click_With(tobject->What_Action(object), object); + } else { + tobject->Active_Click_With(tobject->What_Action(cell), cell); + } + AllowVoice = false; + } + AllowVoice = true; + + if (action == ACTION_REPAIR && object->What_Am_I() == RTTI_BUILDING) { + OutList.Add(EventClass(EventClass::REPAIR, object->As_Target())); + } + if (action == ACTION_SELL_UNIT && object) { + switch (object->What_Am_I()) { + case RTTI_AIRCRAFT: + case RTTI_UNIT: + OutList.Add(EventClass(EventClass::SELL, object->As_Target())); + break; + + default: + break; + } + + } + if (action == ACTION_SELL) { + if (object) { + OutList.Add(EventClass(EventClass::SELL, object->As_Target())); + } else { + OutList.Add(EventClass(EventClass::SELL, ::As_Target(cell))); + } + } + if (action == ACTION_ION) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_ION_CANNON, cell)); + } + if (action == ACTION_NUKE_BOMB) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_NUCLEAR_BOMB, cell)); + } + if (action == ACTION_AIR_STRIKE) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_AIR_STRIKE, cell)); + } + } + + IsTentative = false; + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Mouse_Left_Press -- Handles the left mouse button press. * + * * + * Handle the left mouse button press while over the tactical map. If it isn't is * + * repair or sell mode, then a tentative transition to rubber band mode is flagged. If the * + * mouse moves a sufficient distance from this recorded position, then rubber band mode * + * is officially started. * + * * + * INPUT: x,y -- The mouse coordinates at the time of the press. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/24/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Mouse_Left_Press(int x, int y) +{ + if (!IsRepairMode && !IsSellMode && !IsTargettingMode && !PendingObject) { + IsTentative = true; + BandX = x; + BandY = y; + NewX = x; + NewY = y; + } +} + + +/*********************************************************************************************** + * DisplayClass::Mouse_Left_Held -- Handles the left button held down. * + * * + * This routine is called continuously while the left mouse button is held down over * + * the tactical map. This handles the rubber band mode detection and dragging. * + * * + * INPUT: x,y -- The mouse coordinate. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/24/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Mouse_Left_Held(int x, int y) +{ + if (IsRubberBand) { + if (x != NewX || y != NewY) { + x = Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1); + y = Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1); + Refresh_Band(); + NewX = x; + NewY = y; + IsToRedraw = true; + Flag_To_Redraw(false); + } + } else { + + /* + ** If the mouse is still held down while a tentative extended select is possible, then + ** check to see if the mouse has moved a sufficient distance in order to activate + ** extended select mode. + */ + if (IsTentative) { + + /* + ** The mouse must have moved a minimum distance before rubber band mode can be + ** initiated. + */ + if (ABS(x - BandX) > 4 || ABS(y - BandY) > 4) { + IsRubberBand = true; + x = Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1); + y = Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1); + NewX = x; + NewY = y; + IsToRedraw = true; + Flag_To_Redraw(false); + } + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Set_Tactical_Position -- Sets the tactical view position. * + * * + * This routine is used to set the tactical view position. The requested position is * + * clipped to the map dimensions as necessary. * + * * + * INPUT: coord -- The coordinate desired for the upper left corner. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Set_Tactical_Position(COORDINATE coord) +{ + /* + ** Bound the desired location to fit the legal map edges. + */ + int xx = Coord_X(coord) - Cell_To_Lepton(MapCellX); + int yy = Coord_Y(coord) - Cell_To_Lepton(MapCellY); + + Confine_Rect(&xx, &yy, TacLeptonWidth, TacLeptonHeight, Cell_To_Lepton(MapCellWidth), Cell_To_Lepton(MapCellHeight)); + coord = XY_Coord(xx + Cell_To_Lepton(MapCellX), yy + Cell_To_Lepton(MapCellY)); + + if (ScenarioInit) { + TacticalCoord = coord; + } + DesiredTacticalCoord = coord; + IsToRedraw = true; + Flag_To_Redraw(false); +} + + +/*********************************************************************************************** + * DisplayClass::Compute_Start_Pos -- Computes player's start pos from unit coords. * + * * + * Use this function in multiplayer games, to compute the scenario starting Tactical Pos. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/28/1995 JLB : Commented. * + * 06/26/1995 JLB : Fixed building loop. * + *=============================================================================================*/ +void DisplayClass::Compute_Start_Pos(void) +{ + /* + ** Find the summation cell-x & cell-y for all the player's units, infantry, + ** and buildings. Buildings are weighted so that they count 16 times more + ** than units or infantry. + */ + long x = 0; + long y = 0; + long num = 0; + for (int i = 0; i < Infantry.Count(); i++) { + InfantryClass * infp = Infantry.Ptr(i); + if (!infp->IsInLimbo && infp->House == PlayerPtr) { + x += (long)Coord_XCell (infp->Coord); + y += (long)Coord_YCell (infp->Coord); + num++; + } + } + + for (i = 0; i < Units.Count(); i++) { + UnitClass * unitp = Units.Ptr(i); + if (!unitp->IsInLimbo && unitp->House == PlayerPtr) { + x += (long)Coord_XCell (unitp->Coord); + y += (long)Coord_YCell (unitp->Coord); + num++; + } + } + + for (i = 0; i < Buildings.Count(); i++) { + BuildingClass * bldgp = Buildings.Ptr(i); + if (!bldgp->IsInLimbo && bldgp->House == PlayerPtr) { + x += (((long)Coord_XCell (bldgp->Coord)) << 4); + y += (((long)Coord_YCell (bldgp->Coord)) << 4); + num += 16; + } + } + + /* + ** Divide each coord by 'num' to compute the average value + */ + if (num > 0) { + x /= num; + } else { + x = 0; + } + + if (num > 0) { + y /= num; + } else { + y = 0; + } + + /* + ** Since the TacticalCell (starting cell) represents the screen's upper-left + ** corner, adjust for that. + */ + x -= (Lepton_To_Cell(TacLeptonWidth) / 2); + y -= (Lepton_To_Cell(TacLeptonHeight) / 2); + + /* + ** Clip the computed x,y cell coords to the map's size. + */ + if (x < MapCellX) { + x = MapCellX; + } + if (x + Lepton_To_Cell(TacLeptonWidth) > MapCellX + MapCellWidth) { + x = MapCellX + MapCellWidth - Lepton_To_Cell(TacLeptonWidth); + } + + if (y < MapCellY) { + y = MapCellY; + } + if (y + Lepton_To_Cell(TacLeptonHeight) > MapCellY + MapCellHeight) { + y = MapCellY + MapCellHeight - Lepton_To_Cell(TacLeptonHeight); + } + + /* + ** Set our TacticalCell + */ + Set_Tactical_Position(Cell_Coord(XY_Cell(x, y))); + for (int index = 0; index < sizeof(Views)/sizeof(Views[0]); index++) { + Views[index] = Coord_Cell(TacticalCoord); + } +} + + +/*********************************************************************************************** + * DisplayClass::Sell_Mode_Control -- Controls the sell mode. * + * * + * This routine will control the sell mode for the player. * + * * + * INPUT: control -- The mode to set the sell state to. * + * 0 = Turn sell mode off. * + * 1 = Turn sell mode on. * + * -1 = Toggle sell mode. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Sell_Mode_Control(int control) +{ + bool mode = IsSellMode; + switch (control) { + case 0: + mode = false; + break; + + case -1: + mode = (IsSellMode == false); + break; + + case 1: + mode = true; + break; + } + + if (mode != IsSellMode && !PendingObject) { + IsRepairMode = false; + if (mode && PlayerPtr->BScan) { + IsSellMode = true; + Unselect_All(); + } else { + IsSellMode = false; + Revert_Mouse_Shape(); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Repair_Mode_Control -- Controls the repair mode. * + * * + * This routine is used to control the repair mode for the player. * + * * + * INPUT: control -- The mode to set the repair to. * + * 0 = Turn repair off. * + * 1 = Turn repair on. * + * -1= Toggle repair state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Repair_Mode_Control(int control) +{ + bool mode = IsRepairMode; + switch (control) { + case 0: + mode = false; + break; + + case -1: + mode = (IsRepairMode == false); + break; + + case 1: + mode = true; + break; + } + + if (mode != IsRepairMode && !PendingObject) { + IsSellMode = false; + if (mode && PlayerPtr->BScan) { + IsRepairMode = true; + Unselect_All(); + } else { + IsRepairMode = false; + Revert_Mouse_Shape(); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::In_View -- Determines if cell is visible on screen. * + * * + * Use this routine to determine if the specified cell is visible on * + * the display. This is a useful fact, since many display operations * + * can be skipped if the cell is not visible. * + * * + * INPUT: cell -- The cell number to check. * + * * + * OUTPUT: bool; Is this cell visible on the display? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/30/1994 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + *=============================================================================================*/ +bool DisplayClass::In_View(register CELL cell) +{ + COORDINATE coord = Cell_Coord(cell) & 0xFF00FF00L; + COORDINATE tcoord = TacticalCoord & 0xFF00FF00L; + + if ((unsigned)(Coord_X(coord) - Coord_X(tcoord)) > TacLeptonWidth+255) return(false); + if ((unsigned)(Coord_Y(coord) - Coord_Y(tcoord)) > TacLeptonHeight+255) return(false); + return(true); + +#ifdef OBSOLETE + int fudgex = Coord_XLepton(TacticalCoord) ? -1 : 0; + int fudgey = Coord_YLepton(TacticalCoord) ? -1 : 0; + if ((unsigned)(Cell_X(cell)-Coord_XCell(TacticalCoord)) > Lepton_To_Cell(TacLeptonWidth)+fudgex) return(false); + if ((unsigned)(Cell_Y(cell)-Coord_YCell(TacticalCoord)) > Lepton_To_Cell(TacLeptonHeight)+fudgey) return(false); + return(true); +#endif + +#ifdef OBSOLETE + cell -= TacticalCell; + + if (Cell_X(cell) >= TacWidth + (TacPartialX ? 1 : 0)) return(false); + if (Cell_Y(cell) >= TacHeight + (TacPartialY ? 1 : 0)) return(false); + return(true); +#endif +} + + +COORDINATE DisplayClass::Closest_Free_Spot(COORDINATE coord, bool any) const +{ + if (coord & 0xC000C000) { + return(0x00800080); + } + return (*this)[Coord_Cell(coord)].Closest_Free_Spot(coord, any); +} + + +bool DisplayClass::Is_Spot_Free(COORDINATE coord) const +{ + if (coord & 0xC000C000) { + return(0x00800080); + } + return (*this)[Coord_Cell(coord)].Is_Spot_Free(CellClass::Spot_Index(coord)); +} + + +/*********************************************************************************************** + * DisplayClass::Center_Map -- Centers the map about the currently selected objects * + * * + * This routine will average the position of all the selected objects and then center * + * the map about those objects. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The map position changes by this routine. * + * * + * HISTORY: * + * 08/22/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Center_Map(void) +{ + if (CurrentObject.Count()) { + unsigned x = 0; + unsigned y = 0; + + for (int index = 0; index < CurrentObject.Count(); index++) { + COORDINATE coord = CurrentObject[index]->Center_Coord(); + + x += Coord_X(coord); + y += Coord_Y(coord); + } + + x /= CurrentObject.Count(); + y /= CurrentObject.Count(); + Set_Tactical_Position(XY_Coord(x - (TacLeptonWidth/2), y - (TacLeptonHeight/2))); + } +} diff --git a/DISPLAY.H b/DISPLAY.H new file mode 100644 index 0000000..be410c5 --- /dev/null +++ b/DISPLAY.H @@ -0,0 +1,320 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\display.h_v 2.15 16 Oct 1995 16:47:42 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DISPLAY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 1, 1994 * + * * + * Last Update : May 1, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DISPLAY_H +#define DISPLAY_H + +#include "map.h" +#include "layer.h" + + +#define ICON_PIXEL_W 24 +#define ICON_PIXEL_H 24 +#define ICON_LEPTON_W 256 +#define ICON_LEPTON_H 256 +#define CELL_PIXEL_W ICON_PIXEL_W +#define CELL_PIXEL_H ICON_PIXEL_H +#define CELL_LEPTON_W ICON_LEPTON_W +#define CELL_LEPTON_H ICON_LEPTON_H + +// ----------------------------------------------------------- +#define PIXEL_LEPTON_W (ICON_LEPTON_W/ICON_PIXEL_W) +#define PIXEL_LEPTON_H (ICON_LEPTON_H/ICON_PIXEL_H) + + +extern COORDINATE Coord_Add(COORDINATE coord1, COORDINATE coord2); + +class DisplayClass: public MapClass +{ + public: + + /* + ** This indicates the theater that the display is to represent. + */ + TheaterType Theater; + + /* + ** The tactical map display position is indicated by the cell of the + ** upper left hand corner. These should not be altered directly. Use + ** the Set_Tactical_Position function instead. + */ + COORDINATE TacticalCoord; + + /* + ** The dimensions (in cells) of the visible window onto the game map. This tactical + ** map is how the player interacts and views the game world. + */ + int TacLeptonWidth; + int TacLeptonHeight; + + /* + ** These layer control elements are used to group the displayable objects + ** so that proper overlap can be obtained. + */ + static LayerClass Layer[LAYER_COUNT]; + + /* + ** This records the position and shape of a placement cursor to display + ** over the map. This cursor is used when placing buildings and also used + ** extensively by the scenario editor. + */ + CELL ZoneCell; + short ZoneOffset; + short const *CursorSize; + bool ProximityCheck; // Is proximity check ok? + + /* + ** This holds the building type that is about to be placed upon the map. + ** It is only valid during the building placement state. The PendingLegal + ** flag is updated as the cursor moves and it reflects the legality of + ** placing the building at the desired location. + */ + ObjectClass * PendingObjectPtr; + ObjectTypeClass const * PendingObject; + HousesType PendingHouse; + + static unsigned char FadingBrighten[256]; + static unsigned char FadingShade[256]; + static unsigned char FadingLight[256]; + static unsigned char RemapTables[HOUSE_COUNT][3][256]; + static unsigned char FadingGreen[256]; + static unsigned char FadingYellow[256]; + static unsigned char FadingRed[256]; + static unsigned char TranslucentTable[(MAGIC_COL_COUNT+1)*256]; + static unsigned char WhiteTranslucentTable[(1+1)*256]; + static unsigned char MouseTranslucentTable[(4+1)*256]; + static void const *TransIconset; + static unsigned char UnitShadow[(USHADOW_COL_COUNT+1)*256]; + static unsigned char SpecialGhost[2*256]; + + //------------------------------------------------------------------------- + DisplayClass(void); + + virtual void Read_INI(char *buffer); + void Write_INI(char *buffer); + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time inits + virtual void Init_Clear(void); // Clears all to known state + virtual void Init_IO(void); // Inits button list + virtual void Init_Theater(TheaterType theater); // Theater-specific inits + + /* + ** General display/map/interface support functionality. + */ + virtual void AI(KeyNumType &input, int x, int y); + virtual void Draw_It(bool complete=false); + + /* + ** Added functionality. + */ + void Center_Map(void); + virtual bool Map_Cell(CELL cell, HouseClass *house); + virtual CELL Click_Cell_Calc(int x, int y); + virtual void Help_Text(int , int =-1, int =-1, int =YELLOW, bool =false, int =0) {}; + virtual MouseType Get_Mouse_Shape(void) const = 0; + virtual bool Scroll_Map(DirType facing, int & distance, bool really); + virtual void Refresh_Cells(CELL cell, short const *list); + virtual void Set_View_Dimensions(int x, int y, int width=-1, int height=-1); + + /* + ** Pending object placement control. + */ + virtual void Put_Place_Back(TechnoClass * ) {}; // Affects 'pending' system. + void Cursor_Mark(CELL pos, bool on); + void Set_Cursor_Shape(short const * list); + CELL Set_Cursor_Pos(CELL pos = -1); + void Get_Occupy_Dimensions(int & w, int & h, short const *list); + + /* + ** Tactical map only functionality. + */ + virtual void Set_Tactical_Position(COORDINATE coord); + void Refresh_Band(void); + void Select_These(COORDINATE coord1, COORDINATE coord2); + COORDINATE Pixel_To_Coord(int x, int y); + bool Coord_To_Pixel(COORDINATE coord, int &x, int &y); + bool Push_Onto_TacMap(COORDINATE &source, COORDINATE &dest); + void Remove(ObjectClass const *object, LayerType layer); + void Submit(ObjectClass const *object, LayerType layer); + CELL Calculated_Cell(SourceType dir, HousesType house); + bool In_View(register CELL cell); + bool Passes_Proximity_Check(ObjectTypeClass const *object); + ObjectClass * Cell_Object(CELL cell, int x=0, int y=0); + ObjectClass * Next_Object(ObjectClass * object); + ObjectClass * Prev_Object(ObjectClass * object); + int Cell_Shadow(CELL cell); + short const * Text_Overlap_List(char const * text, int x, int y, int lines = 1); + bool Is_Spot_Free(COORDINATE coord) const; + COORDINATE Closest_Free_Spot(COORDINATE coord, bool any=false) const; + void Sell_Mode_Control(int control); + void Repair_Mode_Control(int control); + + void Flag_Cell(CELL cell) { + Flag_To_Redraw(false); + IsToRedraw = true; + CellRedraw[cell] = true; + }; + bool Is_Cell_Flagged(CELL cell) const {return CellRedraw.Is_True(cell);}; + + /* + ** Computes starting position based on player's units' Coords. + */ + void Compute_Start_Pos(void); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + protected: + virtual void Mouse_Right_Press(void); + virtual void Mouse_Left_Press(int x, int y); + virtual void Mouse_Left_Up(bool shadow, ObjectClass * object, ActionType action, bool wwsmall = false); + virtual void Mouse_Left_Held(int x, int y); + virtual void Mouse_Left_Release(CELL cell, int x, int y, ObjectClass * object, ActionType action, bool wwsmall = false); + + public: + /* + ** This is the pixel offset for the upper left corner of the tactical map. + */ + int TacPixelX; + int TacPixelY; + + /* + ** This is the coordinate that the tactical map should be in at next available opportunity. + */ + COORDINATE DesiredTacticalCoord; + + /* + ** If something in the tactical map is to be redrawn, this flag is set to true. + */ + unsigned IsToRedraw:1; + + /* + ** If the player is currently wielding a wrench (to select buildings for repair), + ** then this flag is true. In such a state, normal movement and combat orders + ** are preempted. + */ + unsigned IsRepairMode:1; + + /* + ** If the player is currently in "sell back" mode, then this flag will be + ** true. While in this mode, anything clicked on will be sold back to the + ** "factory". + */ + unsigned IsSellMode:1; + + /* + ** If the player is currently in ion cannon targetting mode, then this + ** flag will be true. While in this mode, anything clicked on will be + ** be destroyed by the ION cannon. + */ + unsigned IsTargettingMode:2; + + protected: + + /* + ** If it is currently in rubber band mode (multi unit selection), then this + ** flag will be true. While in such a mode, normal input is prempted while + ** the extended selection is in progress. + */ + unsigned IsRubberBand:1; + + /* + ** The moment the mouse is held down, this flag gets set. If the mouse is dragged + ** a sufficient distance while held down, then true rubber band mode selection + ** can begin. Using a minimum distance prevents accidental rubber band selection + ** mode from being initiated. + */ + unsigned IsTentative:1; + + /* + ** This gadget class is used for capturing input to the tactical map. All mouse input + ** will be routed through this gadget. + */ + class TacticalClass : public GadgetClass { + public: + TacticalClass(void) : GadgetClass(0,0,0,0,LEFTPRESS|LEFTRELEASE|LEFTHELD|LEFTUP|RIGHTPRESS,true) {}; + + protected: + virtual int Action(unsigned flags, KeyNumType & key); + }; + friend class TacticalClass; + + /* + ** This is the "button" that tracks all input to the tactical map. + ** It must be available to derived classes, for Save/Load purposes. + */ + static TacticalClass TacButton; + + private: + + /* + ** This is a utility flag that is set during the icon draw process only if there + ** was at least one shadow icon detected that should be redrawn. When the shadow + ** drawing logic is to take place, but this flag is false, then the shadow drawing + ** will be skipped since it would perform no function. + */ + unsigned IsShadowPresent:1; + + /* + ** Rubber band mode consists of stretching a box from the anchor point (specified + ** here) to the current cursor position. + */ + int BandX,BandY; + int NewX,NewY; + + static void const *ShadowShapes; + static unsigned char ShadowTrans[(SHADOW_COL_COUNT+1)*256]; + + void Redraw_Icons(int draw_flags=0); + void Redraw_Shadow(void); + void Redraw_Shadow_Rects(void); + + /* + ** This bit array is used to flag cells to be redrawn. If the icon needs to + ** be redrawn for a cell, then the corresponding flag will be true. + */ + static BooleanVectorClass CellRedraw; +}; + + +#endif diff --git a/DOOR.CPP b/DOOR.CPP new file mode 100644 index 0000000..d94c491 --- /dev/null +++ b/DOOR.CPP @@ -0,0 +1,202 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\door.cpv 1.4 16 Oct 1995 16:49:16 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DOOR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/11/95 * + * * + * Last Update : June 14, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * DoorClass::AI -- Handles the door processing logic. * + * DoorClass::Close_Door -- Try to close the unit's door. * + * DoorClass::DoorClass -- Constructor for the DoorClass object. * + * DoorClass::Door_Stage -- Fetches the current door animation frame. * + * DoorClass::Open_Door -- Opens the door for this unit. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + + +/*********************************************************************************************** + * DoorClass::DoorClass -- Constructor for the DoorClass object. * + * * + * This constructor sets the door to an initial closed state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/14/1995 JLB : Created. * + *=============================================================================================*/ +DoorClass::DoorClass(void) +{ + State = IS_CLOSED; + IsToRedraw = false; + Stages = 0; +} + + +/*********************************************************************************************** + * DoorClass::AI -- Handles the door processing logic. * + * * + * This routine should be called every game frame. It handles the door closing and opening * + * logic. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/13/1995 JLB : Created. * + *=============================================================================================*/ +void DoorClass::AI(void) +{ + if (Control.Graphic_Logic()) { + if (Control.Fetch_Stage() >= Stages) { + Control.Set_Rate(0); + switch (State) { + case IS_OPENING: + State = IS_OPEN; + break; + + case IS_CLOSING: + State = IS_CLOSED; + break; + } + } + IsToRedraw = true; + } +} + + +/*********************************************************************************************** + * DoorClass::Open_Door -- Opens the door for this unit. * + * * + * This routine will perform the door open operation for this unit. It will control vehicle * + * rotation if necessary. * + * * + * INPUT: rate -- The animation rate (delay) to use for the door animation logic. * + * * + * stages -- The number of animations stages that this door must pass through. * + * * + * OUTPUT: Was action initiated to open the door? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +bool DoorClass::Open_Door(int rate, int stages) +{ + switch (State) { + case IS_CLOSED: + case IS_CLOSING: + State = IS_OPENING; + Stages = stages-1; + Control.Set_Stage(0); + Control.Set_Rate(rate); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * DoorClass::Close_Door -- Try to close the unit's door. * + * * + * This routine will attempt to close the unit's door. If the door is already closed or * + * in the process of closing, then no action is performed. * + * * + * INPUT: rate -- The animation rate (delay) to use for the door animation logic. * + * * + * stages -- The number of animations stages that this door must pass through. * + * * + * OUTPUT: Action was initiated to close the door? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +bool DoorClass::Close_Door(int rate, int stages) +{ + switch (State) { + case IS_OPEN: + case IS_OPENING: + State = IS_CLOSING; + Stages = stages-1; + Control.Set_Stage(0); + Control.Set_Rate(rate); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * DoorClass::Door_Stage -- Fetches the current door animation frame. * + * * + * Use this routine to fetch the current door animation frame number. Frame zero is the * + * closed frame and frame 'N' is the open frame. If the door is in the process of opening * + * or closing, the appropriate frame number is used. 'N' is defined as the number of * + * stages in the animation minus 1 (e.g., a four frame animation will return a door stage * + * number between 0 and 3, inclusive). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the door animation frame number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/14/1995 JLB : Created. * + *=============================================================================================*/ +int DoorClass::Door_Stage(void) const +{ + switch (State) { + case IS_CLOSING: + return((Stages-1) - Control.Fetch_Stage()); + + case IS_CLOSED: + return(0); + + case IS_OPENING: + return(Control.Fetch_Stage()); + + case IS_OPEN: + return(Stages-1); + } + return(0); +} diff --git a/DOOR.H b/DOOR.H new file mode 100644 index 0000000..7f16bb9 --- /dev/null +++ b/DOOR.H @@ -0,0 +1,94 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\door.h_v 1.8 16 Oct 1995 16:47:54 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DOOR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/11/95 * + * * + * Last Update : June 11, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DOOR_H +#define DOOR_H + +class DoorClass +{ + private: + + /* + ** This is the animation control handler. + */ + StageClass Control; + + /* + ** This is the recorded number of stages of the current + ** door animation process. + */ + unsigned char Stages; + + /* + ** This is the door state. + */ + enum { + IS_CLOSED, // Door is closed. + IS_OPENING, // Door is in the process of opening. + IS_OPEN, // Door is fully open. + IS_CLOSING // Door is in the process of closing. + } State; + + /* + ** If the animation for this door indicates that the object it is + ** attached to should be redrawn, then this flag will be true. + */ + unsigned IsToRedraw:1; + + public: + DoorClass(void); + + bool Time_To_Redraw(void) {return(IsToRedraw);}; + void Clear_Redraw_Flag(void) {IsToRedraw = false;}; + void AI(void); + int Door_Stage(void) const; + bool Is_Door_Opening(void) {return(State == IS_OPENING);}; + bool Is_Door_Closing(void) {return(State == IS_CLOSING);}; + bool Open_Door(int rate, int stages); + bool Close_Door(int rate, int stages); + bool Is_Door_Open(void) {return(State == IS_OPEN);}; + bool Is_Door_Closed(void) {return(State == IS_CLOSED);}; + bool Is_Ready_To_Open(void); + + /* + ** File I/O. + */ + void Code_Pointers(void) { return; } + void Decode_Pointers(void) { return; } +}; + +#endif diff --git a/DPMI.CPP b/DPMI.CPP new file mode 100644 index 0000000..d7fa607 --- /dev/null +++ b/DPMI.CPP @@ -0,0 +1,169 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\dpmi.cpv 2.17 16 Oct 1995 16:49:36 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DPMI.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : July 2, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifdef __FLAT__ +#pragma inline +#endif + +#include "function.h" +#include "dpmi.h" + +#ifndef __FLAT__ + +void DOSSegmentClass::Swap(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size) +{ + if (!size) return; + + unsigned short ssel = src.Selector; + unsigned short dsel = dest.Selector; + + asm { + push es + push ds + + mov si,soffset + mov di,doffset + mov cx,size + mov ax,ssel + mov dx,dsel + mov ds,ax + mov es,dx + } +again: + asm { + mov al,ds:[si] + mov ah,es:[di] + mov ds:[si],ah + mov es:[di],al + inc di + inc si + dec cx + jnz again + + pop ds + pop es + } +} +#endif + + +void DOSSegmentClass::Swap(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size) +{ + extern void dss_swap(char *src, char *dest, int size); + + #pragma aux dss_swap = \ + "again: mov al,[esi]" \ + "mov ah,[edi]" \ + "mov [esi],ah" \ + "stosb" \ + "inc esi" \ + "loop again" \ + parm [esi] [edi] [ecx] \ + modify [ax]; + + if (!size) return; + dss_swap((char *)(src.Selector + soffset), (char *)(dest.Selector + doffset), size); +} + +#ifdef OBSOLETE +void DOSSegmentClass::Copy(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size) +{ + extern void dss_copy(char *src, char *dest, int size); + #pragma aux dss_copy = \ + "mov ebx,ecx" \ + "shr ecx,2" \ + "jecxz copskip1" \ + "rep movsd" \ +"copskip1: mov ecx,ebx" \ + "and ecx,3" \ + "jecxz copskip2" \ + "rep movsb" \ +"copskip2:" \ + parm [esi edi ecx] \ + modify [ebx]; + + if (!size) return; + dss_copy((char *)(src.Selector + soffset), (char *)(dest.Selector + doffset), size); +} +#endif + +#ifdef OBSOLETE +void DOSSegmentClass::Copy_To(void *source, int dest, int size) +{ + extern void dss_copy_to(void *src, (void *)dest, int size); + + #pragma aux dss_copy_to = \ + "mov ebx,ecx" \ + "shr ecx,2" \ + "jecxz cop2skip1" \ + "rep movsd" \ +"cop2skip1: mov ecx,ebx" \ + "and ecx,3" \ + "jecxz cop2skip2" \ + "rep movsb" \ +"cop2skip2:" \ + parm [esi edi ecx] \ + modify [ebx]; + + if (!size) return; + dss_copy_to(src, (void *)(Selector + dest), size); + +} +#endif + +#ifdef OBSOLETE +void DOSSegmentClass::Copy_From(void *dest, int source, int size) +{ + extern void dss_copy_from(void *dest, (void *)source, int size); + + #pragma aux dss_copy_from = \ + "mov ebx,ecx" \ + "shr ecx,2" \ + "jecxz copfskip1" \ + "rep movsd" \ +"copfskip1: mov ecx,ebx" \ + "and ecx,3" \ + "jecxz copfskip2" \ + "rep movsb" \ +"copfskip2:" \ + parm [edi esi ecx] \ + modify [ebx]; + + if (!size) return; + dss_copy_from(dest, (void *)(Selector + source), size); +} +#endif diff --git a/DPMI.H b/DPMI.H new file mode 100644 index 0000000..a65a0be --- /dev/null +++ b/DPMI.H @@ -0,0 +1,175 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\dpmi.h_v 2.17 16 Oct 1995 16:44:52 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DPMI.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : July 2, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DPMI_H +#define DPMI_H +#include +#include +#include +#include + + +extern void output(short port, short data); + + +class DOSSegmentClass { + /* + ** This is the selector/segment value. In real mode it is the segment, in protected + ** mode it is the selector (also 16 bits). This value is moved into DS or ES when + ** accessing memory. + ** Note: in Watcom flat addressing, Selector == Segment<<4 (ex: 0A0000h) + */ + unsigned int Selector; + + /* + ** These are C equivalents for pushing and popping the DS segment register. By using + ** these, it is possible to create very small code that uses a segment and + ** offset without damaging the DS register. These are especially useful in protected + ** mode, but they are legal in real mode as well. + */ + void Push_DS(void) {/*__emit__(0x1E);*/}; + void Pop_DS(void) {/*__emit__(0x1F);*/}; + + public: + DOSSegmentClass(void); + ~DOSSegmentClass(void); + DOSSegmentClass(unsigned short segment, long size=(1024L*64L)); + + unsigned int Get_Selector(void); + + /* + ** This routine is used to assign where the descriptor actually points to in + ** low DOS memory. In real mode, this is a simple segment assignment and the size + ** is always 64K regardless of what is specified. In protected mode, the segment + ** is used to update the selector and the size can be any length. + ** In Watcom flat mode, it sets Selector == segment<<4 + */ + void Assign(unsigned short segment, long size=(1024L*64L)); + + /* + ** These routines will move the data to/from regular memory and the segment/descriptor + ** memory. + */ + void Copy_To(void *source, int dest, int size); + void Copy_From(void *dest, int source, int size); + void Copy_Word_To(short data, int dest); + void Copy_Byte_To(char data, int dest); + void Copy_DWord_To(long data, int dest); + short Copy_Word_From(int source); + char Copy_Byte_From(int source); + long Copy_DWord_From(int source); + + /* + ** These routines move data around between sections of segmented (descriptor) memory. + ** Typically, this is used when accessing DOS memory in protected mode or when dealing + ** with hard memory areas such as the screen. + */ + static void Copy(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size); + static void Swap(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size); +}; + + +inline DOSSegmentClass::DOSSegmentClass(void) +{ + Selector = 0xB0000; +} + +inline DOSSegmentClass::~DOSSegmentClass(void) +{ +} + +inline void DOSSegmentClass::Copy_Word_To(short data, int dest) +{ + *(short *)(Selector+dest) = data; +} + +inline void DOSSegmentClass::Copy_Byte_To(char data, int dest) +{ + *(char *)(Selector+dest) = data; +} + +inline void DOSSegmentClass::Copy_DWord_To(long data, int dest) +{ + *(long *)(Selector+dest) = data; +} + +inline DOSSegmentClass::DOSSegmentClass(unsigned short segment, long) +{ + Assign(segment); +} + +inline void DOSSegmentClass::Assign(unsigned short segment, long) +{ + Selector = (long)(segment)<<4L; +} + +inline void DOSSegmentClass::Copy_To(void *source, int dest, int size) +{ + memmove((void*)(Selector+dest), source, size); +} + +inline void DOSSegmentClass::Copy_From(void *dest, int source, int size) +{ + memmove(dest, (void*)(Selector+source), size); +} + +inline void DOSSegmentClass::Copy(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size) { + memmove((void*)(dest.Selector+doffset), (void*)(src.Selector+soffset), size); +} + +inline short DOSSegmentClass::Copy_Word_From(int source) +{ + return *(short*)(Selector+source); +} + +inline char DOSSegmentClass::Copy_Byte_From(int source) +{ + return *(char*)(Selector+source); +} + +inline long DOSSegmentClass::Copy_DWord_From(int source) +{ + return *(long*)(Selector+source); +} + +inline unsigned int DOSSegmentClass::Get_Selector(void) +{ + return Selector; +} +#endif + + diff --git a/DRIVE.CPP b/DRIVE.CPP new file mode 100644 index 0000000..1c7d5ec --- /dev/null +++ b/DRIVE.CPP @@ -0,0 +1,2213 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\drive.cpv 2.17 16 Oct 1995 16:51:16 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DRIVE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 22, 1994 * + * * + * Last Update : July 30, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * DriveClass::AI -- Processes unit movement and rotation. * + * DriveClass::Approach_Target -- Handles approaching the target in order to attack it. * + * DriveClass::Assign_Destination -- Set the unit's NavCom. * + * DriveClass::Class_Of -- Fetches a reference to the class type for this object. * + * DriveClass::Debug_Dump -- Displays status information to monochrome screen. * + * DriveClass::Do_Turn -- Tries to turn the vehicle to the specified direction. * + * DriveClass::DriveClass -- Constructor for drive class object. * + * DriveClass::Exit_Map -- Give the unit a movement order to exit the map. * + * DriveClass::Fixup_Path -- Adds smooth start path to normal movement path. * + * DriveClass::Force_Track -- Forces the unit to use the indicated track. * + * DriveClass::Lay_Track -- Handles track laying logic for the unit. * + * DriveClass::Offload_Tiberium_Bail -- Offloads one Tiberium quantum from the object. * + * DriveClass::Ok_To_Move -- Checks to see if this object can begin moving. * + * DriveClass::Overrun_Square -- Handles vehicle overrun of a cell. * + * DriveClass::Per_Cell_Process -- Handles when unit finishes movement into a cell. * + * DriveClass::Smooth_Turn -- Handles the low level coord calc for smooth turn logic. * + * DriveClass::Start_Of_Move -- Tries to get a unit to advance toward cell. * + * DriveClass::Tiberium_Load -- Determine the Tiberium load as a percentage. * + * DriveClass::While_Moving -- Processes unit movement. * + * DriveClass::Mark_Track -- Marks the midpoint of the track as occupied. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +DriveClass::DriveClass(void) : Class(0) {}; + + +/*********************************************************************************************** + * DriveClass::Do_Turn -- Tries to turn the vehicle to the specified direction. * + * * + * This routine will set the vehicle to rotate to the direction specified. For tracked * + * vehicles, it is just a simple rotation. For wheeled vehicles, it performs a series * + * of short drives (three point turn) to face the desired direction. * + * * + * INPUT: dir -- The direction that this vehicle should face. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/29/1995 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Do_Turn(DirType dir) +{ + if (dir != PrimaryFacing) { + + /* + ** Special rotation track is needed for units that + ** cannot rotate in place. + */ + if (Special.IsThreePoint && TrackNumber == -1 && Class->Speed == SPEED_WHEEL) { + int facediff; // Signed difference between current and desired facing. + FacingType face; // Current facing (ordinal value). + + facediff = PrimaryFacing.Difference(dir) >> 5; + facediff = Bound(facediff, -2, 2); + if (facediff) { + face = Dir_Facing(PrimaryFacing); + + IsOnShortTrack = true; + Force_Track(face*FACING_COUNT + (face + facediff), Coord); + + Path[0] = FACING_NONE; + Set_Speed(0xFF); // Full speed. + } + } else { + PrimaryFacing.Set_Desired(dir); + if (Special.IsJurassic && AreThingiesEnabled && What_Am_I() == RTTI_UNIT && ((UnitClass *)this)->Class->IsPieceOfEight) PrimaryFacing.Set_Current(dir); + } + } +} + + +/*********************************************************************************************** + * DriveClass::Force_Track -- Forces the unit to use the indicated track. * + * * + * This override (nuclear bomb) style routine is to be used when a unit needs to start * + * on a movement track but is outside the normal movement system. This occurs when a * + * harvester starts driving off of a refinery. * + * * + * INPUT: track -- The track number to start on. * + * * + * coord -- The coordinate that the unit will end up at when the movement track * + * is completed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/17/1995 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Force_Track(int track, COORDINATE coord) +{ + TrackNumber = track; + TrackIndex = 0; + Start_Driver(coord); +} + + +/*********************************************************************************************** + * DriveClass::Tiberium_Load -- Determine the Tiberium load as a percentage. * + * * + * Use this routine to determine what the Tiberium load is (as a fixed point percentage). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current "fullness" rating for the object. This will be 0x0000 for * + * empty and 0x0100 for full. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/17/1995 JLB : Created. * + *=============================================================================================*/ +int DriveClass::Tiberium_Load(void) const +{ + if (*this == UNIT_HARVESTER) { + return(Cardinal_To_Fixed(UnitTypeClass::STEP_COUNT, Tiberium)); + } + return(0x0000); +} + + +/*********************************************************************************************** + * DriveClass::Approach_Target -- Handles approaching the target in order to attack it. * + * * + * This routine will check to see if the target is infantry and it can be overrun. It will * + * try to overrun the infantry rather than attack it. This only applies to computer * + * controlled vehicles. If it isn't the infantry overrun case, then it falls into the * + * base class for normal (complex) approach algorithm. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/17/1995 JLB : Created. * + * 07/12/1995 JLB : Flamethrower tanks don't overrun -- their weapon is better. * + *=============================================================================================*/ +void DriveClass::Approach_Target(void) +{ + /* + ** Only if there is a legal target should the approach check occur. + */ + if (!House->IsHuman && Target_Legal(TarCom) && !Target_Legal(NavCom)) { + + /* + ** Special case: + ** If this is for a unit that can crush infantry, and the target is + ** infantry, AND the infantry is pretty darn close, then just try + ** to drive over the infantry instead of firing on it. + */ + TechnoClass * target = As_Techno(TarCom); + if (Class->Primary != WEAPON_FLAME_TONGUE && Class->IsCrusher && Distance(TarCom) < 0x0180 && target && ((TechnoTypeClass const &)(target->Class_Of())).IsCrushable) { + Assign_Destination(TarCom); + return; + } + } + + /* + ** In the other cases, uses the more complex "get to just within weapon range" + ** algorithm. + */ + FootClass::Approach_Target(); +} + + +/*********************************************************************************************** + * DriveClass::Overrun_Square -- Handles vehicle overrun of a cell. * + * * + * This routine is called when a vehicle enters a square or when it is about to enter a * + * square (controlled by parameter). When a vehicle that can crush infantry enters a * + * cell that contains infantry, then the infantry will be destroyed (regardless of * + * affiliation). When a vehicle threatens to overrun a square, all occupying infantry * + * will attempt to get out of the way. * + * * + * INPUT: cell -- The cell that is, or soon will be, entered by a vehicle. * + * * + * threaten -- Don't kill, but just threaten to enter the cell. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Overrun_Square(CELL cell, bool threaten) +{ + CellClass * cellptr = &Map[cell]; + + if (Class->IsCrusher) { + if (threaten) { + + /* + ** If the cell contains infantry, then they will panic when a vehicle tries + ** drive over them. Have the infantry run away instead. + */ + if (cellptr->Flag.Composite & 0x1F) { + + /* + ** Scattering is controlled by the game difficulty level. + */ + if ((Special.IsDifficult || Special.IsScatter || Scenario > 8) && !Special.IsEasy) { + cellptr->Incoming(0, true); + } + } + } else { + ObjectClass * object = cellptr->Cell_Occupier(); + int crushed = false; + while (object) { + if (object->Class_Of().IsCrushable && !House->Is_Ally(object) && Distance(object->Center_Coord()) < 0x80) { + ObjectClass * next = object->Next; + crushed = true; + + /* + ** Record credit for the kill(s) + */ + Sound_Effect(VOC_SQUISH2, Coord); + object->Record_The_Kill(this); + object->Mark(MARK_UP); + object->Limbo(); + delete object; + new OverlayClass(OVERLAY_SQUISH, Coord_Cell(Coord)); + + object = next; + } else { + object = object->Next; + } + } + if (crushed) Do_Uncloak(); + } + } +} + + +/*********************************************************************************************** + * DriveClass::DriveClass -- Constructor for drive class object. * + * * + * This will initialize the drive class to its default state. It is called as a result * + * of creating a unit. * + * * + * INPUT: classid -- The unit's ID class. It is passed on to the foot class constructor. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/13/1994 JLB : Created. * + *=============================================================================================*/ +DriveClass::DriveClass(UnitType classid, HousesType house) : + Class(&UnitTypeClass::As_Reference(classid)), + FootClass(house) +{ + /* + ** For two shooters, clear out the second shot flag -- it will be set the first time + ** the object fires. For non two shooters, set the flag since it will never be cleared + ** and the second shot flag tells the system that normal rearm times apply -- this is + ** what is desired for non two shooters. + */ + if (Class->IsTwoShooter) { + IsSecondShot = false; + } else { + IsSecondShot = true; + } + IsHarvesting = false; + IsTurretLockedDown = false; + IsOnShortTrack = false; + IsReturning = false; + TrackNumber = -1; + TrackIndex = 0; + SpeedAccum = 0; + Tiberium = 0; + Strength = Class->MaxStrength; +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * DriveClass::Debug_Dump -- Displays status information to monochrome screen. * + * * + * This debug utility function will display the status of the drive class to the mono * + * screen. It is through this information that bugs can be tracked down. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Debug_Dump(MonoClass *mono) const +{ + mono->Set_Cursor(33, 7); + mono->Printf("%2d:%2d", TrackNumber, TrackIndex); + mono->Text_Print("X", 16 + (IsTurretLockedDown?2:0), 10); +// mono->Text_Print("X", 16 + (IsOnShortTrack?2:0), 11); + mono->Set_Cursor(41, 7);mono->Printf("%d", Fixed_To_Cardinal(100, Tiberium_Load())); + FootClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * DriveClass::Exit_Map -- Give the unit a movement order to exit the map. * + * * + * This routine is used to assign an appropriate movement destination for the unit so that * + * it will leave the map. The scripts are usually the one to call this routine when it * + * is determined that the unit has fulfilled its mission and must "depart". * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Exit_Map(void) +{ + CELL cell; // Map exit cell number. + + if (*this == UNIT_HOVER && !Target_Legal(NavCom)) { + + /* + ** Scan a swath of cells from current position to the edge of the map and if + ** there is any blocking object, just wait so to try again later. + */ + Mark(MARK_UP); + for (int x = Cell_X(Coord_Cell(Center_Coord()))-1; x <= Cell_X(Coord_Cell(Center_Coord()))+1; x++) { + for (int y = Cell_Y(Coord_Cell(Center_Coord()))+1; y < Map.MapCellY+Map.MapCellHeight; y++) { + cell = XY_Cell(x, y); + if (Map[cell].Cell_Techno()) { + Mark(MARK_DOWN); + return; + } + } + } + Mark(MARK_DOWN); + + /* + ** A clear path to the map edge exists. Assign it as the navigation computer + ** destination and let the transport move. + */ + cell = XY_Cell(Cell_X(Coord_Cell(Coord)), Map.MapCellY+Map.MapCellHeight); + IsReturning = true; + Assign_Destination(::As_Target(cell)); + } +} + + +/*********************************************************************************************** + * DriveClass::Smooth_Turn -- Handles the low level coord calc for smooth turn logic. * + * * + * This routine calculates the new coordinate value needed for the * + * smooth turn logic. The adjustment and flag values must be * + * determined prior to entering this routine. * + * * + * INPUT: adj -- The adjustment coordinate as lifted from the * + * correct smooth turn table. * + * * + * dir -- Pointer to dir for possible modification * + * according to the flag bits. * + * * + * OUTPUT: Returns with the coordinate the unit should positioned to. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1994 JLB : Created. * + * 07/13/1994 JLB : Converted to member function. * + *=============================================================================================*/ +COORDINATE DriveClass::Smooth_Turn(COORDINATE adj, DirType *dir) +{ + DirType workdir = *dir; + int x,y; + int temp; + TrackControlType flags = TrackControl[TrackNumber].Flag; + + x = Coord_X(adj); + y = Coord_Y(adj); + + if (flags & F_T) { + temp = x; + x = y; + y = temp; + workdir = (DirType)(DIR_W - workdir); + } + + if (flags & F_X) { + x = -x; + workdir = (DirType)-workdir; + } + + if (flags & F_Y) { + y = -y; + workdir = (DirType)(DIR_S - workdir); + } + + *dir = workdir; + + return(XY_Coord( Coord_X(Head_To_Coord()) + x, Coord_Y(Head_To_Coord()) + y)); +} + + +/*********************************************************************************************** + * DriveClass::Assign_Destination -- Set the unit's NavCom. * + * * + * This routine is used to set the unit's navigation computer to the * + * specified target. Once the navigation computer is set, the unit * + * will start planning and moving toward the destination. * + * * + * INPUT: target -- The destination target for the unit to head to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/07/1992 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void DriveClass::Assign_Destination(TARGET target) +{ + + /* + ** Abort early if there is anything wrong with the parameters + ** or the unit already is assigned the specified destination. + */ + if (target == NavCom) return; + +#ifdef NEVER + UnitClass *tunit; // Destination unit pointer. + + /* + ** When in move mode, a map position may really indicate + ** a unit to guard. + */ + if (Is_Target_Cell(target)) { + cell = As_Cell(target); + + tunit = Map[cell].Cell_Unit(); + if (tunit) { + + /* + ** Prevent targeting of itself. + */ + if (tunit != this) { + target = tunit->As_Target(); + } + } else { + + tbuilding = Map[cell].Cell_Building(); + if (tbuilding) { + target = tbuilding->As_Target(); + } + } + } +#endif + + /* + ** For harvesting type vehicles, it might go into a dock and unload procedure + ** when the harvester is full and an empty refinery is selected as a target. + */ + BuildingClass * b = As_Building(target); + + /* + ** Transport vehicles must tell all passengers that are about to load, that they + ** cannot proceed. This is accomplished with a radio message to this effect. + */ + //if (tunit && In_Radio_Contact() && Class->IsTransporter && Contact_With_Whom()->Is_Infantry()) { + if (In_Radio_Contact() && Class->IsTransporter && Contact_With_Whom()->Is_Infantry()) { + Transmit_Message(RADIO_OVER_OUT); + } + + /* + ** If the player clicked on a friendly repair facility and the repair + ** facility is currently not involved with some other unit (radio or unloading). + */ + if (b && *b == STRUCT_REPAIR) { + if (b->In_Radio_Contact()) { + target = 0; + } else { + + /* + ** Establish radio contact protocol. If the facility responds correctly, + ** then remain in radio contact and proceed toward the desired destination. + */ + if (Transmit_Message(RADIO_HELLO, b) == RADIO_ROGER) { + + /* + ** Last check to make sure that the loading square is free from permanent + ** occupation (such as a building). + */ + CELL cell = Coord_Cell(b->Center_Coord()) + (MAP_CELL_W-1); + if (Ground[Map[cell].Land_Type()].Cost[Class->Speed] ) { + if (Transmit_Message(RADIO_DOCKING) == RADIO_ROGER) { + FootClass::Assign_Destination(target); + Path[0] = FACING_NONE; + return; + } + + /* + ** Failure to establish a docking relationship with the refinery. + ** Bail & await further instructions. + */ + Transmit_Message(RADIO_OVER_OUT); + } + } + } + } + + /* + ** Set the unit's navigation computer. + */ + FootClass::Assign_Destination(target); + Path[0] = FACING_NONE; // Force recalculation of path. + if (!IsDriving) { + Start_Of_Move(); + } +} + + +/*********************************************************************************************** + * DriveClass::While_Moving -- Processes unit movement. * + * * + * This routine is used to process movement for the units as they move. * + * It is called many times for each cell's worth of movement. This * + * routine only applies after the next cell HeadTo has been determined. * + * * + * INPUT: none * + * * + * OUTPUT: true/false; Should this routine be called again? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/02/1992 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +bool DriveClass::While_Moving(void) +{ + int actual; // Working movement addition value. + + /* + ** Perform quick legality checks. + */ + if (!IsDriving || TrackNumber == -1 || (IsRotating && !Class->IsTurretEquipped)) { + SpeedAccum = 0; // Kludge? No speed should accumulate if movement is on hold. + return(false); + } + + /* + ** If enough movement has accumulated so that the unit can + ** visibly move on the map, then process accordingly. + ** Slow the unit down if he's carrying a flag. + */ + if (((UnitClass *)this)->Flagged != HOUSE_NONE) { + actual = SpeedAccum + Fixed_To_Cardinal(Class->MaxSpeed/2, Speed); + } else { + actual = SpeedAccum + Fixed_To_Cardinal(Class->MaxSpeed, Speed); + } + + if (actual > PIXEL_LEPTON_W) { + TurnTrackType const *track; // Track control pointer. + TrackType const *ptr; // Pointer to coord offset values. + int tracknum; // The track number being processed. + FacingType nextface; // Next facing queued in path. + bool adj; // Is a turn coming up? + + track = &TrackControl[TrackNumber]; + if (IsOnShortTrack) { + tracknum = track->StartTrack; + } else { + tracknum = track->Track; + } + ptr = RawTracks[tracknum-1].Track; + nextface = Path[0]; + + /* + ** Determine if there is a turn coming up. If there is + ** a turn, then track jumping might occur. + */ + adj = false; + if (nextface != FACING_NONE && Dir_Facing(track->Facing) != nextface) { + adj = true; + } + + /* + ** Skip ahead the number of track steps required (limited only + ** by track length). Set the unit to the new position and + ** flag the unit accordingly. + */ + Mark(MARK_UP); + while (actual > PIXEL_LEPTON_W) { + COORDINATE offset; + DirType dir; + + actual -= PIXEL_LEPTON_W; + + offset = ptr[TrackIndex].Offset; + if (offset || !TrackIndex) { + dir = ptr[TrackIndex].Facing; + Coord = Smooth_Turn(offset, &dir); + + PrimaryFacing.Set(dir); + + /* + ** See if "per cell" processing is necessary. + */ + if (TrackIndex && RawTracks[tracknum-1].Cell == TrackIndex) { + Per_Cell_Process(false); + if (!IsActive) { + return(false); + } + } + + /* + ** The unit could "jump tracks". Check to see if the unit should + ** do so. + */ + if (*this != UNIT_GUNBOAT && nextface != FACING_NONE && adj && RawTracks[tracknum-1].Jump == TrackIndex && TrackIndex) { + TurnTrackType const *newtrack; // Proposed jump-to track. + int tnum; + + tnum = Dir_Facing(track->Facing)*FACING_COUNT + nextface; + newtrack = &TrackControl[tnum]; + if (newtrack->Track && RawTracks[newtrack->Track-1].Entry) { + COORDINATE c = Head_To_Coord(); + int oldspeed = Speed; + + c = Adjacent_Cell(c, nextface); + + switch(Can_Enter_Cell(Coord_Cell(c), nextface)) { + case MOVE_OK: + IsOnShortTrack = false; // Shouldn't be necessary, but... + TrackNumber = tnum; + track = newtrack; + + // Mono_Printf("**Jumping from track %d to track %d. **\n", tracknum, track->Track);Keyboard::Get(); + + tracknum = track->Track; + TrackIndex = RawTracks[tracknum-1].Entry-1; // Anticipate increment. + ptr = RawTracks[tracknum-1].Track; + adj = false; + + Stop_Driver(); + Per_Cell_Process(true); + if (Start_Driver(c)) { + Set_Speed(oldspeed); + memcpy(&Path[0], &Path[1], CONQUER_PATH_MAX-1); + Path[CONQUER_PATH_MAX-1] = FACING_NONE; + } else { + Path[0] = FACING_NONE; + TrackNumber = -1; + actual = 0; + } + break; + + case MOVE_CLOAK: + Map[Coord_Cell(c)].Shimmer(); + break; + + case MOVE_TEMP: + if (*this == UNIT_HARVESTER || !House->IsHuman) { + bool old = Special.IsScatter; + Special.IsScatter = true; + Map[Coord_Cell(c)].Incoming(0, true); + Special.IsScatter = old; + } + break; + } + } + } + TrackIndex++; + + } else { + actual = 0; + Coord = Head_To_Coord(); + Stop_Driver(); + TrackNumber = -1; + TrackIndex = NULL; + + /* + ** Perform "per cell" activities. + */ + Per_Cell_Process(true); + + break; + } + } + if (IsActive) { + Mark(MARK_DOWN); + } + } + + /* + ** Replace any remainder back into the unit's movement + ** accumulator to be processed next pass. + */ + SpeedAccum = actual; + return(true); +} + + +/*********************************************************************************************** + * DriveClass::Per_Cell_Process -- Handles when unit finishes movement into a cell. * + * * + * This routine is called when a unit has mostly or completely * + * entered a cell. The unit might be in the middle of a movement track * + * when this routine is called. It's primary purpose is to perform * + * sighting and other "per cell" activities. * + * * + * INPUT: center -- Is the unit safely at the center of a cell? If it is merely "close" * + * to the center, then this parameter will be false. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/03/1993 JLB : Created. * + * 03/30/1994 JLB : Revamped for track system. * + * 04/15/1994 JLB : Converted to member function. * + * 06/18/1994 JLB : Converted to virtual function. * + * 06/18/1994 JLB : Distinguishes between center and near-center conditions. * + *=============================================================================================*/ +void DriveClass::Per_Cell_Process(bool center) +{ + CELL cell = Coord_Cell(Coord); + + /* + ** Check to see if it has reached its destination. If so, then clear the NavCom + ** regardless of the remaining path list. + */ + if (center && As_Cell(NavCom) == cell) { + IsTurretLockedDown = false; + NavCom = TARGET_NONE; + Path[0] = FACING_NONE; + } + +#ifdef NEVER + /* + ** A "lemon" vehicle will have a tendency to break down as + ** it moves about the terrain. + */ + if (Is_A_Lemon) { + if (Random_Pick(1, 4) == 1) { + Take_Damage(1); + } + } +#endif + + Lay_Track(); + + FootClass::Per_Cell_Process(center); +} + + +/*********************************************************************************************** + * DriveClass::Start_Of_Move -- Tries to get a unit to advance toward cell. * + * * + * This will try to start a unit advancing toward the cell it is * + * facing. It will check for and handle legality and reserving of the * + * necessary cell. * + * * + * INPUT: none * + * * + * OUTPUT: true/false; Should this routine be called again because * + * initial start operation is temporarily delayed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/02/1992 JLB : Created. * + * 10/18/1993 JLB : This should be called repeatedly until HeadTo is not NULL. * + * 03/16/1994 JLB : Revamped for track logic. * + * 04/15/1994 JLB : Converted to member function. * + * 06/19/1995 JLB : Fixed so that it won't fire on ground unnecessarily. * + * 07/13/1995 JLB : Handles bumping into cloaked objects. * + *=============================================================================================*/ +bool DriveClass::Start_Of_Move(void) +{ + FacingType facing; // Direction movement will commence. + DirType dir; // Desired actual facing toward destination. + int facediff; // Difference between current and desired facing. + int speed; // Speed of unit. + CELL destcell; // Cell of destination. + LandType ground; // Ground unit is entering. + COORDINATE dest; // Destination coordinate. + + facing = Path[0]; + + if (!Target_Legal(NavCom) && facing == FACING_NONE) { + IsTurretLockedDown = false; + Stop_Driver(); + if (Mission == MISSION_MOVE) { + Enter_Idle_Mode(); + } + return(false); // Why is it calling this routine!?! + } + +#ifdef NEVER + /* + ** Movement start logic can't begin until a unit that requires + ** a locked down turret gets to a locked down state (i.e., the + ** turret rotation stops. + */ + if (ClassF & CLASSF_LOCKTURRET) { + Set_Secondary_Facing(facing<<5); + if (Is_Rotating) { + return(true); + } + } +#endif + + /* + ** Reduce the path length if the target is a unit and the + ** range to the unit is less than the precalculated path steps. + */ + if (facing != FACING_NONE) { + int dist; + + if (Is_Target_Unit(NavCom) || Is_Target_Infantry(NavCom)) { + dist = Lepton_To_Cell(Distance(NavCom)); + +// if (dist > CELL_LEPTON_W || +// !As_Techno(NavCom)->Techno_Type_Class()->IsCrushable || +// !Class->IsCrusher) { + + if (dist < CONQUER_PATH_MAX) { + Path[dist] = FACING_NONE; + facing = Path[0]; // Maybe needed. + } +// } + } + } + + /* + ** If the path is invalid at this point, then generate one. If + ** generating a new path fails, then abort NavCom. + */ + if (facing == FACING_NONE) { + + /* + ** If after a path search, there is still no valid path, then set the + ** NavCom to null and let the script take care of assigning a new + ** navigation target. + */ + if (!PathDelay.Expired()) { + return(false); + } + if (!Basic_Path()) { + if (Distance(NavCom) < 0x0280 && (Mission == MISSION_MOVE || Mission == MISSION_GUARD_AREA)) { + Assign_Destination(TARGET_NONE); + } else { + + /* + ** If a basic path could be found, but the immediate move destination is + ** blocked by a friendly temporary blockage, then cause that blockage + ** to scatter. + */ + CELL cell = Adjacent_Cell(Coord_Cell(Center_Coord()), PrimaryFacing.Current()); + if (Map.In_Radar(cell)) { + if (Can_Enter_Cell(cell) == MOVE_TEMP) { + CellClass * cellptr = &Map[cell]; + TechnoClass * blockage = cellptr->Cell_Techno(); + if (blockage && House->Is_Ally(blockage)) { + bool old = Special.IsScatter; + Special.IsScatter = true; + cellptr->Incoming(0, true); + Special.IsScatter = old; + } + } + } + + if (TryTryAgain) { + TryTryAgain--; + } else { + Assign_Destination(TARGET_NONE); + if (IsNewNavCom) Sound_Effect(VOC_SCOLD); + IsNewNavCom = false; + } + } + Stop_Driver(); + TrackNumber = -1; + IsTurretLockedDown = false; + return(false); + } + + /* + ** If a basic path could be found, but the immediate move destination is + ** blocked by a friendly temporary blockage, then cause that blockage + ** to scatter. + */ + CELL cell = Adjacent_Cell(Coord_Cell(Center_Coord()), Path[0]); + if (Map.In_Radar(cell)) { + if (Can_Enter_Cell(cell) == MOVE_TEMP) { + CellClass * cellptr = &Map[cell]; + TechnoClass * blockage = cellptr->Cell_Techno(); + if (blockage && House->Is_Ally(blockage)) { + bool old = Special.IsScatter; + Special.IsScatter = true; + cellptr->Incoming(0, true); + Special.IsScatter = old; + } + } + } + + TryTryAgain = PATH_RETRY; + facing = Path[0]; + } + + if (Class->IsLockTurret || !Class->IsTurretEquipped) { + IsTurretLockedDown = true; + } + +#ifdef NEVER + /* + ** If the turret needs to match the body's facing before + ** movement can occur, then start it's rotation and + ** don't start a movement track until it is aligned. + */ + if (!Ok_To_Move(BodyFacing)) { + return(true); + } +#endif + + /* + ** Determine the coordinate of the next cell to move into. + */ + dest = Adjacent_Cell(Coord, facing); + dir = Facing_Dir(facing); + + /* + ** Set the facing correctly if it isn't already correct. This + ** means starting a rotation track if necessary. + */ + facediff = PrimaryFacing.Difference(dir); + if (facediff) { + + /* + ** Request a change of facing. + */ + Do_Turn(dir); + return(true); + + } else { + + /* NOTE: Beyond this point, actual track assignment can begin. + ** + ** If the cell to move into is impassable (probably for some unexpected + ** reason), then abort the path list and set the speed to zero. The + ** next time this routine is called, a new path will be generated. + */ + destcell = Coord_Cell(dest); + Mark(MARK_UP); + MoveType cando = Can_Enter_Cell(destcell, facing); + Mark(MARK_DOWN); + + if (cando != MOVE_OK) { + + if (Mission == MISSION_MOVE && House->IsHuman && Distance(NavCom) < 0x0200) { + Assign_Destination(TARGET_NONE); + } + + /* + ** If a temporary friendly object is blocking the path, then cause it to + ** get out of the way. + */ + if (cando == MOVE_TEMP) { + bool old = Special.IsScatter; + Special.IsScatter = true; + Map[destcell].Incoming(0, true); + Special.IsScatter = old; + } + + /* + ** If a cloaked object is blocking, then shimmer the cell. + */ + if (cando == MOVE_CLOAK) { + Map[destcell].Shimmer(); + } + + Stop_Driver(); + if (cando != MOVE_MOVING_BLOCK) { + Path[0] = FACING_NONE; // Path is blocked! + } + + /* + ** If blocked by a moving block then just exit start of move and + ** try again next tick. + */ + if (cando == MOVE_DESTROYABLE) { + if (Map[destcell].Cell_Object()) { + if (!House->Is_Ally(Map[destcell].Cell_Object())) { + Override_Mission(MISSION_ATTACK, Map[destcell].Cell_Object()->As_Target(), TARGET_NONE); + } + } else { + if (Map[destcell].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[destcell].Overlay).IsWall) { + Override_Mission(MISSION_ATTACK, ::As_Target(destcell), TARGET_NONE); + } + } + } else { + if (IsNewNavCom) Sound_Effect(VOC_SCOLD); + } + IsNewNavCom = false; + TrackNumber = -1; + return(true); + } + + /* + ** Determine the speed that the unit can travel to the desired square. + */ + ground = Map[destcell].Land_Type(); + speed = Ground[ground].Cost[Class->Speed]; + if (!speed) speed = 128; + +#ifdef NEVER + /* + ** Set the jiggle flag if the terrain would cause the unit + ** to jiggle when travelled over. + */ + BaseF &= ~BASEF_JIGGLE; + if (Ground[ground].Jiggle) { + BaseF |= BASEF_JIGGLE; + } +#endif + + /* + ** A damaged unit has a reduced speed. + */ + if ((Class->MaxStrength>>1) > Strength) { + speed -= (speed>>2); // Three quarters speed. + } + if ((speed != Speed)/* || !SpeedAdd*/) { + Set_Speed(speed); // Full speed. + } + + /* + ** Adjust speed depending on distance to ultimate movement target. The + ** further away the target is, the faster the vehicle will travel. + */ + int dist = Distance(NavCom); + if (dist < 0x0200) { + speed = Fixed_To_Cardinal(speed, 0x00A0); + } else { + if (dist < 0x0700) { + speed = Fixed_To_Cardinal(speed, 0x00D0); + } + } + + /* + ** Reserve the destination cell so that it won't become + ** occupied AS this unit is moving into it. + */ + if (cando != MOVE_OK) { + Path[0] = FACING_NONE; // Path is blocked! + TrackNumber = -1; + dest = NULL; + } else { + + Overrun_Square(Coord_Cell(dest), true); + + /* + ** Determine which track to use (based on recorded path). + */ + FacingType nextface = Path[1]; + if (nextface == FACING_NONE) nextface = facing; + + IsOnShortTrack = false; + TrackNumber = facing * FACING_COUNT + nextface; + if (TrackControl[TrackNumber].Track == 0) { + Path[0] = FACING_NONE; + TrackNumber = -1; + return(true); + } else { + if (TrackControl[TrackNumber].Flag & F_D) { + /* + ** If the middle cell of a two cell track contains a crate, + ** the check for goodies before movement starts. + */ + if (!Map[destcell].Goodie_Check(this)) { + cando = MOVE_NO; + } else { + + dest = Adjacent_Cell(dest, nextface); + destcell = Coord_Cell(dest); + cando = Can_Enter_Cell(destcell); + } + + if (cando != MOVE_OK) { + + /* + ** If a temporary friendly object is blocking the path, then cause it to + ** get out of the way. + */ + if (cando == MOVE_TEMP) { + bool old = Special.IsScatter; + Special.IsScatter = true; + Map[destcell].Incoming(0, true); + Special.IsScatter = old; + } + + /* + ** If a cloaked object is blocking, then shimmer the cell. + */ + if (cando == MOVE_CLOAK) { + Map[destcell].Shimmer(); + } + + Path[0] = FACING_NONE; // Path is blocked! + TrackNumber = -1; + dest = NULL; + if (cando == MOVE_DESTROYABLE) { + + if (Map[destcell].Cell_Object()) { + if (!House->Is_Ally(Map[destcell].Cell_Object())) { + Override_Mission(MISSION_ATTACK, Map[destcell].Cell_Object()->As_Target(), TARGET_NONE); + } + } else { + if (Map[destcell].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[destcell].Overlay).IsWall) { + Override_Mission(MISSION_ATTACK, ::As_Target(destcell), TARGET_NONE); + } + } + IsNewNavCom = false; + TrackIndex = 0; + return(true); + } + } else { + memcpy(&Path[0], &Path[2], CONQUER_PATH_MAX-2); + Path[CONQUER_PATH_MAX-2] = FACING_NONE; + IsPlanningToLook = true; + } + } else { + memcpy(&Path[0], &Path[1], CONQUER_PATH_MAX-1); + } + Path[CONQUER_PATH_MAX-1] = FACING_NONE; + } + } + + IsNewNavCom = false; + TrackIndex = 0; + if (!Start_Driver(dest)) { + TrackNumber = -1; + Path[0] = FACING_NONE; + Set_Speed(0); + } + } + return(false); +} + + +/*********************************************************************************************** + * DriveClass::AI -- Processes unit movement and rotation. * + * * + * This routine is used to process unit movement and rotation. It * + * functions autonomously from the script system. Thus, once a unit * + * is give rotation command or movement path, it will follow this * + * until specifically instructed to stop. The advantage of this * + * method is that it allows smooth movement of units, faster game * + * execution, and reduced script complexity (since actual movement * + * dynamics need not be controlled directly by the scripts). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine relies on the process control bits for the * + * specified unit (for speed reasons). Thus, only setting * + * movement, rotation, or path list will the unit perform * + * any physics. * + * * + * HISTORY: * + * 09/26/1993 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void DriveClass::AI(void) +{ + FootClass::AI(); + + /* + ** If the unit is following a track, then continue + ** to do so -- mindlessly. + */ + if (TrackNumber != -1) { + + /* + ** Perform the movement accumulation. + */ + While_Moving(); + if (!IsActive) return; + if (TrackNumber == -1 && (Target_Legal(NavCom) || Path[0] != FACING_NONE)) { + Start_Of_Move(); + While_Moving(); + if (!IsActive) return; + } + + } else { + + /* + ** For tracked units that are rotating in place, perform the rotation now. + */ + if ((Class->Speed == SPEED_FLOAT || Class->Speed == SPEED_HOVER || Class->Speed == SPEED_TRACK || (Class->Speed == SPEED_WHEEL && !Special.IsThreePoint)) && PrimaryFacing.Is_Rotating()) { + if (PrimaryFacing.Rotation_Adjust(Class->ROT)) { + Mark(MARK_CHANGE); + } + if (!IsRotating) { + Per_Cell_Process(true); + if (!IsActive) return; + } + + } else { + + /* + ** The unit has no track to follow, but if there + ** is a navigation target or a remaining path, + ** then start on a new track. + */ + if (Mission != MISSION_GUARD || NavCom != TARGET_NONE) { + if (Target_Legal(NavCom) || Path[0] != FACING_NONE) { + Start_Of_Move(); + While_Moving(); + if (!IsActive) return; + } else { + Stop_Driver(); + } + } + } + } +} + + +/*********************************************************************************************** + * DriveClass::Fixup_Path -- Adds smooth start path to normal movement path. * + * * + * This routine modifies the path of the specified unit so that it * + * will not start out with a rotation. This is necessary for those * + * vehicles that have difficulty with rotating in place. Typically, * + * this includes wheeled vehicles. * + * * + * INPUT: unit -- Pointer to the unit to adjust. * + * * + * path -- Pointer to path structure. * + * * + * OUTPUT: none * + * * + * WARNINGS: Only units that require a fixup get modified. The * + * modification only occurs, if there is a legal path to * + * do so. * + * * + * HISTORY: * + * 04/03/1994 JLB : Created. * + * 04/06/1994 JLB : Uses path structure. * + * 04/10/1994 JLB : Diagonal smooth turn added. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void DriveClass::Fixup_Path(PathType *path) +{ + FacingType stage[6]={FACING_N,FACING_N,FACING_N,FACING_N,FACING_N,FACING_N}; // Prefix path elements. + int facediff; // The facing difference value (0..4 | 0..-4). + static FacingType _path[4][6] = { + {(FacingType)2,(FacingType)0,(FacingType)2,(FacingType)0,(FacingType)0,(FacingType)0}, + {(FacingType)3,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)0,(FacingType)0}, + {(FacingType)4,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)0,(FacingType)0}, + {(FacingType)4,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)1,(FacingType)0} + }; + static FacingType _dpath[4][6] = { + {(FacingType)0,(FacingType)0,(FacingType)0,(FacingType)0,(FacingType)0,(FacingType)0}, + {(FacingType)3,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)0,(FacingType)0}, + {(FacingType)4,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)1,(FacingType)0}, + {(FacingType)5,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)1,(FacingType)0} + }; + + int index; + int counter; // Path addition + FacingType *ptr; // Path list pointer. + FacingType *ptr2; // Copy of new path list pointer. + FacingType nextpath; // Next path value. + CELL cell; // Working cell value. + bool ok; + + /* + ** Verify that the unit is valid and there is a path problem to resolve. + */ + if (!path || path->Command[0] == FACING_NONE) { + return; + } + + /* + ** Only wheeled vehicles need a path fixup -- to avoid 3 point turns. + */ + if (!Special.IsThreePoint || Class->Speed != SPEED_WHEEL) { + return; + } + + /* + ** If the original path starts in the same direction as the unit, then + ** there is no problem to resolve -- abort. + */ + facediff = PrimaryFacing.Difference((DirType)(path->Command[0]<<5)) >> 5; + + if (!facediff) return; + + if (Dir_Facing(PrimaryFacing) & FACING_NE) { + ptr = &_dpath[(FacingType)ABS((int)facediff)-FACING_NE][1]; // Pointer to path adjust list. + counter = (int)_dpath[(FacingType)ABS((int)facediff)-FACING_NE][0]; // Number of path adjusts. + } else { + ptr = &_path[(FacingType)ABS((int)facediff)-FACING_NE][1]; // Pointer to path adjust list. + counter = (int)_path[(FacingType)ABS((int)facediff)-FACING_NE][0]; // Number of path adjusts. + } + ptr2 = ptr; + + ok = true; // Presume adjustment is all ok. + cell = Coord_Cell(Coord); // Starting cell. + nextpath = Dir_Facing(PrimaryFacing); // Starting path. + for (index = 0; index < counter; index++) { + + /* + ** Determine next path element and add it to the + ** working path list. + */ + if (facediff > 0) { + nextpath = nextpath + *ptr++; + } else { + nextpath = nextpath - *ptr++; + } + stage[index] = nextpath; + cell = Adjacent_Cell(cell, nextpath); + //cell = Coord_Cell(Adjacent_Cell(Cell_Coord(cell), nextpath)); + + /* + ** If it can't enter this cell, then abort the path + ** building operation without adjusting the unit's + ** path. + */ + if (Can_Enter_Cell(cell, nextpath) != MOVE_OK) { + ok = false; + break; + } + } + + /* + ** If veering to the left was not successful, then try veering + ** to the right. This only makes sense if the vehicle is trying + ** to turn 180 degrees. + */ + if (!ok && ABS(facediff) == 4) { + ptr = ptr2; // Pointer to path adjust list. + facediff = -facediff; + ok = true; // Presume adjustment is all ok. + cell = Coord_Cell(Coord); // Starting cell. + nextpath = Dir_Facing(PrimaryFacing); // Starting path. + for (index = 0; index < counter; index++) { + + /* + ** Determine next path element and add it to the + ** working path list. + */ + if (facediff > 0) { + nextpath = nextpath + *ptr++; + } else { + nextpath = nextpath - *ptr++; + } + stage[index] = nextpath; + cell = Coord_Cell(Adjacent_Cell(Cell_Coord(cell), nextpath)); + + /* + ** If it can't enter this cell, then abort the path + ** building operation without adjusting the unit's + ** path. + */ + if (Can_Enter_Cell(cell, nextpath) != MOVE_OK) { + ok = false; + break; + } + } + } + + /* + ** If a legal path addition was created, then install it in place + ** of the first path value. The initial path entry is to be replaced + ** with a sequence of path entries that create smooth turning. + */ + if (ok) { + if (path->Length <= 1) { + movmem(&stage[0], path->Command, MAX(counter, 1)); + path->Length = counter; + } else { + + /* + ** Optimize the transition path step from the smooth turn + ** first part as it joins with the rest of the normal + ** path. The normal prefix path steps are NOT to be optimized. + */ + if (counter) { + counter--; + path->Command[0] = stage[counter]; + Optimize_Moves(path, MOVE_OK); + } + + /* + ** If there is more than one prefix path element, then + ** insert the rest now. + */ + if (counter) { + movmem(&path->Command[0], &path->Command[counter], 40-counter); + movmem(&stage[0], &path->Command[0], counter); + path->Length += counter; + } + } + path->Command[path->Length] = FACING_NONE; + } +} + + +/*********************************************************************************************** + * DriveClass::Lay_Track -- Handles track laying logic for the unit. * + * * + * This routine handles the track laying for the unit. This entails examining the unit's * + * current location as well as the direction and whether this unit is allowed to lay * + * tracks in the first place. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Lay_Track(void) +{ +#ifdef NEVER + static IconCommandType *_trackdirs[8] = { + TrackN_S, + TrackNE_SW, + TrackE_W, + TrackNW_SE, + TrackN_S, + TrackNE_SW, + TrackE_W, + TrackNW_SE + }; + + if (!(ClassF & CLASSF_TRACKS)) return; + + Icon_Install(Coord_Cell(Coord), _trackdirs[Facing_To_8(BodyFacing)]); +#endif +} + + +/*********************************************************************************************** + * DriveClass::Mark_Track -- Marks the midpoint of the track as occupied. * + * * + * This routine will ensure that the midpoint (if any) of the track that the unit is * + * following, will be marked according to the mark type specified. * + * * + * INPUT: headto -- The head to coordinate. * + * * + * type -- The type of marking to perform. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1995 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Mark_Track(COORDINATE headto, MarkType type) +{ + int value; + + if (type == MARK_UP) { + value = false; + } else { + value = true; + } + + if (headto) { + if (!IsOnShortTrack && TrackNumber != -1) { + + /* + ** If we have not passed the per cell process point we need + ** to deal with it. + */ + int tracknum = TrackControl[TrackNumber].Track; + if (tracknum) { + TrackType const * ptr = RawTracks[tracknum - 1].Track; + int cellidx = RawTracks[tracknum - 1].Cell; + if (cellidx > -1) { + DirType dir = ptr[cellidx].Facing; + + if (TrackIndex < cellidx && cellidx != -1) { + COORDINATE offset = Smooth_Turn(ptr[cellidx].Offset, &dir); + Map[Coord_Cell(offset)].Flag.Occupy.Vehicle = value; + } + } + } + } + Map[Coord_Cell(headto)].Flag.Occupy.Vehicle = value; + } +} + + +/*********************************************************************************************** + * DriveClass::Offload_Tiberium_Bail -- Offloads one Tiberium quantum from the object. * + * * + * This routine will offload one Tiberium packet/quantum/bail from the object. Multiple * + * calls to this routine are needed in order to fully offload all Tiberium. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of credits offloaded for the one call. If zero is returned,* + * then this indicates that all Tiberium has been offloaded. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +int DriveClass::Offload_Tiberium_Bail(void) +{ + if (Tiberium) { + Tiberium--; + if (House->IsHuman) { + return(UnitTypeClass::FULL_LOAD_CREDITS/UnitTypeClass::STEP_COUNT); + } + return(UnitTypeClass::FULL_LOAD_CREDITS+(UnitTypeClass::FULL_LOAD_CREDITS/3)/UnitTypeClass::STEP_COUNT); + } + return(0); +} + + +/*********************************************************************************************** + * DriveClass::Ok_To_Move -- Checks to see if this object can begin moving. * + * * + * This routine is used to verify that this object is allowed to move. Some objects can * + * be temporarily occupied and thus cannot move until the situation permits. * + * * + * INPUT: direction -- The direction that movement would be desired. * + * * + * OUTPUT: Can the unit move in the direction specified? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +bool DriveClass::Ok_To_Move(DirType ) const +{ + return true; +} + + +/*********************************************************************************************** + * DriveClass::Class_Of -- Fetches a reference to the class type for this object. * + * * + * This routine will fetch a reference to the TypeClass of this object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with reference to the type class of this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +ObjectTypeClass const & DriveClass::Class_Of(void) const +{ + return *Class; +} + + +/*************************************************************************** +** Smooth turn track tables. These are coordinate offsets from the center +** of the destination cell. These are the raw tracks that are modified +** by negating the X and Y portions as necessary. Also for reverse travelling +** direction, the track list can be processed backward. +** +** Track 1 = N +** Track 2 = NE +** Track 3 = N->NE 45 deg (double path consumption) +** Track 4 = N->E 90 deg (double path consumption) +** Track 5 = NE->SE 90 deg (double path consumption) +** Track 6 = NE->N 45 deg (double path consumption) +** Track 7 = N->NE (facing change only) +** Track 8 = NE->E (facing change only) +** Track 9 = N->E (facing change only) +** Track 10= NE->SE (facing change only) +** Track 11= back up into refinery +** Track 12= drive out of refinery +*/ +#pragma warn -ias +DriveClass::TrackType const DriveClass::Track1[24] = { + {0x00F50000L,(DirType)0}, + {0x00EA0000L,(DirType)0}, + {0x00DF0000L,(DirType)0}, + {0x00D40000L,(DirType)0}, + {0x00C90000L,(DirType)0}, + {0x00BE0000L,(DirType)0}, + {0x00B30000L,(DirType)0}, + {0x00A80000L,(DirType)0}, + {0x009D0000L,(DirType)0}, + {0x00920000L,(DirType)0}, + {0x00870000L,(DirType)0}, + {0x007C0000L,(DirType)0}, // Track jump check here. + {0x00710000L,(DirType)0}, + {0x00660000L,(DirType)0}, + {0x005B0000L,(DirType)0}, + {0x00500000L,(DirType)0}, + {0x00450000L,(DirType)0}, + {0x003A0000L,(DirType)0}, + {0x002F0000L,(DirType)0}, + {0x00240000L,(DirType)0}, + {0x00190000L,(DirType)0}, + {0x000E0000L,(DirType)0}, + {0x00030000L,(DirType)0}, + {0x00000000L,(DirType)0} +}; + +DriveClass::TrackType const DriveClass::Track2[] = { + {0x00F8FF08L,(DirType)32}, + {0x00F0FF10L,(DirType)32}, + {0x00E8FF18L,(DirType)32}, + {0x00E0FF20L,(DirType)32}, + {0x00D8FF28L,(DirType)32}, + {0x00D0FF30L,(DirType)32}, + {0x00C8FF38L,(DirType)32}, + {0x00C0FF40L,(DirType)32}, + {0x00B8FF48L,(DirType)32}, + {0x00B0FF50L,(DirType)32}, + {0x00A8FF58L,(DirType)32}, + {0x00A0FF60L,(DirType)32}, + {0x0098FF68L,(DirType)32}, + {0x0090FF70L,(DirType)32}, + {0x0088FF78L,(DirType)32}, + {0x0080FF80L,(DirType)32}, // Track jump check here. + {0x0078FF88L,(DirType)32}, + {0x0070FF90L,(DirType)32}, + {0x0068FF98L,(DirType)32}, + {0x0060FFA0L,(DirType)32}, + {0x0058FFA8L,(DirType)32}, + {0x0050FFB0L,(DirType)32}, + {0x0048FFB8L,(DirType)32}, + {0x0040FFC0L,(DirType)32}, + {0x0038FFC8L,(DirType)32}, + {0x0030FFD0L,(DirType)32}, + {0x0028FFD8L,(DirType)32}, + {0x0020FFE0L,(DirType)32}, + {0x0018FFE8L,(DirType)32}, + {0x0010FFF0L,(DirType)32}, + {0x0008FFF8L,(DirType)32}, + {0x00000000L,(DirType)32} +}; + +DriveClass::TrackType const DriveClass::Track3[] = { + {0x01F5FF00L,(DirType)0}, + {0x01EAFF00L,(DirType)0}, + {0x01DFFF00L,(DirType)0}, + {0x01D4FF00L,(DirType)0}, + {0x01C9FF00L,(DirType)0}, + {0x01BEFF00L,(DirType)0}, + {0x01B3FF00L,(DirType)0}, + {0x01A8FF00L,(DirType)0}, + {0x019DFF00L,(DirType)0}, + {0x0192FF00L,(DirType)0}, + {0x0187FF00L,(DirType)0}, + {0x0180FF00L,(DirType)0}, + {0x0175FF00L,(DirType)0}, // Jump entry point here. + {0x016BFF00L,(DirType)0}, + {0x0160FF02L,(DirType)1}, + {0x0155FF04L,(DirType)3}, + {0x014CFF06L,(DirType)4}, + {0x0141FF08L,(DirType)5}, + {0x0137FF0BL,(DirType)7}, + {0x012EFF0FL,(DirType)8}, + {0x0124FF13L,(DirType)9}, + {0x011AFF17L,(DirType)11}, + {0x0110FF1BL,(DirType)12}, + {0x0107FF1FL,(DirType)13}, // Center cell processing here. + {0x00FCFF24L,(DirType)15}, + {0x00F3FF28L,(DirType)16}, + {0x00ECFF2CL,(DirType)17}, + {0x00E0FF32L,(DirType)19}, + {0x00D7FF36L,(DirType)20}, + {0x00CFFF3DL,(DirType)21}, + {0x00C6FF42L,(DirType)23}, + {0x00BAFF49L,(DirType)24}, + {0x00B0FF4DL,(DirType)25}, + {0x00A8FF58L,(DirType)27}, + {0x00A0FF60L,(DirType)28}, + {0x0098FF68L,(DirType)29}, + {0x0090FF70L,(DirType)31}, + {0x0088FF78L,(DirType)32}, + {0x0080FF80L,(DirType)32}, // Track jump check here. + {0x0078FF88L,(DirType)32}, + {0x0070FF90L,(DirType)32}, + {0x0068FF98L,(DirType)32}, + {0x0060FFA0L,(DirType)32}, + {0x0058FFA8L,(DirType)32}, + {0x0050FFB0L,(DirType)32}, + {0x0048FFB8L,(DirType)32}, + {0x0040FFC0L,(DirType)32}, + {0x0038FFC8L,(DirType)32}, + {0x0030FFD0L,(DirType)32}, + {0x0028FFD8L,(DirType)32}, + {0x0020FFE0L,(DirType)32}, + {0x0018FFE8L,(DirType)32}, + {0x0010FFF0L,(DirType)32}, + {0x0008FFF8L,(DirType)32}, + {0x00000000L,(DirType)32} +}; + +DriveClass::TrackType const DriveClass::Track4[] = { + {0x00F5FF00L,(DirType)0}, + {0x00EBFF00L,(DirType)0}, + {0x00E0FF00L,(DirType)0}, + {0x00D5FF00L,(DirType)0}, + {0x00CBFF01L,(DirType)0}, + {0x00C0FF03L,(DirType)0}, + {0x00B5FF05L,(DirType)1}, + {0x00ABFF07L,(DirType)1}, + {0x00A0FF0AL,(DirType)2}, + {0x0095FF0DL,(DirType)3}, + {0x008BFF10L,(DirType)4}, + {0x0080FF14L,(DirType)5}, // Track entry here. + {0x0075FF18L,(DirType)8}, + {0x006DFF1CL,(DirType)12}, + {0x0063FF22L,(DirType)16}, + {0x005AFF25L,(DirType)20}, + {0x0052FF2BL,(DirType)23}, + {0x0048FF32L,(DirType)27}, + {0x0040FF37L,(DirType)32}, + {0x0038FF3DL,(DirType)36}, + {0x0030FF46L,(DirType)39}, + {0x002BFF4FL,(DirType)43}, + {0x0024FF58L,(DirType)47}, + {0x0020FF60L,(DirType)51}, + {0x001BFF6DL,(DirType)54}, + {0x0017FF79L,(DirType)57}, + {0x0014FF82L,(DirType)60}, // Track jump here. + {0x0011FF8FL,(DirType)62}, + {0x000DFF98L,(DirType)63}, + {0x0009FFA2L,(DirType)64}, + {0x0006FFACL,(DirType)64}, + {0x0004FFB5L,(DirType)66}, + {0x0003FFC0L,(DirType)64}, + {0x0002FFCBL,(DirType)64}, + {0x0001FFD5L,(DirType)64}, + {0x0000FFE0L,(DirType)64}, + {0x0000FFEBL,(DirType)64}, + {0x0000FFF5L,(DirType)64}, + {0x00000000L,(DirType)64} +}; + +DriveClass::TrackType const DriveClass::Track5[] = { + {0xFFF8FE08L,(DirType)32}, + {0xFFF0FE10L,(DirType)32}, + {0xFFE8FE18L,(DirType)32}, + {0xFFE0FE20L,(DirType)32}, + {0xFFD8FE28L,(DirType)32}, + {0xFFD0FE30L,(DirType)32}, + {0xFFC8FE38L,(DirType)32}, + {0xFFC0FE40L,(DirType)32}, + {0xFFB8FE48L,(DirType)32}, + {0xFFB0FE50L,(DirType)32}, + {0xFFA8FE58L,(DirType)32}, + {0xFFA0FE60L,(DirType)32}, + {0xFF98FE68L,(DirType)32}, + {0xFF90FE70L,(DirType)32}, + {0xFF88FE78L,(DirType)32}, + {0xFF80FE80L,(DirType)32}, // Track entry here. + {0xFF78FE88L,(DirType)32}, + {0xFF71FE90L,(DirType)32}, + {0xFF6AFE97L,(DirType)32}, + {0xFF62FE9FL,(DirType)32}, + {0xFF5AFEA8L,(DirType)32}, + {0xFF53FEB0L,(DirType)35}, + {0xFF4BFEB7L,(DirType)38}, + {0xFF44FEBEL,(DirType)41}, + {0xFF3EFEC4L,(DirType)44}, + {0xFF39FECEL,(DirType)47}, + {0xFF34FED8L,(DirType)50}, + {0xFF30FEE0L,(DirType)53}, + {0xFF2DFEEBL,(DirType)56}, + {0xFF2CFEF5L,(DirType)59}, + {0xFF2BFF00L,(DirType)62}, + {0xFF2CFF0BL,(DirType)66}, + {0xFF2DFF15L,(DirType)69}, + {0xFF30FF1FL,(DirType)72}, + {0xFF34FF28L,(DirType)75}, + {0xFF39FF30L,(DirType)78}, + {0xFF3EFF3AL,(DirType)81}, + {0xFF44FF44L,(DirType)84}, + {0xFF4BFF4BL,(DirType)87}, + {0xFF53FF50L,(DirType)90}, + {0xFF5AFF58L,(DirType)93}, + {0xFF62FF60L,(DirType)96}, + {0xFF6AFF68L,(DirType)96}, + {0xFF71FF70L,(DirType)96}, + {0xFF78FF78L,(DirType)96}, + {0xFF80FF80L,(DirType)96}, // Track jump check here. + {0xFF88FF88L,(DirType)96}, + {0xFF90FF90L,(DirType)96}, + {0xFF98FF98L,(DirType)96}, + {0xFFA0FFA0L,(DirType)96}, + {0xFFA8FFA8L,(DirType)96}, + {0xFFB0FFB0L,(DirType)96}, + {0xFFB8FFB8L,(DirType)96}, + {0xFFC0FFC0L,(DirType)96}, + {0xFFC8FFC8L,(DirType)96}, + {0xFFD0FFD0L,(DirType)96}, + {0xFFD8FFD8L,(DirType)96}, + {0xFFE0FFE0L,(DirType)96}, + {0xFFE8FFE8L,(DirType)96}, + {0xFFF0FFF0L,(DirType)96}, + {0xFFF8FFF8L,(DirType)96}, + {0x00000000L,(DirType)96} +}; + +DriveClass::TrackType const DriveClass::Track6[] = { + {0x0100FE00L,(DirType)32}, + {0x00F8FE08L,(DirType)32}, + {0x00F0FE10L,(DirType)32}, + {0x00E8FE18L,(DirType)32}, + {0x00E0FE20L,(DirType)32}, + {0x00D8FE28L,(DirType)32}, + {0x00D0FE30L,(DirType)32}, + {0x00C8FE38L,(DirType)32}, + {0x00C0FE40L,(DirType)32}, + {0x00B8FE48L,(DirType)32}, + {0x00B0FE50L,(DirType)32}, + {0x00A8FE58L,(DirType)32}, + {0x00A0FE60L,(DirType)32}, + {0x0098FE68L,(DirType)32}, + {0x0090FE70L,(DirType)32}, + {0x0088FE78L,(DirType)32}, + {0x0080FE80L,(DirType)32}, // Jump entry point here. + {0x0078FE88L,(DirType)32}, + {0x0070FE90L,(DirType)32}, + {0x0068FE98L,(DirType)32}, + {0x0060FEA0L,(DirType)32}, + {0x0058FEA8L,(DirType)32}, + {0x0055FEAEL,(DirType)32}, + {0x004EFEB8L,(DirType)35}, + {0x0048FEC0L,(DirType)37}, + {0x0042FEC9L,(DirType)40}, + {0x003BFED2L,(DirType)43}, + {0x0037FEDAL,(DirType)45}, + {0x0032FEE3L,(DirType)48}, + {0x002BFEEBL,(DirType)51}, + {0x0026FEF5L,(DirType)53}, + {0x0022FEFEL,(DirType)56}, + {0x001CFF08L,(DirType)59}, + {0x0019FF12L,(DirType)61}, + {0x0015FF1BL,(DirType)64}, + {0x0011FF26L,(DirType)64}, + {0x000EFF30L,(DirType)64}, + {0x000BFF39L,(DirType)64}, + {0x0009FF43L,(DirType)64}, + {0x0007FF4EL,(DirType)64}, + {0x0005FF57L,(DirType)64}, + {0x0003FF62L,(DirType)64}, + {0x0001FF6DL,(DirType)64}, + {0x0000FF77L,(DirType)64}, + {0x0000FF80L,(DirType)64}, // Track jump check here. + {0x0000FF8BL,(DirType)64}, + {0x0000FF95L,(DirType)64}, + {0x0000FFA0L,(DirType)64}, + {0x0000FFABL,(DirType)64}, + {0x0000FFB5L,(DirType)64}, + {0x0000FFC0L,(DirType)64}, + {0x0000FFCBL,(DirType)64}, + {0x0000FFD5L,(DirType)64}, + {0x0000FFE0L,(DirType)64}, + {0x0000FFEBL,(DirType)64}, + {0x0000FFF5L,(DirType)64}, + {0x00000000L,(DirType)64} +}; + +DriveClass::TrackType const DriveClass::Track7[] = { + {0x0006FFFFL,(DirType)0}, + {0x000CFFFEL,(DirType)4}, + {0x0011FFFCL,(DirType)8}, + {0x0018FFFAL,(DirType)12}, + {0x001FFFF6L,(DirType)16}, + {0x0024FFF3L,(DirType)19}, + {0x002BFFF0L,(DirType)22}, + {0x0030FFFDL,(DirType)23}, + {0x0035FFEBL,(DirType)24}, + {0x0038FFE8L,(DirType)25}, + {0x003CFFE6L,(DirType)26}, + {0x0040FFE3L,(DirType)27}, + {0x0043FFE0L,(DirType)28}, + {0x0046FFDDL,(DirType)29}, + {0x0043FFDFL,(DirType)30}, + {0x0040FFE1L,(DirType)30}, + {0x003CFFE3L,(DirType)30}, + {0x0038FFE5L,(DirType)30}, + {0x0035FFE7L,(DirType)31}, + {0x0030FFE9L,(DirType)31}, + {0x002BFFEBL,(DirType)31}, + {0x0024FFEDL,(DirType)31}, + {0x001FFFF1L,(DirType)31}, + {0x0018FFF4L,(DirType)32}, + {0x0011FFF7L,(DirType)32}, + {0x000CFFFAL,(DirType)32}, + {0x0006FFFDL,(DirType)32}, + {0x00000000L,(DirType)32} +}; + +DriveClass::TrackType const DriveClass::Track8[] = { + {0x0003FFFCL,(DirType)32}, + {0x0006FFF7L,(DirType)36}, + {0x000AFFF1L,(DirType)40}, + {0x000CFFEBL,(DirType)44}, + {0x000DFFE4L,(DirType)46}, + {0x000EFFDCL,(DirType)48}, + {0x000FFFD5L,(DirType)50}, + {0x0010FFD0L,(DirType)52}, + {0x0011FFC9L,(DirType)54}, + {0x0012FFC2L,(DirType)56}, + {0x0011FFC0L,(DirType)58}, + {0x0010FFC2L,(DirType)60}, + {0x000EFFC9L,(DirType)62}, + {0x000CFFCFL,(DirType)64}, + {0x000AFFD5L,(DirType)64}, + {0x0008FFDAL,(DirType)64}, + {0x0006FFE2L,(DirType)64}, + {0x0004FFE9L,(DirType)64}, + {0x0002FFEFL,(DirType)64}, + {0x0001FFF5L,(DirType)64}, + {0x0000FFF9L,(DirType)64}, + {0x00000000L,(DirType)64} +}; + +DriveClass::TrackType const DriveClass::Track9[] = { + {0xFFF50002L,(DirType)0}, + {0xFFEB0004L,(DirType)2}, + {0xFFE00006L,(DirType)4}, + {0xFFD50009L,(DirType)6}, + {0xFFCE000CL,(DirType)9}, + {0xFFC8000FL,(DirType)11}, + {0xFFC00012L,(DirType)13}, + {0xFFB80015L,(DirType)16}, + {0xFFC00012L,(DirType)18}, + {0xFFC8000EL,(DirType)20}, + {0xFFCE000AL,(DirType)22}, + {0xFFD50004L,(DirType)24}, + {0xFFDE0000L,(DirType)26}, + {0xFFE9FFF8L,(DirType)28}, + {0xFFEEFFF2L,(DirType)30}, + {0xFFF5FFEBL,(DirType)32}, + {0xFFFDFFE1L,(DirType)34}, + {0x0002FFD8L,(DirType)36}, + {0x0007FFD2L,(DirType)39}, + {0x000BFFCBL,(DirType)41}, + {0x0010FFC5L,(DirType)43}, + {0x0013FFBEL,(DirType)45}, + {0x0015FFB7L,(DirType)48}, + {0x0013FFBEL,(DirType)50}, + {0x0011FFC5L,(DirType)52}, + {0x000BFFCCL,(DirType)54}, + {0x0008FFD4L,(DirType)56}, + {0x0005FFDFL,(DirType)58}, + {0x0003FFEBL,(DirType)62}, + {0x0001FFF5L,(DirType)64}, + {0x00000000L,(DirType)64} +}; + +DriveClass::TrackType const DriveClass::Track10[] = { + {0xFFF6000BL,(DirType)32}, + {0xFFF00015L,(DirType)37}, + {0xFFEB0020L,(DirType)42}, + {0xFFE9002BL,(DirType)47}, + {0xFFE50032L,(DirType)52}, + {0xFFE30038L,(DirType)57}, + {0xFFE00040L,(DirType)60}, + {0xFFE20038L,(DirType)62}, + {0xFFE40032L,(DirType)64}, + {0xFFE5002AL,(DirType)68}, + {0xFFE6001EL,(DirType)70}, + {0xFFE70015L,(DirType)72}, + {0xFFE8000BL,(DirType)74}, + {0xFFE90000L,(DirType)76}, + {0xFFE8FFF5L,(DirType)78}, + {0xFFE7FFEBL,(DirType)80}, + {0xFFE6FFE0L,(DirType)82}, + {0xFFE5FFD5L,(DirType)84}, + {0xFFE4FFCEL,(DirType)86}, + {0xFFE2FFC5L,(DirType)88}, + {0xFFE0FFC0L,(DirType)90}, + {0xFFE3FFC5L,(DirType)92}, + {0xFFE5FFCEL,(DirType)94}, + {0xFFE9FFD5L,(DirType)95}, + {0xFFEBFFE0L,(DirType)96}, + {0xFFF0FFEBL,(DirType)96}, + {0xFFF6FFF5L,(DirType)96}, + {0x00000000L,(DirType)96} +}; + +DriveClass::TrackType const DriveClass::Track11[] = { + {0x01000000L,DIR_SW}, + {0x00F30008L,DIR_SW}, + {0x00E50010L,DIR_SW_X1}, + {0x00D60018L,DIR_SW_X1}, + {0x00C80020L,DIR_SW_X1}, + {0x00B90028L,DIR_SW_X1}, + {0x00AB0030L,DIR_SW_X2}, + {0x009C0038L,DIR_SW_X2}, + {0x008D0040L,DIR_SW_X2}, + {0x007F0048L,DIR_SW_X2}, + {0x00710050L,DIR_SW_X2}, + {0x00640058L,DIR_SW_X2}, + {0x00550060L,DIR_SW_X2}, + + {0x00000000L,DIR_SW_X2} +}; + +DriveClass::TrackType const DriveClass::Track12[] = { + {0xFF550060L,DIR_SW_X2}, + {0xFF640058L,DIR_SW_X2}, + {0xFF710050L,DIR_SW_X2}, + {0xFF7F0048L,DIR_SW_X2}, + {0xFF8D0040L,DIR_SW_X2}, + {0xFF9C0038L,DIR_SW_X2}, + {0xFFAB0030L,DIR_SW_X2}, + {0xFFB90028L,DIR_SW_X1}, + {0xFFC80020L,DIR_SW_X1}, + {0xFFD60018L,DIR_SW_X1}, + {0xFFE50010L,DIR_SW_X1}, + {0xFFF30008L,DIR_SW}, + + {0x00000000L,DIR_SW} +}; + +/* +** Drive out of weapon's factory. +*/ +DriveClass::TrackType const DriveClass::Track13[] = { + {XYP_COORD(10,-21),(DirType)(DIR_SW-10)}, + {XYP_COORD(10,-21),(DirType)(DIR_SW-10)}, + {XYP_COORD(10,-20),(DirType)(DIR_SW-10)}, + {XYP_COORD(10,-20),(DirType)(DIR_SW-10)}, + {XYP_COORD(9,-18),(DirType)(DIR_SW-10)}, + {XYP_COORD(9,-18),(DirType)(DIR_SW-10)}, + {XYP_COORD(9,-17),(DirType)(DIR_SW-10)}, + {XYP_COORD(8,-16),(DirType)(DIR_SW-10)}, + {XYP_COORD(8,-15),(DirType)(DIR_SW-10)}, + {XYP_COORD(7,-14),(DirType)(DIR_SW-10)}, + {XYP_COORD(7,-13),(DirType)(DIR_SW-10)}, + {XYP_COORD(6,-12),(DirType)(DIR_SW-10)}, + {XYP_COORD(6,-11),(DirType)(DIR_SW-10)}, + {XYP_COORD(5,-10),(DirType)(DIR_SW-10)}, + {XYP_COORD(5,-9),(DirType)(DIR_SW-10)}, + {XYP_COORD(4,-8),(DirType)(DIR_SW-10)}, + {XYP_COORD(4,-7),(DirType)(DIR_SW-10)}, + {XYP_COORD(3,-6),(DirType)(DIR_SW-10)}, + {XYP_COORD(3,-5),(DirType)(DIR_SW-9)}, + {XYP_COORD(2,-4),(DirType)(DIR_SW-7)}, + {XYP_COORD(2,-3),(DirType)(DIR_SW-5)}, + {XYP_COORD(1,-2),(DirType)(DIR_SW-3)}, + {XYP_COORD(1,-1),(DirType)(DIR_SW-1)}, + + {0x00000000L,DIR_SW} +}; + + +/* +** There are a limited basic number of tracks that a vehicle can follow. These +** are they. Each track can be interpreted differently but this is controlled +** by the TrackControl structure elaborated elsewhere. +*/ +DriveClass::RawTrackType const DriveClass::RawTracks[13] = { + {Track1, -1, 0, -1}, + {Track2, -1, 0, -1}, + {Track3, 37, 12, 22}, + {Track4, 26, 11, 19}, + {Track5, 45, 15, 31}, + {Track6, 44, 16, 27}, + {Track7, -1, 0, -1}, + {Track8, -1, 0, -1}, + {Track9, -1, 0, -1}, + {Track10, -1, 0, -1}, + {Track11, -1, 0, -1}, + {Track12, -1, 0, -1}, + {Track13, -1, 0, -1} +}; + + +/*************************************************************************** +** Smooth turning control table. Given two directions in a path list, this +** table determines which track to use and what modifying operations need +** be performed on the track data. +*/ +DriveClass::TurnTrackType const DriveClass::TrackControl[67] = { + {1, 0, DIR_N, F_}, // 0-0 + {3, 7, DIR_NE, F_D}, // 0-1 (raw chart) + {4, 9, DIR_E, F_D}, // 0-2 (raw chart) + {0, 0, DIR_SE, F_}, // 0-3 ! + {0, 0, DIR_S, F_}, // 0-4 ! + {0, 0, DIR_SW, F_}, // 0-5 ! + {4, 9, DIR_W, (DriveClass::TrackControlType)(F_X|F_D)}, // 0-6 + {3, 7, DIR_NW, (DriveClass::TrackControlType)(F_X|F_D)}, // 0-7 + {6, 8, DIR_N, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 1-0 + {2, 0, DIR_NE, F_}, // 1-1 (raw chart) + {6, 8, DIR_E, F_D}, // 1-2 (raw chart) + {5, 10, DIR_SE, F_D}, // 1-3 (raw chart) + {0, 0, DIR_S, F_}, // 1-4 ! + {0, 0, DIR_SW, F_}, // 1-5 ! + {0, 0, DIR_W, F_}, // 1-6 ! + {5, 10, DIR_NW, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 1-7 + {4, 9, DIR_N, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 2-0 + {3, 7, DIR_NE, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 2-1 + {1, 0, DIR_E, (DriveClass::TrackControlType)(F_T|F_X)}, // 2-2 + {3, 7, DIR_SE, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 2-3 + {4, 9, DIR_S, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 2-4 + {0, 0, DIR_SW, F_}, // 2-5 ! + {0, 0, DIR_W, F_}, // 2-6 ! + {0, 0, DIR_NW, F_}, // 2-7 ! + {0, 0, DIR_N, F_}, // 3-0 ! + {5, 10, DIR_NE, (DriveClass::TrackControlType)(F_Y|F_D)}, // 3-1 + {6, 8, DIR_E, (DriveClass::TrackControlType)(F_Y|F_D)}, // 3-2 + {2, 0, DIR_SE, F_Y}, // 3-3 + {6, 8, DIR_S, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 3-4 + {5, 10, DIR_SW, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 3-5 + {0, 0, DIR_W, F_}, // 3-6 ! + {0, 0, DIR_NW, F_}, // 3-7 ! + {0, 0, DIR_N, F_}, // 4-0 ! + {0, 0, DIR_NE, F_}, // 4-1 ! + {4, 9, DIR_E, (DriveClass::TrackControlType)(F_Y|F_D)}, // 4-2 + {3, 7, DIR_SE, (DriveClass::TrackControlType)(F_Y|F_D)}, // 4-3 + {1, 0, DIR_S, F_Y}, // 4-4 + {3, 7, DIR_SW, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 4-5 + {4, 9, DIR_W, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 4-6 + {0, 0, DIR_NW, F_}, // 4-7 ! + {0, 0, DIR_N, F_}, // 5-0 ! + {0, 0, DIR_NE, F_}, // 5-1 ! + {0, 0, DIR_E, F_}, // 5-2 ! + {5, 10, DIR_SE, (DriveClass::TrackControlType)(F_T|F_D)}, // 5-3 + {6, 8, DIR_S, (DriveClass::TrackControlType)(F_T|F_D)}, // 5-4 + {2, 0, DIR_SW, F_T}, // 5-5 + {6, 8, DIR_W, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 5-6 + {5, 10, DIR_NW, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 5-7 + {4, 9, DIR_N, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 6-0 + {0, 0, DIR_NE, F_}, // 6-1 ! + {0, 0, DIR_E, F_}, // 6-2 ! + {0, 0, DIR_SE, F_}, // 6-3 ! + {4, 9, DIR_S, (DriveClass::TrackControlType)(F_T|F_D)}, // 6-4 + {3, 7, DIR_SW, (DriveClass::TrackControlType)(F_T|F_D)}, // 6-5 + {1, 0, DIR_W, F_T}, // 6-6 + {3, 7, DIR_NW, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 6-7 + {6, 8, DIR_N, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 7-0 + {5, 10, DIR_NE, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 7-1 + {0, 0, DIR_E, F_}, // 7-2 ! + {0, 0, DIR_SE, F_}, // 7-3 ! + {0, 0, DIR_S, F_}, // 7-4 ! + {5, 10, DIR_SW, (DriveClass::TrackControlType)(F_X|F_D)}, // 7-5 + {6, 8, DIR_W, (DriveClass::TrackControlType)(F_X|F_D)}, // 7-6 + {2, 0, DIR_NW, F_X}, // 7-7 + + {11, 11, DIR_SW, F_}, // Backup harvester into refinery. + {12, 12, DIR_SW_X2, F_}, // Drive back into refinery. + {13, 13, DIR_SW, F_} // Drive out of weapons factory. +}; + diff --git a/DRIVE.H b/DRIVE.H new file mode 100644 index 0000000..4a8225b --- /dev/null +++ b/DRIVE.H @@ -0,0 +1,215 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\drive.h_v 2.19 16 Oct 1995 16:47:44 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DRIVE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DRIVE_H +#define DRIVE_H + +#include "foot.h" + +/**************************************************************************** +** Movable objects are handled by this class definition. Moveable objects +** cover everything except buildings. +*/ +class DriveClass : public FootClass +{ + public: + /* + ** This points to the static control data that gives 'this' unit its characteristics. + */ + UnitTypeClass const * const Class; + + /* + ** This records the number of "loads" of Tiberium the unit is carrying. Only + ** harvesters use this field. + */ + unsigned char Tiberium; + + /* + ** If this unit performing harvesting action, then this flag is true. The flag + ** is located here because the other bit flags here give it a free place to + ** reside. + */ + unsigned IsHarvesting:1; + + /* + ** This flags when a transport vehicle could not unload at its designated location + ** and is heading off the map to try again later. When this flag is true, the + ** transport unit is allowed to disappear when it reaches the edge of the map. + */ + unsigned IsReturning:1; + + /* + ** Some units must have their turret locked down to face their body direction. + ** When this flag is set, this condition is in effect. This flag is a more + ** accurate check than examining the TrackNumber since the turret may be + ** rotating into position so that a pending track may start. During this process + ** the track number does not indicate anything. + */ + unsigned IsTurretLockedDown:1; + + /* + ** This vehicle could be processing a "short track". A short track is one that + ** doesn't actually go anywhere. Kind of like turning in place. + */ + unsigned IsOnShortTrack:1; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + DriveClass(void); + DriveClass(UnitType classid, HousesType house); + virtual ~DriveClass(void) {}; + operator UnitType(void) const {return Class->Type;}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + virtual int Offload_Tiberium_Bail(void); + void Do_Turn(DirType dir); + virtual void Approach_Target(void); + virtual ObjectTypeClass const & Class_Of(void) const; + virtual void Overrun_Square(CELL cell, bool threaten=true); + virtual void Assign_Destination(TARGET target); + virtual void Per_Cell_Process(bool center); + virtual bool Ok_To_Move(DirType ) const; + virtual void AI(void); + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + void Force_Track(int track, COORDINATE coord); + virtual int Tiberium_Load(void) const; + + void Exit_Map(void); + void Mark_Track(COORDINATE headto, MarkType type); + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /********************************************************************** + ** These enumerations are used as working constants that exist only + ** in the DriveClass namespace. + */ + enum DriveClassEnum { + BACKUP_INTO_REFINERY=64, // Track to backup into refinery. + OUT_OF_REFINERY, // Track to leave refinery. + OUT_OF_WEAPON_FACTORY // Track to leave weapons factory. + }; + + private: + + /**************************************************************************** + ** Smooth turning tracks are controlled by this structure and these + ** processing bits. + */ + typedef enum TrackControlType { + F_=0x00, // No translation necessary? + F_T=0x01, // Transpose X and Y components? + F_X=0x02, // Reverse X component sign? + F_Y=0x04, // Reverse Y component sign? + F_D=0x08 // Two cell consumption? + } TrackControlType; + //#define F_S 0x10 // Is this a 90 degree turn? + + typedef struct { + char Track; // Which track to use. + char StartTrack; // Track when starting from stand-still. + DirType Facing; // Facing when track has been completed. + DriveClass::TrackControlType Flag; // List processing flag bits. + } TurnTrackType; + + typedef struct { + COORDINATE Offset; // Offset to origin coordinate. + DirType Facing; // Facing (primary track). + } TrackType; + + typedef struct { + DriveClass::TrackType const * Track; // Pointer to track list. + int Jump; // Index where track jumping is allowed. + int Entry; // Entry point if jumping to this track. + int Cell; // Per cell process should occur at this index. + } RawTrackType; + + /* + ** These speed values are used to accumulate movement and then + ** convert them into pixel "steps" that are then translated through + ** the currently running track so that the unit will move. + */ + unsigned char SpeedAccum; + + /* + ** This the track control logic (used for ground vehicles only). The 'Track' + ** variable holds the track being followed (0 == not following track). The + ** 'TrackIndex' variable holds the current index into the specified track + ** (starts at 0). + */ + char TrackNumber; + char TrackIndex; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + virtual void Fixup_Path(PathType *path); + bool While_Moving(void); + bool Start_Of_Move(void); + void Lay_Track(void); + COORDINATE Smooth_Turn(COORDINATE adj, DirType *dir); + + static TurnTrackType const TrackControl[67]; + static RawTrackType const RawTracks[13]; + static TrackType const Track13[]; + static TrackType const Track12[]; + static TrackType const Track11[]; + static TrackType const Track10[]; + static TrackType const Track9[]; + static TrackType const Track8[]; + static TrackType const Track7[]; + static TrackType const Track6[]; + static TrackType const Track5[]; + static TrackType const Track4[]; + static TrackType const Track3[]; + static TrackType const Track2[]; + static TrackType const Track1[24]; +}; + +inline DriveClass::TrackControlType operator |(DriveClass::TrackControlType, DriveClass::TrackControlType); +inline DriveClass::TrackControlType operator &(DriveClass::TrackControlType, DriveClass::TrackControlType); +inline DriveClass::TrackControlType operator ~(DriveClass::TrackControlType); + + +#endif diff --git a/EDIT.CPP b/EDIT.CPP new file mode 100644 index 0000000..1c099af --- /dev/null +++ b/EDIT.CPP @@ -0,0 +1,471 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\edit.cpv 2.18 16 Oct 1995 16:48:16 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : EDIT.CPP * + * * + * Programmer : Joe L. Bostic, Maria del Mar McCready Legg * + * * + * Start Date : 01/15/95 * + * * + * Last Update : June 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * EditClass::Action -- Handles input events. * + * EditClass::Draw_Background -- Draw the background to the edit gadget. * + * EditClass::Draw_Me -- Draws the edit box and embedded text. * + * EditClass::Draw_Text -- Draws the edit gadget text. * + * EditClass::EditClass -- Normal constructor for edit class object. * + * EditClass::Handle_Key -- Handles keyboard input to edit gadget. * + * EditClass::Set_Text -- Sets the text to the edit gadget. * + * EditClass::~EditClass -- Default destructor for the edit gadget. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * EditClass::EditClass -- Normal constructor for edit class object. * + * * + * This is the normal constructor used to create an edit object. * + * * + * INPUT: id -- The ID number for this edit object. This is the ID number that will be * + * returned by the Input() function when the key is pressed if this * + * gadget has the keyboard input focus. * + * * + * text -- Referenct to the text buffer that the edit gadget will modify as keyboard * + * input is processed. The value that this buffer contains is the default * + * text displayed. * + * * + * maxlen-- The maximum size of the text buffer specified. This length INCLUDES the * + * trailing null character so a simple sizeof() function call can be used. * + * * + * flags -- These are the text print control flags. It is used to control how the * + * text looks in the edit box. Use the normal TPF_??? flags. * + * * + * x,y -- The pixel coordinates of the upper left corner of the edit gadget area. * + * * + * w,h -- The pixel dimensions of the edit box. If either of these are no provided, * + * or set to -1, then the dimension is determined from the string itself. * + * * + * sytle -- This style flag parameter control what kind of characters are allowed in * + * the edit box. The initial string in the text buffer may contain illegal * + * characters, but they are NOT removed regardless of this parameter. * + * * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: * + * 01/05/1995 MML : Created. * + * 01/21/1995 JLB : Modified. * + *=============================================================================================*/ +EditClass::EditClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w, int h, EditStyle style) : + ControlClass (id, x, y, w, h, LEFTPRESS), String(text) +{ + TextFlags = flags; + EditFlags = style; + Color = CC_GREEN; + Set_Text(text, max_len); + + if (w == -1 || h == -1) { + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TextFlags); + + if (h == -1) { + Height = FontHeight+2; + } + if (w == -1) { + if (strlen(String) > 0) { + Width = String_Pixel_Width(String) + 6; + } else { + Width = ((Char_Pixel_Width('X')+FontXSpacing) * (MaxLength+1)) + 2; + } + } + } + IsReadOnly = 0; +} + + +/*********************************************************************************************** + * EditClass::~EditClass -- Default destructor for the edit gadget. * + * * + * This default destructor removes the focus setting if it currently has it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/24/1995 JLB : Created. * + *=============================================================================================*/ +EditClass::~EditClass(void) +{ + if (Has_Focus()) { + Clear_Focus(); + } +} + + +/*********************************************************************************************** + * EditClass::Set_Text -- Sets the text to the edit gadget. * + * * + * Use this routine to change the text that this edit gadget refers to. * + * * + * INPUT: text -- Reference to the character array that this edit gadget will be * + * modifying. * + * max_len -- The maximum size of the buffer that will be modified. * + * * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +void EditClass::Set_Text(char * text, int max_len) +{ + String = text; + MaxLength = max_len-1; + Length = strlen(String); + Flag_To_Redraw(); +} + + +/*********************************************************************************************** + * EditClass::Draw_Me -- Draws the edit box and embedded text. * + * * + * This routine will render the edit box. This will show the box outline as well as any * + * text it may contain. * + * * + * INPUT: forced -- Should the edit box be drawn even if it thinks it doesn't have to? * + * * + * OUTPUT: Was the edit box drawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int EditClass::Draw_Me(int forced) +{ + if (ControlClass::Draw_Me(forced)) { + /* + ** Hide the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + /* + ** Draw the body & set text color. + */ + Draw_Background(); + + /* + ** Display the text. + */ + Draw_Text(String); + + /* + ** Display the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * EditClass::Action -- Handles input events. * + * * + * This routine will handle all mouse and keyboard events directed at this edit box * + * gadget. For keyboard events, this will insert the characters into the edit box. * + * * + * INPUT: flags -- The event flag that triggered this function call. * + * * + * key -- Reference to the keyboard/mouse event that triggered this function call. * + * * + * OUTPUT: Should the list be processed further? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int EditClass::Action(unsigned flags, KeyNumType & key) +{ + + /* + ** If this is a read-only edit box, it's a display-only device + */ + if (IsReadOnly) { + return(false); + } + + /* + ** If the left mouse button is pressed over this gadget, then set the focus to + ** this gadget. The event flag is cleared so that no button ID number is returned. + */ + if ((flags & LEFTPRESS)) { + flags &= ~LEFTPRESS; + Set_Focus(); + Flag_To_Redraw(); // force to draw cursor + } + + /* + ** Handle keyboard events here. Normally, the key is added to the string, but if the + ** RETURN key is pressed, then the button ID number is returned from the Input() + ** function. + */ + if ((flags & KEYBOARD) && Has_Focus()) { + + /* + ** Process the keyboard character. If indicated, consume this keyboard event + ** so that the edit gadget ID number is not returned. + */ + if (key == KN_ESC) { + + Clear_Focus(); + flags = 0; + + } else { + + KeyASCIIType ascii = (KeyASCIIType)(Keyboard::To_ASCII(key) & 0x00ff); + + /* + ** Allow numeric keypad presses to map to ascii numbers + */ + if ((key & WWKEY_VK_BIT) && ascii >='0' && ascii <= '9'){ + key &= ~WWKEY_VK_BIT; + + if ( (!(flags & LEFTRELEASE)) && (!(flags & RIGHTRELEASE))){ + if (Handle_Key (ascii) ) { + flags &= ~KEYBOARD; + key = KN_NONE; + } + } + + }else{ + + /* + ** Filter out all special keys except return and backspace + */ + if ((!(key & WWKEY_VK_BIT) && ascii >= ' ' && ascii <= 127) + || key == KN_RETURN || key == KN_BACKSPACE){ + + + if ( (!(flags & LEFTRELEASE)) && (!(flags & RIGHTRELEASE))){ + if (Handle_Key(Keyboard::To_ASCII(key))) { + flags &= ~KEYBOARD; + key = KN_NONE; + } + } + }else{ + //if (key & WWKEY_RLS_BIT){ + // if ( (!(flags & LEFTRELEASE)) && (!(flags & RIGHTRELEASE))){ + flags &= ~KEYBOARD; + key = KN_NONE; + // } + //} + } + } + } + } + + return(ControlClass::Action(flags, key)); +} + + +/*********************************************************************************************** + * EditClass::Draw_Background -- Draw the background to the edit gadget. * + * * + * This routine will redraw the edit gadget background. The overlaying text is handled by * + * a different routine. The mouse is guaranteed to be hidden when this routine is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +void EditClass::Draw_Background(void) +{ + Draw_Box (X, Y, Width, Height, BOXSTYLE_GREEN_BOX, true); +} + + +/*********************************************************************************************** + * EditClass::Draw_Text -- Draws the edit gadget text. * + * * + * This routine is called when the edit gadget text needs to be drawn. The background has * + * already been drawn by the time this function is called. The mouse is guaranteed to be * + * hidden as well. * + * * + * INPUT: text -- The text to draw in the edit gadget. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +void EditClass::Draw_Text(char const * text) +{ + if (FontPtr == GradFont6Ptr) { + TextPrintType flags; + + if (Has_Focus()) { + flags = TPF_BRIGHT_COLOR; + } else { + flags = (TextPrintType)0; + } + + Conquer_Clip_Text_Print(text, X+1, Y+1, Color, TBLACK, TextFlags | flags, Width-2); + + if (Has_Focus() && strlen(text) < MaxLength && + (String_Pixel_Width(text) + String_Pixel_Width ("_") < Width-2) ) { + Conquer_Clip_Text_Print( "_", X+1+String_Pixel_Width(text), Y+1, Color, TBLACK, TextFlags | flags); + } + } else { + Conquer_Clip_Text_Print(text, X+1, Y+1, Has_Focus() ? BLUE : WHITE, TBLACK, TextFlags, Width-2); + + if (Has_Focus() && strlen(text) < MaxLength && + (String_Pixel_Width(text) + String_Pixel_Width ("_") < Width-2) ) { + Conquer_Clip_Text_Print("_",X+1+String_Pixel_Width(text),Y+1,BLUE,TBLACK, TextFlags); + } + } + +} + + +/*********************************************************************************************** + * EditClass::Handle_Key -- Handles keyboard input to edit gadget. * + * * + * This is the gruntwork routine that processes keyboard input to the edit gadget. This * + * routine will be called when keyboard input has been detected and this gadget has the * + * current focus. * + * * + * INPUT: ascii -- The ASCII key code that was fetched from the keyboard buffer. * + * * + * OUTPUT: bool; Should this keyboard input NOT cause the gadget ID number to be returned * + * from the controlling Input() routine? Typically, the return value would be * + * true unless the focus is lost due to the key being pressed. * + * * + * WARNINGS: none * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +bool EditClass::Handle_Key(KeyASCIIType ascii) +{ + switch (ascii) { + /* + ** Handle the special case of a non-keyboard event. It is possible that this + ** key code might be passed to this routine if this routine has been overridden + ** and the key event was consumed. + */ + case 0: + break; + + /* + ** If the return key is pressed, then remove the focus from this edit + ** gadget but otherwise let the normal gadget processing proceed. This + ** causes the gadget ID number to be returned from the Input() function + ** so that the controlling program will know that the text can be + ** processed. + */ + case KA_RETURN: + Clear_Focus(); + return(false); + + /* + ** When the BACKSPACE key is pressed, remove the last character in the edit string. + */ + case KA_BACKSPACE: + if (Length) { + Length--; + String[Length] = '\0'; + Flag_To_Redraw(); + } + break; + + /* + ** If the keyboard event was not a recognized special key, then examine to see + ** if it can legally be added to the edit string and do so if possible. + */ + default: + + /* + ** Don't add a character if the length is greater than edit width. + */ + if ((String_Pixel_Width(String) + Char_Pixel_Width(ascii) ) >= (Width-2)) { + break; + } + + /* + ** Don't add a character if the length is already at maximum. + */ + if (Length >= MaxLength) break; + + /* + ** Invisible characters are never added to the string. This is + ** especially true for spaces at the beginning of the string. + */ + if (!isgraph(ascii) && ascii != ' ') break; + if (ascii == ' ' && Length == 0) break; + + /* + ** If this is an upper case only edit gadget, then force the alphabetic + ** character to upper case. + */ + if ((EditFlags & UPPERCASE) && isalpha(ascii)) { + ascii = (KeyASCIIType)toupper(ascii); + } + + if ((!(EditFlags & NUMERIC) || !isdigit(ascii)) && + (!(EditFlags & ALPHA) || !isalpha(ascii)) && + (!(EditFlags & MISC) || isalnum(ascii)) && + ascii != ' ') { + break; + } + + /* + ** The character passed all legality checks, so add it to the edit string + ** and flag this gadget to be redrawn. The manual flag to redraw is needed + ** because the event flag has been cleared. This prevents the gadget's ID + ** number from being returned just because the gadget has been edited. + */ + String[Length++] = ascii; + String[Length] = '\0'; + Flag_To_Redraw(); + break; + } + return(true); +} diff --git a/EDIT.H b/EDIT.H new file mode 100644 index 0000000..fae3928 --- /dev/null +++ b/EDIT.H @@ -0,0 +1,106 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\edit.h_v 2.17 16 Oct 1995 16:46:32 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : EDIT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef EDIT_H +#define EDIT_H + +class EditClass : public ControlClass +{ + public: + typedef enum EditStyle { + ALPHA =0x0001, // Edit accepts alphabetic characters. + NUMERIC =0x0002, // Edit accepts numbers. + MISC =0x0004, // Edit accepts misc graphic characters. + UPPERCASE =0x0008, // Force to upper case. + ALPHANUMERIC=(int)ALPHA|(int)NUMERIC|(int)MISC, + } EditStyle; + + EditClass (int id, char * text, int max_len, TextPrintType flags, int x, int y, int w=-1, int h=-1, EditStyle style=ALPHANUMERIC); + virtual ~EditClass(void); + + virtual int Draw_Me(int forced); + virtual void Set_Text(char * text, int max_len); + void Set_Color (int color) { Color = color; } + + void Set_Read_Only(int rdonly) {IsReadOnly = rdonly;} + + protected: + + /* + ** These are the text size and style flags to be used when displaying the text + ** of the edit gadget. + */ + TextPrintType TextFlags; + + /* + ** Input flags that control what characters are allowed in the string. + */ + EditStyle EditFlags; + + /* + ** Pointer to text staging buffer and the maximum length of the string it + ** can contain. + */ + char *String; + int MaxLength; + + /* + ** This is the current length of the string. This length will never exceed the + ** MaxLength allowed. + */ + int Length; + + /* + ** This is the desired color of the edit control. + */ + int Color; + + virtual int Action (unsigned flags, KeyNumType &key); + virtual void Draw_Background(void); + virtual void Draw_Text(char const * text); + virtual bool Handle_Key(KeyASCIIType ascii); + + private: + int IsReadOnly; + +}; + +inline EditClass::EditStyle operator |(EditClass::EditStyle, EditClass::EditStyle); +inline EditClass::EditStyle operator &(EditClass::EditStyle, EditClass::EditStyle); +inline EditClass::EditStyle operator ~(EditClass::EditStyle); + +#endif diff --git a/ENDING.CPP b/ENDING.CPP new file mode 100644 index 0000000..2484ed5 --- /dev/null +++ b/ENDING.CPP @@ -0,0 +1,261 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\ending.cpv 1.5 16 Oct 1995 16:50:30 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ENDING.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : July 10, 1995 * + * * + * Last Update : July 10, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "textblit.h" + +void GDI_Ending(void) +{ +#ifdef DEMO + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + Load_Title_Screen("DEMOPIC.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Fade_Palette_To(Palette, FADE_PALETTE_MEDIUM, Call_Back); + Clear_KeyBuffer(); + Get_Key_Num(); + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + VisiblePage.Clear(); + +#else + if (TempleIoned) { + Play_Movie("GDIFINB"); + } else { + Play_Movie("GDIFINA"); + } + + Score.Presentation(); + + if (TempleIoned) { + Play_Movie("GDIEND2"); + } else { + Play_Movie("GDIEND1"); + } + + CountDownTimerClass count; + if (CCFileClass("TRAILER.VQA").Is_Available()) { + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + Load_Uncompress(CCFileClass("ATTRACT2.CPS"), SysMemPage, SysMemPage, Palette); + SysMemPage.Scale(SeenBuff, 0, 0, 0, 0, 320, 199, 640, 398); + Fade_Palette_To(Palette, FADE_PALETTE_MEDIUM, Call_Back); + Clear_KeyBuffer(); + count.Set(TIMER_SECOND*3); + while (count.Time()) { + Call_Back(); + } + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + + Play_Movie("TRAILER"); // Red Alert teaser. + } + + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + Load_Uncompress(CCFileClass("ATTRACT2.CPS"), SysMemPage, SysMemPage, Palette); + SysMemPage.Scale(SeenBuff, 0, 0, 0, 0, 320, 199, 640, 398); + Fade_Palette_To(Palette, FADE_PALETTE_MEDIUM, Call_Back); + Clear_KeyBuffer(); +// CountDownTimerClass count; + count.Set(TIMER_SECOND*3); + while (count.Time()) { + Call_Back(); + } + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + + Play_Movie("CC2TEASE"); +#endif +} + + +#ifndef DEMO +/*********************************************************************************************** + * Nod_Ending -- play ending movies for Nod players * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 7/10/1995 BWG : Created. * + *=============================================================================================*/ +void Nod_Ending(void) +{ + static char const _tanpal[]={0x0,0xED,0xED,0x2C,0x2C,0xFB,0xFB,0xFD,0xFD,0x0,0x0,0x0,0x0,0x0,0x52,0x0}; + + char fname[12]; +#ifdef NOT_FOR_WIN95 + char *satpic = new char[64000]; +#endif //NOT_FOR_WIN95 + int oldfontxspacing = FontXSpacing; + void const *oldfont; + + Score.Presentation(); + + oldfont = Set_Font(ScoreFontPtr); + + PseudoSeenBuff = new GraphicBufferClass(320,200,(void*)NULL); + TextPrintBuffer = new GraphicBufferClass(SeenBuff.Get_Width(), SeenBuff.Get_Height(), (void*)NULL); + TextPrintBuffer->Clear(); + BlitList.Clear(); + SeenBuff.Clear(); + HidPage.Clear(); + PseudoSeenBuff->Clear(); + + void * localpal = Load_Alloc_Data(CCFileClass("SATSEL.PAL")); + Load_Uncompress(CCFileClass("SATSEL.CPS"), SysMemPage, SysMemPage); +#ifdef NOT_FOR_WIN95 + memcpy(satpic, HidPage.Get_Buffer(), 64000); +#else + SysMemPage.Blit(*PseudoSeenBuff); +#endif //NOT_FOR_WIN95 + void *kanefinl = Load_Sample("KANEFINL.AUD"); + void *loopie6m = Load_Sample("LOOPIE6M.AUD"); + + Play_Movie("NODFINAL", THEME_NONE, false); + + Hide_Mouse(); + Wait_Vert_Blank(); + Set_Palette(localpal); +#ifdef NOT_FOR_WIN95 + memcpy(SeenBuff.Get_Buffer(), satpic, 64000); +#endif //NOT_FOR_WIN95 + Show_Mouse(); + + InterpolationPaletteChanged = TRUE; + InterpolationPalette = (unsigned char*)localpal; + Increase_Palette_Luminance(InterpolationPalette , 30,30,30,63); + Read_Interpolation_Palette("SATSELIN.PAL"); + Interpolate_2X_Scale(PseudoSeenBuff, &SeenBuff,"SATSELIN.PAL"); + + Keyboard::Clear(); + Play_Sample(kanefinl,255,128); + Play_Sample(loopie6m,255,128); + + bool mouseshown = false; + bool done = false; + int selection = 1; + bool printedtext = false; + while (!done) { + if (!printedtext && !Is_Sample_Playing(kanefinl)) { + printedtext++; + Alloc_Object(new ScorePrintClass(Text_String(TXT_SEL_TARGET), 0, 180,_tanpal)); + mouseshown = true; + Show_Mouse(); + } + Call_Back_Delay(1); + if (!Keyboard::Check()) { + if (!Is_Sample_Playing(loopie6m)) Play_Sample(loopie6m,255,128); + } else { + if (Is_Sample_Playing(kanefinl)) { + Clear_KeyBuffer(); + } else { + int key = Keyboard::Get(); + if ((key & 0x10FF) == KN_LMOUSE && !(key & KN_RLSE_BIT)) { + int mousex = _Kbd->MouseQX; + int mousey = _Kbd->MouseQY; + if (mousey >= 22*2 && mousey <= 177*2) { + done++; + if (mousex < 160*2 && mousey < 100*2) selection = 2; + if (mousex < 160*2 && mousey >= 100*2) selection = 3; + if (mousex >= 160*2 && mousey >= 100*2) selection = 4; + } + } + } + } + } + if (mouseshown) Hide_Mouse(); +#ifdef NOT_FOR_WIN95 + delete satpic; +#else + delete PseudoSeenBuff; +#endif //NOT_FOR_WIN95 + +/* get rid of all the animating objects */ + for (int i = 0; i < MAXSCOREOBJS; i++) if (ScoreObjs[i]) { + delete ScoreObjs[i]; + ScoreObjs[i] = 0; + } + // erase the "choose a target" text + SeenBuff.Fill_Rect(0,180*2,319*2,199*2,0); + TextPrintBuffer->Fill_Rect(0,180*2,319*2,199*2,0); + + Hide_Mouse(); + Keyboard::Clear(); + + Set_Font(oldfont); + FontXSpacing = oldfontxspacing; + Free_Sample(kanefinl); + Free_Sample(loopie6m); + + sprintf(fname,"NODEND%d",selection); + PreserveVQAScreen = 1; + Play_Movie(fname); + + CountDownTimerClass count; + if (CCFileClass("TRAILER.VQA").Is_Available()) { + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + Load_Uncompress(CCFileClass("ATTRACT2.CPS"), SysMemPage, SysMemPage, Palette); + SysMemPage.Scale(SeenBuff, 0, 0, 0, 0, 320, 199, 640, 398); + Fade_Palette_To(Palette, FADE_PALETTE_MEDIUM, Call_Back); + Clear_KeyBuffer(); + count.Set(TIMER_SECOND*3); + while (count.Time()) { + Call_Back(); + } + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + + Play_Movie("TRAILER"); // Red Alert teaser. + } + + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + Load_Uncompress(CCFileClass("ATTRACT2.CPS"), SysMemPage, SysMemPage, Palette); + SysMemPage.Scale(SeenBuff, 0, 0, 0, 0, 320, 199, 640, 398); + Fade_Palette_To(Palette, FADE_PALETTE_MEDIUM, Call_Back); + Clear_KeyBuffer(); +// CountDownTimerClass count; + count.Set(TIMER_SECOND*3); + while (count.Time()) { + Call_Back(); + } + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + + Play_Movie("CC2TEASE"); + + delete [] localpal; + delete TextPrintBuffer; + BlitList.Clear(); +} +#endif diff --git a/ENDING.H b/ENDING.H new file mode 100644 index 0000000..65a28e4 --- /dev/null +++ b/ENDING.H @@ -0,0 +1,42 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ENDING.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : July 10, 1995 * + * * + * Last Update : July 10, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef ENDING_H +#define ENDING_H + +void Nod_Ending(void); + +#endif diff --git a/EVENT.CPP b/EVENT.CPP new file mode 100644 index 0000000..3f05482 --- /dev/null +++ b/EVENT.CPP @@ -0,0 +1,760 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\event.cpv 2.17 16 Oct 1995 16:50:28 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : EVENT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/09/94 * + * * + * Last Update : June 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * EventClass::EventClass -- Construct an id and cell based event. * + * EventClass::EventClass -- Construct simple target type event. * + * EventClass::EventClass -- Constructor for mission change events. * + * EventClass::EventClass -- Constructor for navigation computer events. * + * EventClass::EventClass -- Constructor for object types affecting cells event. * + * EventClass::EventClass -- Constructor for sidebar build events. * + * EventClass::EventClass -- Constructs event to transfer special flags. * + * EventClass::EventClass -- Default constructor for event objects. * + * EventClass::EventClass -- Event for sequencing animations. * + * EventClass::EventClass -- Megamission assigned to unit. * + * EventClass::Execute -- Execute a queued command. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "ccdde.h" + +/*************************************************************************** +** Table of what data is really used in the EventClass struct for different +** events. This table must be kept current with the EventType enum. +*/ +unsigned char EventClass::EventLength[EventClass::LAST_EVENT] = { + 0, // EMPTY + size_of(EventClass, Data.General ), // ALLY + size_of(EventClass, Data.MegaMission ), // MEGAMISSION + size_of(EventClass, Data.Target ), // IDLE + size_of(EventClass, Data.Target ), // SCATTER + 0, // DESTRUCT + 0, // DEPLOY + size_of(EventClass, Data.Place ), // PLACE + 0, // OPTIONS + size_of(EventClass, Data.General ), // GAMESPEED + size_of(EventClass, Data.Specific ), // PRODUCE + size_of(EventClass, Data.Specific.Type ), // SUSPEND + size_of(EventClass, Data.Specific.Type ), // ABANDON + size_of(EventClass, Data.Target ), // PRIMARY + size_of(EventClass, Data.Special ), // SPECIAL_PLACE + 0, // EXIT + size_of(EventClass, Data.Anim ), // ANIMATION + size_of(EventClass, Data.Target ), // REPAIR + size_of(EventClass, Data.Target ), // SELL + size_of(EventClass, Data.Options ), // SPECIAL + 0, // FRAMESYNC + 0, // MESSAGE + size_of(EventClass, Data.FrameInfo.Delay ), // RESPONSE_TIME + size_of(EventClass, Data.FrameInfo ), // FRAMEINFO + size_of(EventClass, Data.Timing ), // TIMING + size_of(EventClass, Data.ProcessTime ), // PROCESS_TIME +}; + +char * EventClass::EventNames[EventClass::LAST_EVENT] = { + "EMPTY", + "ALLY", + "MEGAMISSION", + "IDLE", + "SCATTER", + "DESTRUCT", + "DEPLOY", + "PLACE", + "OPTIONS", + "GAMESPEED", + "PRODUCE", + "SUSPEND", + "ABANDON", + "PRIMARY", + "SPECIAL_PLACE", + "EXIT", + "ANIMATION", + "REPAIR", + "SELL", + "SPECIAL", + "FRAMESYNC", + "MESSAGE", + "RESPONSE_TIME", + "FRAMEINFO", + "TIMING", + "PROCESS_TIME", +}; + + +/*********************************************************************************************** + * EventClass::EventClass -- Constructs event to transfer special flags. * + * * + * This constructs an event that will transfer the special flags. * + * * + * INPUT: data -- The special flags to be transported to all linked computers. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(SpecialClass data) +{ + ID = Houses.ID(PlayerPtr); + Type = SPECIAL; + Frame = ::Frame; + Data.Options.Data = data; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Construct simple target type event. * + * * + * This will construct a generic event that needs only a target parameter. The actual * + * event and target values are specified as parameters. * + * * + * INPUT: type -- The event type to construct. * + * * + * target-- The target value that this event is to apply to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, TARGET target) +{ + ID = Houses.ID(PlayerPtr); + Type = type; + Frame = ::Frame; + Data.Target.Whom = target; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Default constructor for event objects. * + * * + * This constructs a simple event object that requires no parameters other than the * + * type of event it is. * + * * + * INPUT: type -- The type of event to construct. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type) +{ + ID = Houses.ID(PlayerPtr); + Type = type; + Frame = ::Frame; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Constructor for general-purpose-data events. * + * * + * INPUT: type -- The type of event to construct. * + * val -- data value * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, int val) +{ + ID = Houses.ID(PlayerPtr); + Type = type; + Data.General.Value = val; + Frame = ::Frame; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Constructor for navigation computer events. * + * * + * Constructor for events that are used to assign the navigation computer. * + * * + * INPUT: type -- The type of event (this constructor can be used by other navigation * + * type events). * + * * + * src -- The object that the event should apply to. * + * * + * dest -- The destination (or target) that the event needs to complete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, TARGET src, TARGET dest) +{ + ID = Houses.ID(PlayerPtr); + Type = type; + Frame = ::Frame; + Data.NavCom.Whom = src; + Data.NavCom.Where = dest; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Event for sequencing animations. * + * * + * This constructor is used for animations that must be created through the event system. * + * * + * INPUT: anim -- The animation that will be created. * + * * + * coord -- The location where the animation is to be created. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(AnimType anim, HousesType owner, COORDINATE coord) +{ + ID = Houses.ID(PlayerPtr); + Type = ANIMATION; + Frame = ::Frame; + Data.Anim.What = anim; + Data.Anim.Owner = owner; + Data.Anim.Where = coord; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Megamission assigned to unit. * + * * + * This is the event that is used to assign most missions to units. It combines both the * + * mission and the target (navcom and tarcom). * + * * + * INPUT: src -- The object that this mission is to apply to. * + * * + * mission -- The mission to assign to this object. * + * * + * target -- The target to assign to this object's TarCom. * + * * + * destination -- The destination to assign to this object's NavCom. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(TARGET src, MissionType mission, TARGET target, TARGET destination) +{ + ID = Houses.ID(PlayerPtr); + Type = MEGAMISSION; + Frame = ::Frame; + Data.MegaMission.Whom = src; + Data.MegaMission.Mission = mission; + Data.MegaMission.Target = target; + Data.MegaMission.Destination = destination; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Constructor for sidebar build events. * + * * + * This constructor is used for events that deal with an object type and an object ID. * + * Typically, this is used exclusively by the sidebar. * + * * + * INPUT: type -- The event type of this object. * + * * + * object -- The object type number. * + * * + * id -- The object sub-type number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, RTTIType object, int id) +{ + ID = Houses.ID(PlayerPtr); + Type = type; + Frame = ::Frame; + Data.Specific.Type = object; + Data.Specific.ID = id; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Constructor for object types affecting cells event. * + * * + * This constructor is used for those events that have an object type and associated cell. * + * Typically, this is for building placement after construction has completed. * + * * + * INPUT: type -- The event type for this object. * + * * + * object -- The object type number (actual object is probably inferred from the * + * sidebar data). * + * * + * cell -- The cell location where this event is to occur. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, RTTIType object, CELL cell) +{ + ID = Houses.ID(PlayerPtr); + Type = type; + Frame = ::Frame; + Data.Place.Type = object; + Data.Place.Cell = cell; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Construct an id and cell based event. * + * * + * This constructor is used for those events that require an ID number and a cell location. * + * * + * INPUT: type -- The event type this will be. * + * * + * id -- The arbitrary id number to assign. * + * * + * cell -- The location for this event. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, int id, CELL cell) +{ + ID = Houses.ID(PlayerPtr); + Type = type; + Frame = ::Frame; + Data.Special.ID = id; + Data.Special.Cell = cell; +} + + +/*********************************************************************************************** + * EventClass::Execute -- Execute a queued command. * + * * + * This routine executes an event. The even must already have been confirmed by any * + * remote machine before calling this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +void EventClass::Execute(void) +{ + TechnoClass * techno; + AnimClass * anim = 0; + HouseClass * house = 0; + char txt[80]; + int i; +//#if (0) +if (Type < 0 || Type > PROCESS_TIME){ +char tempbuf[128]; +sprintf (tempbuf, "Packet type %d received\n", Type); +CCDebugString (tempbuf); + +sprintf (tempbuf, " ID = %d\n", ID); +CCDebugString (tempbuf); + +sprintf (tempbuf, " Frame = %d\n", Frame); +CCDebugString (tempbuf); + +sprintf (tempbuf, " MPlayer ID = %d\n", MPlayerID); +CCDebugString (tempbuf); + +} +//#endif //(0) + + + switch (Type) { + /* + ** Make or break alliance. + */ + case ALLY: + house = Houses.Raw_Ptr(Data.General.Value); + if (Houses.Raw_Ptr(ID)->Is_Ally(house)) { + Houses.Raw_Ptr(ID)->Make_Enemy((HousesType)Data.General.Value); + } else { + Houses.Raw_Ptr(ID)->Make_Ally((HousesType)Data.General.Value); + } + break; + + /* + ** Special self destruct action requested. This is active in the multiplayer mode. + */ + case DESTRUCT: +CCDebugString ("C&C95 - Resignation packet received\n"); + Houses.Raw_Ptr(ID)->Flag_To_Die(); + Houses.Raw_Ptr(ID)->Resigned = true; + break; + + /* + ** Update the special control flags. This is necessary so that in a multiplay + ** game, all machines will agree on the rules. If these options change during + ** game play, then all players are informed that options have changed. + */ + case SPECIAL: + { + Special = Data.Options.Data; + HouseClass * house = Houses.Raw_Ptr(ID); + + sprintf(txt, Text_String(TXT_SPECIAL_WARNING), house->Name); + Messages.Add_Message(txt, MPlayerTColors[house->RemapColor], + TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1200, 0, 0); + Map.Flag_To_Redraw(false); + } + break; + + /* + ** Starts or stops repair on the specified object. This event is triggered by the + ** player clicking the repair wrench on a building. + */ + case REPAIR: +CCDebugString ("C&C95 - Repair packet received\n"); + techno = As_Techno(Data.Target.Whom); + if (techno && techno->IsActive) { + techno->Repair(-1); + } + break; + + /* + ** Tells a building/unit to sell. This event is triggered by the player clicking the + ** sell animating cursor over the building or unit. + */ + case SELL: +CCDebugString ("C&C95 - Sell packet received\n"); + techno = As_Techno(Data.Target.Whom); + if (techno && techno->IsActive && techno->House == Houses.Raw_Ptr(ID)) { + techno->Sell_Back(-1); + } else { + if (Is_Target_Cell(Data.Target.Whom)) { + Houses.Raw_Ptr(ID)->Sell_Wall(As_Cell(Data.Target.Whom)); + } + } + break; + + /* + ** This even is used to trigger an animation that is generated as a direct + ** result of player intervention. + */ + case ANIMATION: + anim = new AnimClass(Data.Anim.What, Data.Anim.Where); + if (anim) { + if (Data.Anim.Owner != HOUSE_NONE && PlayerPtr->Class->House != Data.Anim.Owner && !Special.IsVisibleTarget) { + anim->Make_Invisible(); + } + } + break; + + /* + ** This event will place the specified object at the specified location. + ** The event is used to place newly constructed buildings down on the map. The + ** object type is specified. From this object type, the house can determine the + ** exact factory and real object pointer to use. + */ + case PLACE: +CCDebugString ("C&C95 - Place packet received\n"); + Houses.Raw_Ptr(ID)->Place_Object(Data.Place.Type, Data.Place.Cell); + break; + + /* + ** This event starts production of the speicified object type. The house can + ** determine from the type and ID value, what object to begin production on and + ** what factory to use. + */ + case PRODUCE: +CCDebugString ("C&C95 - Produce packet received\n"); + Houses.Raw_Ptr(ID)->Begin_Production(Data.Specific.Type, Data.Specific.ID); + break; + + /* + ** This event is generated when the player puts production on hold. From the + ** object type, the factory can be inferred. + */ + case SUSPEND: +CCDebugString ("C&C95 - Suspend packet received\n"); + Houses.Raw_Ptr(ID)->Suspend_Production(Data.Specific.Type); + break; + + /* + ** This event is generated when the player cancels production of the specified + ** object type. From the object type, the exact factory can be inferred. + */ + case ABANDON: +CCDebugString ("C&C95 - Abandon packet received\n"); + Houses.Raw_Ptr(ID)->Abandon_Production(Data.Specific.Type); + break; + + /* + ** Toggles the primary factory state of the specified building. + */ + case PRIMARY:{ +CCDebugString ("C&C95 - Primary building packet received\n"); + BuildingClass * building = As_Building(Data.Target.Whom); + if (building && building->IsActive) { + building->Toggle_Primary(); + } + } + break; + + /* + ** This is the general purpose mission control event. Most player + ** action routes through this event. It sets a unit's mission, TarCom, + ** and NavCom to the values specified. + */ + case MEGAMISSION: + techno = As_Techno(Data.MegaMission.Whom); + if (techno && techno->IsActive) { + + /* + ** Fetch a pointer to the object of the mission. + */ + ObjectClass * object; + if (Target_Legal(Data.MegaMission.Target)) { + object = As_Object(Data.MegaMission.Target); + } else { + object = As_Object(Data.MegaMission.Destination); + } + + /* + ** Break any existing team contact, since it is now invalid. + */ + if (!techno->IsTethered) { + techno->Transmit_Message(RADIO_OVER_OUT); + } + switch (techno->What_Am_I()) { + case RTTI_INFANTRY: + case RTTI_UNIT: + if (((FootClass *)techno)->Team) { + ((FootClass *)techno)->Team->Remove((FootClass *)techno); + } + break; + } + + if (object) { + if (PlayerPtr->Is_Ally(techno) || Special.IsVisibleTarget) { + object->Clicked_As_Target(); + } + } + techno->Assign_Mission(Data.MegaMission.Mission); + + /* + ** Guard area mode is handled with care. The specified target is actually + ** assigned as the location that should be guarded. In addition, the + ** movement destination is immediately set to this new location. + */ + if (Data.MegaMission.Mission == MISSION_GUARD_AREA && +// Target_Legal(Data.MegaMission.Target) && + (techno->What_Am_I() == RTTI_INFANTRY || techno->What_Am_I() == RTTI_UNIT || techno->What_Am_I() == RTTI_AIRCRAFT)) { + + ((FootClass *)techno)->ArchiveTarget = Data.MegaMission.Target; + techno->Assign_Target(TARGET_NONE); + techno->Assign_Destination(Data.MegaMission.Target); + } else { + techno->Assign_Target(Data.MegaMission.Target); + techno->Assign_Destination(Data.MegaMission.Destination); + } + +#ifdef NEVER + if ((techno->What_Am_I() == RTTI_UNIT || techno->What_Am_I() == RTTI_INFANTRY) && + Data.MegaMission.Mission == MISSION_GUARD_AREA) { + + ((FootClass *)techno)->ArchiveTarget = Data.MegaMission.Destination; + } +#endif + } + break; + + /* + ** Request that the unit/infantry/aircraft go into idle mode. + */ + case IDLE: + techno = As_Techno(Data.Target.Whom); + if (techno && techno->IsActive && !techno->IsInLimbo && !techno->IsTethered) { + techno->Assign_Destination(TARGET_NONE); + techno->Assign_Target(TARGET_NONE); + techno->Enter_Idle_Mode(); + } + break; + + /* + ** Request that the unit/infantry/aircraft scatter from its current location. + */ + case SCATTER: + techno = As_Techno(Data.Target.Whom); + if (techno && techno->IsActive && !techno->IsInLimbo && !techno->IsTethered) { + techno->Scatter(0, true); + } + break; + + /* + ** If we are placing down the ion cannon blast then lets take + ** care of it. + */ + case SPECIAL_PLACE: +CCDebugString ("C&C95 - Special blast packet received\n"); + Houses.Raw_Ptr(ID)->Place_Special_Blast((SpecialWeaponType)Data.Special.ID, Data.Special.Cell); + break; + + /* + ** Exit the game. + ** Give parting message while palette is fading to black. + */ + case EXIT: +CCDebugString ("C&C95 - Exit game packet received\n"); + Theme.Queue_Song(THEME_NONE); + Stop_Speaking(); + Speak(VOX_CONTROL_EXIT); + while (Is_Speaking()) { + Call_Back(); + } + GameActive = false; + break; + + /* + ** Process the options menu. + */ + case OPTIONS: + SpecialDialog = SDLG_OPTIONS; + break; + + /* + ** Process the options Game Speed + */ + case GAMESPEED: +CCDebugString ("C&C95 - Game speed packet received\n"); + Options.GameSpeed = Data.General.Value; + break; + + /* + ** Adjust connection timing for multiplayer games + */ + case RESPONSE_TIME: +char flip[128]; +sprintf (flip, "C&C95 - Changing MaxAhead to %d frames\n", Data.FrameInfo.Delay); +CCDebugString (flip); + MPlayerMaxAhead = Data.FrameInfo.Delay; + break; + + // + // This event tells all systems to use new timing values. It's like + // RESPONSE_TIME, only it works. It's only used with the + // COMM_MULTI_E_COMP protocol. + // + case TIMING: +CCDebugString ("C&C95 - Timing packet received\n"); +//#if(TIMING_FIX) + // + // If MaxAhead is about to increase, we're vulnerable to a Packet- + // Received-Too-Late error, if any system generates an event after + // this TIMING event, but before it executes. So, record the + // period of vulnerability's frame start & end values, so we + // can reschedule these events to execute after it's over. + // + if (Data.Timing.MaxAhead > MPlayerMaxAhead) { + NewMaxAheadFrame1 = Frame; + NewMaxAheadFrame2 = Frame + Data.Timing.MaxAhead; + } +//#endif + + DesiredFrameRate = Data.Timing.DesiredFrameRate; + MPlayerMaxAhead = Data.Timing.MaxAhead; + +sprintf (flip, "C&C95 - Timing packet: DesiredFrameRate = %d\n", Data.Timing.DesiredFrameRate); +CCDebugString (flip); +sprintf (flip, "C&C95 - Timing packet: MaxAhead = %d\n", Data.Timing.MaxAhead); +CCDebugString (flip); + + /* + ** If spawned from WChat then we should be getting poked every minute. If not then + ** deliberately break the max ahead value + */ + if (Special.IsFromWChat){ + MPlayerMaxAhead += DDEServer.Time_Since_Heartbeat()/(70*60); +//if (DDEServer.Time_Since_Heartbeat() >= 70*60) CCDebugString ("C&C95 - Missed a heartbeat\n"); + } + break; + + // + // This event tells all systems what the other systems' process + // timing requirements are; it's used to compute a desired frame rate + // for the game. + // + case PROCESS_TIME: + for (i = 0; i < MPlayerCount; i++) { + if (MPlayerID == ::MPlayerID[i]) { + TheirProcessTime[i] = Data.ProcessTime.AverageTicks; + +char flip[128]; +sprintf (flip, "C&C95 - Received PROCESS_TIME packet of %04x ticks\n", Data.ProcessTime.AverageTicks); +CCDebugString (flip); + + break; + } + } + break; + + /* + ** Default: do nothing. + */ + default: + break; + } +} diff --git a/EVENT.H b/EVENT.H new file mode 100644 index 0000000..f85f98e --- /dev/null +++ b/EVENT.H @@ -0,0 +1,226 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\event.h_v 2.19 16 Oct 1995 16:46:14 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : EVENT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/09/94 * + * * + * Last Update : December 9, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef EVENT_H +#define EVENT_H + +/* +** This event class is used to contain all external game events (things that the player can +** do at any time) so that these events can be transported between linked computers. This +** encapsulation is required in order to ensure that each event affects all computers at the +** same time (same game frame). +*/ +class EventClass +{ + public: + + /* + ** All external events are identified by these labels. + */ + typedef enum EventType { + EMPTY, + + ALLY, // Make allie of specified house. + MEGAMISSION, // Full change of mission with target and destination. + IDLE, // Request to enter idle mode. + SCATTER, // Request to scatter from current location. + DESTRUCT, // Self destruct request (surrender action). + DEPLOY, // MCV is to deploy at current location. + PLACE, // Place building at location specified. + OPTIONS, // Bring up options screen. + GAMESPEED, // Set game speed + PRODUCE, // Start or Resume production. + SUSPEND, // Suspend production. + ABANDON, // Abandon production. + PRIMARY, // Primary factory selected. + SPECIAL_PLACE, // Special target location selected + EXIT, // Exit game. + ANIMATION, // Flash ground as movement feedback. + REPAIR, // Repair specified object. + SELL, // Sell specified object. + SPECIAL, // Special options control. + + // Private events. + FRAMESYNC, // Game-connection packet; includes Scenario CRC & sender's frame # + // Used to initiate game connection phase & to reconnect; + // When one of these is received, the receiver knows there are + // no associated commands in this packet. + MESSAGE, // Message to another player (The message is the 40 bytes + // after the event class). + RESPONSE_TIME, // use a new propogation delay value + FRAMEINFO, // Game-heartbeat packet; includes Game CRC & command count + // All packets sent for a frame are prefixed with one of these + TIMING, // new timing values for all systems to use + PROCESS_TIME, // a system's average processing time, in ticks per frame + LAST_EVENT, // one past the last event + } EventType; + + EventType Type; // Type of queue command object. + + /* + ** 'Frame' is the frame that the command should execute on. + ** 27 bits gives over 25 days of playing time without wrapping, + ** at 30 frames per second, so it should be plenty! + */ + unsigned Frame : 27; + + /* + ** House index of the player originating this event + */ + unsigned ID : 4; + + /* + ** This bit tells us if we've already executed this event. + */ + unsigned IsExecuted: 1; + + /* + ** Multiplayer ID of the player originating this event. + ** High nybble: the color index of this player. + ** Low nybble: the HousesType this player is "acting like" (GDI/NOD) + */ + unsigned char MPlayerID; + + /* + ** This union contains the specific data that the event requires. + */ + union { + struct { + SpecialClass Data; // The special option flags. + } Options; + struct { + TARGET Whom; // The object to apply the event to. + } Target; + struct { + AnimType What; // The animation to create. + HousesType Owner; // The owner of the animation (when it matters). + COORDINATE Where; // The location to place the animation. + } Anim; + struct { + int Value; // general-purpose data + } General; + struct { + TARGET Whom; // Whom to apply mission to. + MissionType Mission; // What mission to apply. + TARGET Target; // Target to assign. + TARGET Destination;// Destination to assign. + } MegaMission; + struct { + TARGET Whom; // Whom to apply mission to. + MissionType Mission; // What mission to apply. + } Mission; + struct { + TARGET Whom; // Whom to apply movement change to. + TARGET Where; // Where to set NavCom to. + } NavCom; + struct { + TARGET Whom; // Whom to apply attack change to. + TARGET Target; // What to set TarCom to. + } TarCom; + struct { + RTTIType Type; + int ID; + } Specific; + struct { + RTTIType Type; + CELL Cell; + } Place; + struct { + int ID; + CELL Cell; + } Special; + /* + ** This structure is used for FRAMEINFO, FRAMESYNC, and RESPONSE_TIME + ** events; exactly one of these will be sent each frame, whether there's + ** data that frame or not. + ** CRC: the game CRC when this packet was generated; used to detect sync errors + ** CommandCount: # of commands the sender has sent; used to detect missed packets + ** Delay: sender's propogation delay value for this frame + */ + struct { + unsigned long CRC; + unsigned short CommandCount; // # commands sent so far + unsigned char Delay; // propogation delay used this frame + // (Frame - Delay = sender's current frame #) + } FrameInfo; + + // + // This structure sets new timing values for all systems in a multiplayer + // game. This structure replaces the RESPONSE_TIME event for + // the COMM_MULTI_E_COMP protocol. + // + struct { + unsigned short DesiredFrameRate; + unsigned short MaxAhead; + } Timing; + + // + // This structure is transmitted by all systems, and is used to compute + // the "desired" frame rate for the game. + // + struct { + unsigned short AverageTicks; + } ProcessTime; + + } Data; + + //-------------- Functions --------------------- + EventClass(void) {Type = EMPTY;}; + EventClass(SpecialClass data); + EventClass(EventType type, TARGET target); + EventClass(EventType type); + EventClass(EventType type, int val); + EventClass(EventType type, TARGET src, TARGET dest); +// EventClass(TARGET src, MissionType mission); + EventClass(TARGET src, MissionType mission, TARGET target=TARGET_NONE, TARGET destination=TARGET_NONE); + EventClass(EventType type, RTTIType object, int id); + EventClass(EventType type, RTTIType object, CELL cell); + EventClass(EventType type, int id, CELL cell); + EventClass(AnimType anim, HousesType owner, COORDINATE coord); + + // Process the event. + void Execute(void); + + int operator == (EventClass & q) { + return memcmp(this, &q, sizeof(q)) == 0; + }; + + static unsigned char EventLength[LAST_EVENT]; + static char * EventNames[LAST_EVENT]; +}; + +#endif diff --git a/EXPAND.CPP b/EXPAND.CPP new file mode 100644 index 0000000..1465495 --- /dev/null +++ b/EXPAND.CPP @@ -0,0 +1,422 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header$ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : EXPAND.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/03/95 * + * * + * Last Update : November 3, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef NEWMENU + +bool Expansion_Present(void) +{ + CCFileClass file("EXPAND.DAT"); + + return(file.Is_Available()); +} + + + + +class EListClass : public ListClass +{ + public: + EListClass(int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down) : + ListClass(id, x, y, w, h, flags, up, down) {}; + + protected: + virtual void Draw_Entry(int index, int x, int y, int width, int selected); +}; + + +void EListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + if (TextFlags & TPF_6PT_GRAD) { + TextPrintType flags = TextFlags; + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, CC_GREEN_SHADOW); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(List[index]+sizeof(int), x, y, CC_GREEN, TBLACK, flags, width, Tabs); + + } else { + Conquer_Clip_Text_Print(List[index]+sizeof(int), x, y, (selected ? BLUE : WHITE), TBLACK, TextFlags, width, Tabs); + } +} + +bool Expansion_Dialog(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + int option_width = 236 * factor; + int option_height = 162 * factor; + int option_x = (320*factor - option_width) /2; + int option_y = (200*factor - option_height) /2; + + GadgetClass * buttons = NULL; + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + TextButtonClass ok(200, TXT_OK, TPF_6PT_GRAD|TPF_NOSHADOW, option_x+25*factor, option_y+option_height-15*factor); + TextButtonClass cancel(201, TXT_CANCEL, TPF_6PT_GRAD|TPF_NOSHADOW, option_x+option_width-50*factor, option_y+option_height-15*factor); + EListClass list(202, option_x+10*factor, option_y+20*factor, option_width-20*factor, option_height-40*factor, TPF_6PT_GRAD|TPF_NOSHADOW, up_button, down_button); + + buttons = &ok; + cancel.Add(*buttons); + list.Add(*buttons); + + + /* + ** Add in all the expansion scenarios. + */ + char * sbuffer = (char*)_ShapeBuffer; + for (int index = 20; index < 60; index++) { + char buffer[128]; + CCFileClass file; + + Set_Scenario_Name(buffer, index, SCEN_PLAYER_GDI, SCEN_DIR_EAST, SCEN_VAR_A); + strcat(buffer, ".INI"); + file.Set_Name(buffer); + if (file.Is_Available()) { + file.Read(sbuffer, 1000); + sbuffer[1000] = '\r'; + sbuffer[1000+1] = '\n'; + sbuffer[1000+2] = '\0'; + + WWGetPrivateProfileString("Basic", "Name", "x", buffer, sizeof(buffer), sbuffer); + char * data = new char [strlen(buffer)+1+sizeof(int)+25]; + *((int*)&data[0]) = index; + strcpy(&data[sizeof(int)], "GDI: "); + strcat(&data[sizeof(int)], buffer); + list.Add_Item(data); + } + } + + for (index = 20; index < 60; index++) { + char buffer[128]; + CCFileClass file; + + Set_Scenario_Name(buffer, index, SCEN_PLAYER_NOD, SCEN_DIR_EAST, SCEN_VAR_A); + strcat(buffer, ".INI"); + file.Set_Name(buffer); + if (file.Is_Available()) { + file.Read(sbuffer, 1000); + sbuffer[1000] = '\r'; + sbuffer[1000+1] = '\n'; + sbuffer[1000+2] = '\0'; + + WWGetPrivateProfileString("Basic", "Name", "x", buffer, sizeof(buffer), sbuffer); + char * data = new char [strlen(buffer)+1+sizeof(int)+25]; + *((int*)&data[0]) = index; + strcpy(&data[sizeof(int)], "NOD: "); + strcat(&data[sizeof(int)], buffer); + list.Add_Item(data); + } + } + + Set_Logic_Page(SeenBuff); + bool recalc = true; + bool display = true; + bool process = true; + bool okval = true; + while (process) { + + Call_Back(); + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=TRUE; + } + + if (display) { + display = false; + + Hide_Mouse(); + + /* + ** Load the background picture. + */ + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + + Dialog_Box(option_x, option_y, option_width, option_height); + Draw_Caption(TXT_MISSION_DESCRIPTION, option_x, option_y, option_width); + buttons->Draw_All(); + Show_Mouse(); + } + + KeyNumType input = buttons->Input(); + switch (input) { + case KN_RETURN: + case 200|KN_BUTTON: + if (list.Current_Item()[sizeof(int)] == 'G') { + ScenPlayer = SCEN_PLAYER_GDI; + } else { + ScenPlayer = SCEN_PLAYER_NOD; + } + ScenDir = SCEN_DIR_EAST; + Whom = HOUSE_GOOD; + Scenario = *(int *)list.Current_Item(); + process = false; + okval = true; + break; + + case KN_ESC: + case 201|KN_BUTTON: + ScenPlayer = SCEN_PLAYER_GDI; + ScenDir = SCEN_DIR_EAST; + Whom = HOUSE_GOOD; + Scenario = *(int *)list.Current_Item(); + process = false; + okval = false; + break; + + default: + break; + } + } + + /* + ** Free up the allocations for the text lines in the list box. + */ + for (index = 0; index < list.Count(); index++) { + delete [] (char *)list.Get_Item(index); + } + + return(okval); +} + + + + + + + + + + + + +/*********************************************************************************************** + * Bonus_Dialog -- Asks the user which bonus mission he wants to play * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/26/97 11:07AM ST : Created * + *=============================================================================================*/ +bool Bonus_Dialog(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + int option_width = 236 * factor; + int option_height = 162 * factor; + int option_x = (320*factor - option_width) /2; + int option_y = (200*factor - option_height) /2; + + GadgetClass * buttons = NULL; + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + TextButtonClass ok(200, TXT_OK, TPF_6PT_GRAD|TPF_NOSHADOW, option_x+25*factor, option_y+option_height-15*factor); + TextButtonClass cancel(201, TXT_CANCEL, TPF_6PT_GRAD|TPF_NOSHADOW, option_x+option_width-50*factor, option_y+option_height-15*factor); + EListClass list(202, option_x+10*factor, option_y+20*factor, option_width-20*factor, option_height-40*factor, TPF_6PT_GRAD|TPF_NOSHADOW, up_button, down_button); + + buttons = &ok; + cancel.Add(*buttons); + list.Add(*buttons); + + + /* + ** Add in all the expansion scenarios. + */ + char * sbuffer = (char*)_ShapeBuffer; + int gdi_scen_names[3]={ + TXT_BONUS_MISSION_1, + TXT_BONUS_MISSION_2, + TXT_BONUS_MISSION_3 + }; + + int nod_scen_names[2]={ + TXT_BONUS_MISSION_4, + TXT_BONUS_MISSION_5 + }; + + + for (int index = 60; index < 63; index++) { + char buffer[128]; + CCFileClass file; + + Set_Scenario_Name(buffer, index, SCEN_PLAYER_GDI, SCEN_DIR_EAST, SCEN_VAR_A); + strcat(buffer, ".INI"); + file.Set_Name(buffer); + if (file.Is_Available()) { + memcpy (buffer, Text_String (gdi_scen_names[index-60]), 1+strlen(Text_String (gdi_scen_names[index-60]))); + char * data = new char [strlen(buffer)+1+sizeof(int)+25]; + *((int*)&data[0]) = index; + strcpy(&data[sizeof(int)], "GDI: "); + strcat(&data[sizeof(int)], buffer); + list.Add_Item(data); + } + } + + for (index = 60; index < 62; index++) { + char buffer[128]; + CCFileClass file; + + Set_Scenario_Name(buffer, index, SCEN_PLAYER_NOD, SCEN_DIR_EAST, SCEN_VAR_A); + strcat(buffer, ".INI"); + file.Set_Name(buffer); + if (file.Is_Available()) { + memcpy (buffer, Text_String (nod_scen_names[index-60]), 1+strlen(Text_String (nod_scen_names[index-60]))); + char * data = new char [strlen(buffer)+1+sizeof(int)+25]; + *((int*)&data[0]) = index; + strcpy(&data[sizeof(int)], "NOD: "); + strcat(&data[sizeof(int)], buffer); + list.Add_Item(data); + } + } + + Set_Logic_Page(SeenBuff); + bool recalc = true; + bool display = true; + bool process = true; + bool okval = true; + while (process) { + + Call_Back(); + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=TRUE; + } + + if (display) { + display = false; + + Hide_Mouse(); + + /* + ** Load the background picture. + */ + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + + Dialog_Box(option_x, option_y, option_width, option_height); + Draw_Caption(TXT_BONUS_MISSIONS, option_x, option_y, option_width); + buttons->Draw_All(); + Show_Mouse(); + } + + KeyNumType input = buttons->Input(); + switch (input) { + case KN_RETURN: + case 200|KN_BUTTON: + if (list.Current_Item()[sizeof(int)] == 'G') { + ScenPlayer = SCEN_PLAYER_GDI; + } else { + ScenPlayer = SCEN_PLAYER_NOD; + } + ScenDir = SCEN_DIR_EAST; + Whom = HOUSE_GOOD; + Scenario = *(int *)list.Current_Item(); + process = false; + okval = true; + break; + + case KN_ESC: + case 201|KN_BUTTON: + ScenPlayer = SCEN_PLAYER_GDI; + ScenDir = SCEN_DIR_EAST; + Whom = HOUSE_GOOD; + Scenario = *(int *)list.Current_Item(); + process = false; + okval = false; + break; + + default: + break; + } + } + + /* + ** Free up the allocations for the text lines in the list box. + */ + for (index = 0; index < list.Count(); index++) { + delete [] (char *)list.Get_Item(index); + } + + return(okval); +} + + +#endif diff --git a/EXTERNS.H b/EXTERNS.H new file mode 100644 index 0000000..bf2edce --- /dev/null +++ b/EXTERNS.H @@ -0,0 +1,401 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\externs.h_v 2.15 16 Oct 1995 16:45:34 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : EXTERNS.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 27, 1994 * + * * + * Last Update : May 27, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef EXTERNS_H +#define EXTERNS_H + +#include "cell.h" + +#ifdef SCENARIO_EDITOR +#include "mapedit.h" +#endif +#include "techno.h" +#include "type.h" +#include "building.h" +#include "unit.h" +#include "credits.h" +#include "goptions.h" +#include "options.h" +#include "infantry.H" + +#ifdef JAPANESE +extern bool ForceEnglish; +#endif + +extern bool Debug_Quiet; +extern bool Debug_Cheat; +extern bool Debug_Remap; +extern bool Debug_Flag; +extern bool Debug_Lose; +extern bool Debug_Map; +extern bool Debug_Win; +extern bool Debug_Icon; +extern bool Debug_Passable; +extern bool Debug_Unshroud; +extern bool Debug_Threat; +extern bool Debug_Find_Path; +extern bool Debug_Check_Map; +extern bool Debug_Playtest; + +extern bool Debug_Heap_Dump; +extern bool Debug_Smart_Print; +extern bool Debug_Trap_Check_Heap; +extern bool Debug_Instant_Build; + +extern void const *WarFactoryOverlay; + + +/* +** Dynamic global variables (these change or are initialized at run time). +*/ +#ifdef PATCH +extern bool IsV107; +extern char OverridePath[128]; +#endif +extern bool SlowPalette; +extern char VersionText[16]; +extern bool ScoresPresent; +extern int CrateCount; +extern TCountDownTimerClass CrateTimer; +extern bool CrateMaker; +extern ThemeType TransitTheme; +extern bool AllowVoice; +extern NewConfigType NewConfig; +extern char BriefingText[512]; +extern char IntroMovie[_MAX_FNAME+_MAX_EXT]; +extern char ActionMovie[_MAX_FNAME+_MAX_EXT]; +extern char BriefMovie[_MAX_FNAME+_MAX_EXT]; +extern char WinMovie[_MAX_FNAME+_MAX_EXT]; +extern char LoseMovie[_MAX_FNAME+_MAX_EXT]; +extern VoxType SpeakQueue; +extern bool PlayerWins; +extern bool PlayerLoses; +extern bool PlayerRestarts; +extern StructType SabotagedType; +extern bool TempleIoned; +extern long Frame; +extern void * SpeechBuffer; +extern int PreserveVQAScreen; +extern bool BreakoutAllowed; +extern bool Brokeout; +extern CELL Views[4]; + +extern GameOptionsClass Options; + +extern LogicClass Logic; +#ifdef SCENARIO_EDITOR +extern MapEditClass Map; +#else +extern MouseClass Map; +#endif +extern ScoreClass Score; +extern MonoClass MonoArray[MonoClass::MAX_MONO_PAGES]; +extern MixFileClass * ScoreMix; +extern MixFileClass * TheaterData; +extern MixFileClass * LowTheaterData; +extern MixFileClass * MoviesMix; +extern MixFileClass * GeneralMix; +extern ThemeClass Theme; +extern SpecialClass Special; + +/* +** Game object allocation and tracking classes. +*/ +extern TFixedIHeapClass Units; +extern TFixedIHeapClass Factories; +extern TFixedIHeapClass Terrains; +extern TFixedIHeapClass Templates; +extern TFixedIHeapClass Smudges; +extern TFixedIHeapClass Overlays; +extern TFixedIHeapClass Infantry; +extern TFixedIHeapClass Bullets; +extern TFixedIHeapClass Buildings; +extern TFixedIHeapClass Anims; +extern TFixedIHeapClass Aircraft; +extern TFixedIHeapClass Triggers; +extern TFixedIHeapClass TeamTypes; +extern TFixedIHeapClass Teams; +extern TFixedIHeapClass Houses; + +extern QueueClass OutList; +extern QueueClass DoList; + +extern DynamicVectorClass CurrentObject; +extern DynamicVectorClass CellTriggers; +extern DynamicVectorClass HouseTriggers[HOUSE_COUNT]; + +extern CELL Waypoint[WAYPT_COUNT]; + +extern BaseClass Base; + +/* +** Loaded data file pointers. +*/ +extern void const * Green12FontPtr; +extern void const * Green12GradFontPtr; +extern void const * MapFontPtr; +extern void const * VCRFontPtr; +extern void const * Font3Ptr; +extern void const * Font6Ptr; +extern void const * Font8Ptr; +extern void const * FontLEDPtr; +extern void const * ScoreFontPtr; +extern void const * GradFont6Ptr; +extern char const * SystemStrings; + +/* +** Miscellaneous globals. +*/ +extern HousesType Whom; +extern _VQAConfig AnimControl; +extern long SpareTicks; +extern int MonoPage; +extern unsigned char * OriginalPalette; +extern int EndCountDown; +extern bool GameActive; +extern bool SpecialFlag; +extern bool ScenarioInit; +extern long TutorFlags[2]; +extern HouseClass * PlayerPtr; +extern unsigned char * BlackPalette; +extern unsigned char * WhitePalette; +extern unsigned char * GamePalette; +extern unsigned Scenario; +extern ScenarioPlayerType ScenPlayer; +extern ScenarioDirType ScenDir; +extern ScenarioVarType ScenVar; +extern int CarryOverMoney; +extern int CarryOverCap; +extern int CarryOverPercent; +extern char ScenarioName[_MAX_FNAME+_MAX_EXT]; +extern unsigned BuildLevel; +extern unsigned long ScenarioCRC; + +#ifdef SCENARIO_EDITOR +extern CELL CurrentCell; +#endif + +extern GameType GameToPlay; + +extern CommProtocolType CommProtocol; + +extern CCFileClass RecordFile; +extern int RecordGame; +extern int SuperRecord; +extern int PlaybackGame; +extern int AllowAttract; + +extern GetCDClass CDList; + +/* +** Modem globals +*/ +extern bool ModemService; +extern NullModemClass NullModem; +extern DynamicVectorClass PhoneBook; +extern int CurPhoneIdx; +extern DynamicVectorClass InitStrings; +extern SerialSettingsType SerialDefaults; +extern ModemGameType ModemGameToPlay; +extern char * DialMethodCheck[ DIAL_METHODS ]; +extern char * CallWaitStrings[ CALL_WAIT_STRINGS_NUM ]; + +/* +** Network/Modem globals +*/ +extern int ScenarioIdx; +extern int ColorUsed[]; +extern char MPlayerName[MPLAYER_NAME_MAX]; +extern int MPlayerGColors[]; +extern int MPlayerTColors[]; +extern char MPlayerDescriptions[100][40]; +extern DynamicVectorClass MPlayerScenarios; +extern DynamicVectorClass MPlayerFilenum; +extern int MPlayerMax; +extern int MPlayerPrefColor; +extern int MPlayerColorIdx; +extern HousesType MPlayerHouse; +extern unsigned char MPlayerLocalID; +extern int MPlayerCount; +extern int MPlayerBases; +extern int MPlayerCredits; +extern int MPlayerTiberium; +extern int MPlayerGoodies; +extern int MPlayerGhosts; +extern int MPlayerSolo; +extern int MPlayerUnitCount; +extern int MPlayerCountMin[2]; +extern int MPlayerCountMax[2]; +extern unsigned long MPlayerMaxAhead; +extern unsigned long FrameSendRate; +extern unsigned char MPlayerID[MAX_PLAYERS]; +extern HousesType MPlayerHouses[MAX_PLAYERS]; +extern char MPlayerNames [MAX_PLAYERS][MPLAYER_NAME_MAX]; +extern MessageListClass Messages; +extern IPXAddressClass MessageAddress; +extern char LastMessage[MAX_MESSAGE_LENGTH]; +extern int MPlayerBlitz; +extern int MPlayerObiWan; +extern MPlayerScoreType MPlayerScore[MAX_MULTI_NAMES]; +extern int MPlayerGamesPlayed; +extern int MPlayerNumScores; +extern int MPlayerWinner; +extern int MPlayerCurGame; + +extern int TheirProcessTime[MAX_PLAYERS - 1]; +extern int DesiredFrameRate; + +extern char * GlobalPacketNames[]; +extern char * SerialPacketNames[]; + +typedef struct { + union { + AircraftClass *Aircraft; + AnimClass *Anim; + BuildingClass *Building; + BulletClass *Bullet; + InfantryClass *Infantry; + UnitClass *Unit; + void *All; + } Ptr; +} TrapObjectType; + +extern long TrapFrame; +extern RTTIType TrapObjType; +extern TrapObjectType TrapObject; +extern COORDINATE TrapCoord; +extern void * TrapThis; +extern CellClass * TrapCell; +extern int TrapCheckHeap; + +/* +** Network (IPX) globals +*/ +extern IPXManagerClass Ipx; +extern int IsBridge; +extern IPXAddressClass BridgeNet; +extern bool NetMaster; +extern bool NetStealth; +extern bool NetProtect; +extern bool NetOpen; +extern char MPlayerGameName[MPLAYER_NAME_MAX]; +extern GlobalPacketType GPacket; +extern int GPacketlen; +extern IPXAddressClass GAddress; +extern unsigned short GProductID; +extern char * MetaPacket; +extern int MetaSize; +extern DynamicVectorClass Games; +extern DynamicVectorClass Players; + +extern int Seed; +extern long * RandSeedPtr; +extern int CustomSeed; +extern int NewMaxAheadFrame1; +extern int NewMaxAheadFrame2; + +/* +** Constant externs (data is not modified during game play). +*/ +extern unsigned char const RemapGreen[256]; +extern unsigned char const RemapBlue[256]; +extern unsigned char const RemapOrange[256]; +extern unsigned char const RemapNone[256]; +extern unsigned char const RemapYellow[256]; +extern unsigned char const RemapRed[256]; +extern unsigned char const RemapBlueGreen[256]; +extern WeaponTypeClass const Weapons[WEAPON_COUNT]; +extern WarheadTypeClass const Warheads[WARHEAD_COUNT]; +extern char const * SourceName[SOURCE_COUNT]; +extern GroundType const Ground[LAND_COUNT]; +extern TheaterDataType const Theaters[THEATER_COUNT]; +extern unsigned char const Facing32[256]; +extern unsigned char const Facing8[256]; +extern unsigned char const Pixel2Lepton[24]; +extern COORDINATE const StoppingCoordAbs[5]; +extern CELL const AdjacentCell[FACING_COUNT]; +extern COORDINATE const AdjacentCoord[FACING_COUNT]; + +extern int SoundOn; +//extern GraphicBufferClass SeenPage; +extern GraphicBufferClass VisiblePage; +extern GraphicBufferClass HiddenPage; +extern GraphicViewPortClass SeenBuff; +extern GraphicBufferClass ModeXBuff; +extern GraphicViewPortClass HidPage; +extern GraphicBufferClass LoResHidPage; +extern GraphicBufferClass SysMemPage; +extern int MenuList[][8]; +extern CountDownTimerClass FrameTimer; +extern CountDownTimerClass CountDownTimer; + +extern TimerClass ProcessTimer; +extern int ProcessTicks; +extern int ProcessFrames; + +extern SpecialDialogType SpecialDialog; +//extern bool IsFindPath; + +extern char *DebugFname; // for stoopid debugging purposes +extern int DebugLine; // for stoopid debugging purposes +extern int RequiredCD; +extern int MouseInstalled; +extern int AreThingiesEnabled; + +extern WWKeyboardClass Kbd; +extern int In_Debugger; +extern WWMouseClass *WWMouse; +extern HANDLE hInstance; +extern int AllDone; +extern "C" bool MMXAvailable; +extern int Get_CD_Index (int cd_drive, int timeout); +void Memory_Error_Handler(void); +extern bool GameStatisticsPacketSent; +extern bool ConnectionLost; +extern bool InMainLoop; // True if in game state rather than menu state +void CCDebugString (char *string); +extern void *PacketLater; +void Load_Title_Screen(char *name, GraphicViewPortClass *video_page, unsigned char *palette); + +extern "C"{ + extern bool IsTheaterShape; +} + +extern void Reset_Theater_Shapes(void); +extern TheaterType LastTheater; + +#endif diff --git a/FACING.CPP b/FACING.CPP new file mode 100644 index 0000000..62401ae --- /dev/null +++ b/FACING.CPP @@ -0,0 +1,183 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\facing.cpv 1.9 16 Oct 1995 16:49:40 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FACING.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/21/95 * + * * + * Last Update : March 21, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FacingClass::Rotation_Adjust -- Perform a rotation adjustment to current facing. * + * FacingClass::Set_Current -- Sets the current rotation value. * + * FacingClass::Set_Desired -- Sets the desired facing value. * + * FacingClass::FacingClass -- Default constructor for the facing class. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "facing.h" + + +/*********************************************************************************************** + * FacingClass::FacingClass -- Default constructor for the facing class. * + * * + * This default constructor merely sets the desired and current facing values to be the * + * same (North). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/21/1995 JLB : Created. * + *=============================================================================================*/ +FacingClass::FacingClass(void) +{ + CurrentFacing = DIR_N; + DesiredFacing = DIR_N; +} + + +/*********************************************************************************************** + * FacingClass::Set_Desired -- Sets the desired facing value. * + * * + * This routine is used to set the desired facing value without altering the current * + * facing setting. Typicall use of this routine is when a vehicle needs to face a * + * direction, but currently isn't facing the correct direction. After this routine is * + * called, it is presumed that subsequent calls to Rotation_Adjust() will result in the * + * eventual alignment of the current facing. * + * * + * INPUT: facing -- The new facing to assign to the desired value. * + * * + * OUTPUT: bool; Did the desired facing value actually change by this routine call? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/21/1995 JLB : Created. * + *=============================================================================================*/ +int FacingClass::Set_Desired(DirType facing) +{ + if (DesiredFacing != facing) { + DesiredFacing = facing; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FacingClass::Set_Current -- Sets the current rotation value. * + * * + * This routine will set the current rotation value. It is used to override the facing * + * value without adjusting the desired setting. * + * * + * INPUT: facing -- The new facing to assign to the current facing value. * + * * + * OUTPUT: bool; Was the current setting changed by this routine. Failure means that the * + * current setting was already at the value specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/21/1995 JLB : Created. * + *=============================================================================================*/ +int FacingClass::Set_Current(DirType facing) +{ + if (CurrentFacing != facing) { + CurrentFacing = facing; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FacingClass::Rotation_Adjust -- Perform a rotation adjustment to current facing. * + * * + * This routine is used when the current and desired facings differ but the current * + * facing should be adjusted toward the desired facing. The amount of rotation to adjust * + * is provided as a rotation rate parameter. Typical use of this routine is for turrets * + * and other vehicle related rotating. * + * * + * INPUT: rate -- The rotation rate to use when adjusting the current facing toward the * + * desired facing. A value of 127 means instantaneous rotation. * + * * + * OUTPUT: bool; Did the rotation result in the current facing transitioning from one * + * 1/32 zone to another? If true, then the owning object most likely will * + * need to be redrawn to reflect the change. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/21/1995 JLB : Created. * + *=============================================================================================*/ +int FacingClass::Rotation_Adjust(int rate) +{ + /* + ** Only perform the rotation adjustment if the desired facing is not the + ** same as the current facing. + */ + if (Is_Rotating()) { + rate = MIN(rate, 127); + + DirType oldfacing = CurrentFacing; + int diff = Difference(); + + /* + ** If the allowed facing change is greater than the difference between + ** the current facing and the desired facing, then just snap the + ** facing to the new value. + */ + if (ABS(diff) < rate) { + CurrentFacing = DesiredFacing; + } else { + + /* + ** Adjust the current facing clockwise or counterclockwise depending + ** on the shortest distance to the desired facing from the current + ** facing. + */ + if (diff < 0) { + CurrentFacing = (DirType)(CurrentFacing - (DirType)rate); + } else { + CurrentFacing = (DirType)(CurrentFacing + (DirType)rate); + } + } + + /* + ** If this facing adjustment caused the current facing to rotate into a + ** new 1/32 rotation zone (likely to cause a redraw), then return + ** this fact with a true value. + */ + return(Facing_To_32(CurrentFacing) != Facing_To_32(oldfacing)); + } + return(false); +} diff --git a/FACING.H b/FACING.H new file mode 100644 index 0000000..d5cf79e --- /dev/null +++ b/FACING.H @@ -0,0 +1,79 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\facing.h_v 1.14 16 Oct 1995 16:46:02 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FACING.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/21/95 * + * * + * Last Update : March 21, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FACING_H +#define FACING_H + +/* +** This is a general facing handler class. It is used in those cases where facing needs to be +** kept track of, but there could also be an associated desired facing. The current facing +** is supposed to transition to the desired state over time. Using this class facilitates this +** processing as well as isolating the rest of the code from the internals. +*/ +class FacingClass +{ + public: + FacingClass(void); + FacingClass(DirType dir) {CurrentFacing = DesiredFacing = dir;}; + operator DirType(void) const {return CurrentFacing;}; + + DirType Current(void) const {return CurrentFacing;}; + DirType Desired(void) const {return DesiredFacing;}; + + int Set_Desired(DirType facing); + int Set_Current(DirType facing); + + void Set(DirType facing) { + Set_Current(facing); + Set_Desired(facing); + }; + + DirType Get(void) const { return CurrentFacing; } + + int Is_Rotating(void) const {return (DesiredFacing != CurrentFacing);}; + + int Difference(void) const {return (signed char)(*((unsigned char*)&DesiredFacing) - *((unsigned char*)&CurrentFacing));}; + int Difference(DirType facing) const {return (signed char)(*((signed char*)&facing) - *((signed char*)&CurrentFacing));}; + int Rotation_Adjust(int rate); + + private: + DirType CurrentFacing; + DirType DesiredFacing; +}; + + +#endif diff --git a/FACTORY.CPP b/FACTORY.CPP new file mode 100644 index 0000000..ce75b00 --- /dev/null +++ b/FACTORY.CPP @@ -0,0 +1,750 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\factory.cpv 2.18 16 Oct 1995 16:51:26 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FACTORY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/26/94 * + * * + * Last Update : May 22, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FactoryClass::AI -- Process factory production logic. * + * FactoryClass::Abandon -- Abandons current construction with money refunded. * + * FactoryClass::Completed -- Clears factory object after a completed production process. * + * FactoryClass::Completion -- Fetchs the completion step for this factory. * + * FactoryClass::Cost_Per_Tick -- Breaks entire production cost into managable chunks. * + * FactoryClass::FactoryClass -- Default constructor for factory objects. * + * FactoryClass::Get_Object -- Fetches pointer to object being constructed. * + * FactoryClass::Get_Special_Item -- gets factorys spc prod item * + * FactoryClass::Has_Changed -- Checks to see if a production step has occurred? * + * FactoryClass::Has_Completed -- Checks to see if object has completed production. * + * FactoryClass::Set -- Assigns a factory to produce an object. * + * FactoryClass::Set -- Fills a factory with an already completed object. * + * FactoryClass::Set -- Force factory to "produce" special object. * + * FactoryClass::Start -- Resumes production after suspension or creation. * + * FactoryClass::Suspend -- Temporarily stop production. * + * FactoryClass::operator delete -- Returns a factory to the free factory pool. * + * FactoryClass::operator new -- Allocates a factory object from the free factory pool. * + * FactoryClass::~FactoryClass -- Default destructor for factory objects. * + * FactoryClass::Validate -- validates factory pointer * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * FactoryClass::Validate -- validates factory pointer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int FactoryClass::Validate(void) const +{ + int num; + + num = Factories.ID(this); + if (num < 0 || num >= FACTORY_MAX) { + Validate_Error("FACTORY"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * FactoryClass::FactoryClass -- Default constructor for factory objects. * + * * + * This brings the factory into a null state. It is called when a factory object is * + * created. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +FactoryClass::FactoryClass(void) +{ + IsSuspended = false; + IsDifferent = false; + Balance = 0; + SpecialItem = SPC_NONE; + Object = NULL; + House = NULL; + Set_Rate(0); + Set_Stage(0); +} + + +/*********************************************************************************************** + * FactoryClass::~FactoryClass -- Default destructor for factory objects. * + * * + * This cleans up a factory object in preparation for deletion. If there is currently * + * an object in production, it is abandoned and money is refunded. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +FactoryClass::~FactoryClass(void) +{ + if (GameActive) { + Abandon(); + } +} + + +/*********************************************************************************************** + * FactoryClass::Init -- Clears all units for scenario preparation. * + * * + * This routine will zero out the factory list and objects. This routine is typically * + * used in preparation for a new scenario load. All factorys are guaranteed to be eliminated* + * by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/15/1994 JLB : Created. * + *=============================================================================================*/ +void FactoryClass::Init(void) +{ + Factories.Free_All(); +} + + +/*********************************************************************************************** + * FactoryClass::operator new -- Allocates a factory object from the free factory pool. * + * * + * This routine allocates a factory from the free factory pool. If there is no more room * + * to allocate a factory, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with pointer to the newly allocated factory object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +void * FactoryClass::operator new(size_t) +{ + void * ptr = Factories.Allocate(); + if (ptr) { + ((FactoryClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * FactoryClass::operator delete -- Returns a factory to the free factory pool. * + * * + * This returns the factory object back to the factory allocation pool. The factory is then * + * available to be allocated. * + * * + * INPUT: ptr -- Pointer to the factory object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +void FactoryClass::operator delete(void *ptr) +{ + if (ptr) { + ((FactoryClass *)ptr)->IsActive = false; + } + Factories.Free((FactoryClass *)ptr); +} + + +/*********************************************************************************************** + * FactoryClass::AI -- Process factory production logic. * + * * + * This routine should be called once per game tick. It handles the production process. * + * As production proceeds, money is deducted from the owner object's house. When production * + * completes, the factory stop processing. A call to Abandon, Delete, or Completed is * + * required after that point. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + * 01/04/1995 JLB : Uses exact installment payment method. * + *=============================================================================================*/ +void FactoryClass::AI(void) +{ + Validate(); + if (!IsSuspended && (Object != NULL || SpecialItem)) { + int stages = 1; + + /* + ** Determine the acceleration factor for factory production. + ** This applies only to human players. The computer builds + ** units on a building by building basis -- quantity of building + ** factory types doesn't affect individual factories. + */ + if (Object && House->IsHuman) { + switch (Object->What_Am_I()) { + case RTTI_AIRCRAFT: + stages = House->AircraftFactories; + break; + + case RTTI_INFANTRY: + stages = House->InfantryFactories; + break; + + case RTTI_UNIT: + stages = House->UnitFactories; + break; + + case RTTI_BUILDING: + stages = House->BuildingFactories; + break; + } + stages = MAX(stages, 1); + } + + + for (int index = 0; index < stages; index++) { + if (!Has_Completed() && Graphic_Logic()) { + IsDifferent = true; + + int cost = Cost_Per_Tick(); + + cost = MIN(cost, Balance); + + /* + ** Enough time has expired so that another production step can occur. + ** If there is insufficient funds, then go back one production step and + ** continue the countdown. The idea being that by the time the next + ** production step occurs, there may be sufficient funds available. + */ + if (cost > House->Available_Money()) { + Set_Stage(Fetch_Stage()-1); + } else { + House->Spend_Money(cost); + Balance -= cost; + } + if ( Debug_Instant_Build ) { + Set_Stage(STEP_COUNT); + } + /* + ** If the production has completed, then suspend further production. + */ + if (Fetch_Stage() == STEP_COUNT) { + IsSuspended = true; + Set_Rate(0); + House->Spend_Money(Balance); + Balance = 0; + } + } + } + } +} + + +/*********************************************************************************************** + * FactoryClass::Has_Changed -- Checks to see if a production step has occurred? * + * * + * Use this routine to determine if production has advanced at least one step. By using * + * this function, intelligent rendering may be performed. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Has the production process advanced one step since the last time this * + * function was called? * + * * + * WARNINGS: This function clears the changed status flag as a side effect. * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Has_Changed(void) +{ + Validate(); + bool changed = IsDifferent; + IsDifferent = false; + return(changed); +} + + +/*********************************************************************************************** + * FactoryClass::Set -- Assigns a factory to produce an object. * + * * + * This routine initializes a factory to produce the object specified. The desired object * + * type is created and placed in suspended animation (limbo) until such time as production * + * completes. Production is not actually started by this routine. An explicit call to * + * Start() is required to begin production. * + * * + * INPUT: object -- Reference to the object type class that is to be produced. * + * * + * house -- Reference to the owner of the object to be produced. * + * * + * OUTPUT: bool; Was production successfully prepared for this factory object. Failure means * + * that the object could not be created. This is catastrophic and in such * + * cases, the factory object should be deleted. * + * * + * WARNINGS: Be sure to examine the return value from this function. Failure to initialize * + * the factory means that the factory is useless and should be deleted. * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Set(TechnoTypeClass const & object, HouseClass & house) +{ + Validate(); + /* + ** If there is any production currently in progress, abandon it. + */ + Abandon(); + + /* + ** Set up the factory for the new production process. + */ + IsDifferent = true; + IsSuspended = true; + Set_Rate(0); + Set_Stage(0); + + /* + ** Create an object of the type requested. + */ + Object = (TechnoClass *)object.Create_One_Of(&house); + + if (Object) { + House = Object->House; + Balance = object.Cost_Of(); + Object->PurchasePrice = Balance; + } + + /* + ** If all was set up successfully, then return true. + */ + return(Object != NULL); +} + + +/*********************************************************************************************** + * FactoryClass::Set -- Force factory to "produce" special object. * + * * + * Use this routine to force the factory into special production mode. Such production is * + * used for the ion cannon and other timed special weapon events. * + * * + * INPUT: type -- The special weapon type to begin "production" of. * + * * + * house -- The owner of this production object. * + * * + * OUTPUT: Was the assignment successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Set(int const & type, HouseClass & house) +{ + Validate(); + /* + ** If there is any production currently in progress, abandon it. + */ + Abandon(); + + /* + ** Set up the factory for the new production process. + */ + IsDifferent = true; + IsSuspended = true; + Set_Rate(0); + Set_Stage(0); + + /* + ** Create an object of the type requested. + */ + SpecialItem = type; + House = &house; + Balance = 0; + + /* + ** If all was set up successfully, then return true. + */ + return(SpecialItem != SPC_NONE); +} + + +/*********************************************************************************************** + * FactoryClass::Set -- Fills a factory with an already completed object. * + * * + * This routine is called when a produced object is in placement mode but then placement * + * is suspended. The object must then return to the factory as if it were newly completed * + * and awaiting removal. * + * * + * INPUT: object -- The object to return to the factory. * + * * + * OUTPUT: none * + * * + * WARNINGS: This will abandon any current object being produced at the factory in order * + * to set the new object into it. * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +void FactoryClass::Set(TechnoClass & object) +{ + Validate(); + Abandon(); + Object = &object; + House = Object->House; + Balance = 0; + Set_Rate(0); + Set_Stage(STEP_COUNT); + IsDifferent = true; + IsSuspended = true; +} + + +/*********************************************************************************************** + * FactoryClass::Suspend -- Temporarily stop production. * + * * + * This routine will suspend production until a subsiquent call to Start() or Abandon(). * + * Typical use of this function is when the player puts production on hold or when there * + * is insufficient funds. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was production actually stopped? A false return value indicates that the * + * factory was empty or production was already stopped (or never started). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Suspend(void) +{ + Validate(); + if (!IsSuspended) { + IsSuspended = true; + Set_Rate(0); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FactoryClass::Start -- Resumes production after suspension or creation. * + * * + * This function will start the production process. It works for newly created factory * + * objects, as well as if production had been suspended previously. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was production started? A false return value means that the factory is * + * empty or there is unsufficient credits to begin production. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Start(void) +{ + Validate(); + if ((Object || SpecialItem) && IsSuspended && !Has_Completed()) { + if (House->Available_Money() >= Cost_Per_Tick()) { + int time; + + if (Object) { + time = Object->Class_Of().Time_To_Build(House->Class->House); + } else { + time = TICKS_PER_MINUTE * 5; + } + + int frac = House->Power_Fraction(); + frac = Bound(frac, 0x0010, 0x0100); + int rate = (time*256) / frac; + + rate /= STEP_COUNT; + rate = Bound(rate, 1, 255); + + Set_Rate(rate); + IsSuspended = false; + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * FactoryClass::Abandon -- Abandons current construction with money refunded. * + * * + * This routine is used when construction is to be abandoned and current money spend is * + * to be refunded. This function effectively clears out this factory of all record of the * + * producing object so that it may either be deleted or started anew with the Set() * + * function. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was an object actually abandoned? A false return value indicates that the * + * factory was not producing any object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Abandon(void) +{ + Validate(); + if (Object) { + + if (Object) { + /* + ** Refund all money expended so far, back to the owner of the object under construction. + */ + House->Refund_Money(Object->Class_Of().Cost_Of() - Balance); + Balance = 0; + + /* + ** Delete the object under construction. + */ + ScenarioInit++; + delete Object; + Object = NULL; + ScenarioInit--; + } + if (SpecialItem) { + SpecialItem = SPC_NONE; + } + + /* + ** Set the factory back to the idle and empty state. + */ + Set_Rate(0); + Set_Stage(0); + IsSuspended = true; + IsDifferent = true; + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FactoryClass::Completion -- Fetchs the completion step for this factory. * + * * + * Use this routine to determine what animation (or completion step) the factory is * + * currently on. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a completion step number beteen 0 (uncompleted), to STEP_COUNT (completed) * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +int FactoryClass::Completion(void) +{ + Validate(); + return(Fetch_Stage()); +} + + +/*********************************************************************************************** + * FactoryClass::Has_Completed -- Checks to see if object has completed production. * + * * + * Use this routine to examine the factory object in order to determine if the associated * + * object has completed production and is awaiting placement. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the associated object to the factory completed and ready for placement? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Has_Completed(void) +{ + Validate(); + if (Object && Fetch_Stage() == STEP_COUNT) { + return(true); + } + if (SpecialItem && Fetch_Stage() == STEP_COUNT) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FactoryClass::Get_Object -- Fetches pointer to object being constructed. * + * * + * This routine gets the pointer to the currently constructing object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the object undergoing construction. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +TechnoClass * FactoryClass::Get_Object(void) const +{ + Validate(); + return(Object); +} + + +/*************************************************************************** + * FactoryClass::Get_Special_Item -- gets factorys spc prod item * + * * + * INPUT: none * + * * + * OUTPUT: int the item the factory is currently working on * + * * + * HISTORY: * + * 05/05/1995 PWG : Created. * + *=========================================================================*/ +int FactoryClass::Get_Special_Item(void) const +{ + Validate(); + return(SpecialItem); +} + + +/*********************************************************************************************** + * FactoryClass::Cost_Per_Tick -- Breaks entire production cost into managable chunks. * + * * + * Use this routine to determine the cost per game "tick" to produce the object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of credits necessary to advance production one game tick. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +int FactoryClass::Cost_Per_Tick(void) +{ + Validate(); + if (Object) { + int steps = STEP_COUNT - Fetch_Stage(); + if (steps) { + return(Balance / steps); + } + return(Balance); + } + return(0); +} + + +/*********************************************************************************************** + * FactoryClass::Completed -- Clears factory object after a completed production process. * + * * + * This routine is called after production completes, and the object produced has been * + * placed into the game. It resets the factory for deletion or starting of new production. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Did any resetting occur? Failure is the result of the factory not having * + * any completed object. An immediate second call to this routine will also * + * yield false. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Completed(void) +{ + Validate(); + if (Object && Fetch_Stage() == STEP_COUNT) { + Object = NULL; + IsSuspended = true; + IsDifferent = true; + Set_Stage(0); + Set_Rate(0); + return(true); + } + + if (SpecialItem && Fetch_Stage() == STEP_COUNT) { + SpecialItem = SPC_NONE; + IsSuspended = true; + IsDifferent = true; + Set_Stage(0); + Set_Rate(0); + return(true); + } + return(false); +} + + diff --git a/FACTORY.H b/FACTORY.H new file mode 100644 index 0000000..9c01035 --- /dev/null +++ b/FACTORY.H @@ -0,0 +1,144 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\factory.h_v 2.17 16 Oct 1995 16:45:44 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FACTORY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/26/94 * + * * + * Last Update : December 26, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FACTORY_H +#define FACTORY_H + +#include "stage.h" + +class FactoryClass : private StageClass +{ + public: + FactoryClass(void); + ~FactoryClass(void); + static void * operator new(size_t size); + static void operator delete(void *ptr); + + static void Init(void); + + /* + ** File I/O. + */ + bool Load(FileClass & file); + bool Save(FileClass & file); + void Code_Pointers(void); + void Decode_Pointers(void); + + bool Abandon(void); + bool Completed(void); + bool Has_Changed(void); + bool Has_Completed(void); + bool Is_Building(void) const {return(Fetch_Rate() != 0);}; + bool Set(TechnoTypeClass const & object, HouseClass & house); + bool Set(int const & type, HouseClass & house); + bool Start(void); + bool Suspend(void); + int Completion(void); + TechnoClass * Get_Object(void) const; + int Get_Special_Item(void) const; + void AI(void); + void Set(TechnoClass & object); + HouseClass * Get_House(void) {return(House);}; + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + /* + ** This flag is used to maintain the pool of factory class objects. If the object has + ** been allocated, then this flag is true. Otherwise, the object is free to be + ** allocated. + */ + unsigned IsActive:1; + + protected: + enum StepCountEnum { + STEP_COUNT=108 // Number of steps to break production down into. + }; + + int Cost_Per_Tick(void); + + private: + + /* + ** If production is temporarily suspended, then this flag will be true. A factory + ** is suspended when it is first created, when production has completed, and when + ** explicitly instructed to Suspend() production. Suspended production is not + ** abandoned. It may be resumed with a call to Start(). + */ + unsigned IsSuspended:1; + + /* + ** If the AI process detected that the production process has advanced far enough + ** that a change in the building animation would occur, this flag will be true. + ** Examination of this flag (through the Has_Chaged function) allows intelligent + ** updating of any production graphic. + */ + unsigned IsDifferent:1; + + /* + ** This records the balance due on the current production item. This value will + ** be reduced as production proceeds. It will reach zero the moment production has + ** finished. Using this method ensures that the total production cost will be EXACT + ** regardless of the number of installment payments that are made. + */ + int Balance; + int OriginalBalance; + + /* + ** This is the object that is being produced. It is held in a state of limbo while + ** undergoing production. Since the object is created at the time production is + ** started, it is always available when production completes. + */ + TechnoClass * Object; + + /* + ** If the factory is not producing an object and is instead producing + ** a special item, then special item will be set. + */ + int SpecialItem; + + /* + ** The factory has to be doing production for one house or another. + ** The house pointer will point to whichever house it is being done + ** for. + */ + HouseClass * House; +}; + +#endif diff --git a/FIELD.CPP b/FIELD.CPP new file mode 100644 index 0000000..ad77fba --- /dev/null +++ b/FIELD.CPP @@ -0,0 +1,214 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*************************************************************************** + * * + * Project Name : Westwood Auto Registration App * + * * + * File Name : FIELD.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 04/22/96 * + * * + * Last Update : April 22, 1996 [PWG] * + * * + * Actual member function for the field class. * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include +#include "field.h" + +FieldClass::FieldClass(char *id, char data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_CHAR; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, unsigned char data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_UNSIGNED_CHAR; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, short data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_SHORT; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, unsigned short data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_UNSIGNED_SHORT; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, long data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_LONG; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, unsigned long data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_UNSIGNED_LONG; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, char *data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_STRING; + Size = (unsigned short)(strlen(data)+1); + Data = new char[Size]; + memcpy(Data, data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, void *data, int length) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_CHUNK; + Size = (unsigned short)length; + Data = new char[Size]; + memcpy(Data, data, Size); + Next = NULL; +} + + +/************************************************************************** + * PACKETCLASS::HOST_TO_NET_FIELD -- Converts host field to net format * + * * + * INPUT: FIELD * to the data field we need to convert * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 04/22/1996 PWG : Created. * + *========================================================================*/ +void FieldClass::Host_To_Net(void) +{ + // + // Before we convert the data type, we should convert the actual data + // sent. + // + switch (DataType) { + case TYPE_CHAR: + case TYPE_UNSIGNED_CHAR: + case TYPE_STRING: + case TYPE_CHUNK: + break; + + case TYPE_SHORT: + case TYPE_UNSIGNED_SHORT: + *((unsigned short *)Data) = htons(*((unsigned short *)Data)); + break; + + case TYPE_LONG: + case TYPE_UNSIGNED_LONG: + *((unsigned long *)Data) = htonl(*((unsigned long *)Data)); + break; + + // + // Might be good to insert some type of error message here for unknown + // datatypes -- but will leave that for later. + // + default: + break; + } + // + // Finally convert over the data type and the size of the packet. + // + DataType = htons(DataType); + Size = htons(Size); +} +/************************************************************************** + * PACKETCLASS::NET_TO_HOST_FIELD -- Converts net field to host format * + * * + * INPUT: FIELD * to the data field we need to convert * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 04/22/1996 PWG : Created. * + *========================================================================*/ +void FieldClass::Net_To_Host(void) +{ + // + // Convert the variables to host order. This needs to be converted so + // the switch statement does compares on the data that follows. + // + Size = ntohs(Size); + + DataType = ntohs(DataType); + + // + // Before we convert the data type, we should convert the actual data + // sent. + // + switch (DataType) { + case TYPE_CHAR: + case TYPE_UNSIGNED_CHAR: + case TYPE_STRING: + case TYPE_CHUNK: + break; + + case TYPE_SHORT: + case TYPE_UNSIGNED_SHORT: + *((unsigned short *)Data) = ntohs(*((unsigned short *)Data)); + break; + + case TYPE_LONG: + case TYPE_UNSIGNED_LONG: + *((unsigned long *)Data) = ntohl(*((unsigned long *)Data)); + break; + + // + // Might be good to insert some type of error message here for unknown + // datatypes -- but will leave that for later. + // + default: + break; + } +} + diff --git a/FIELD.H b/FIELD.H new file mode 100644 index 0000000..9335437 --- /dev/null +++ b/FIELD.H @@ -0,0 +1,85 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*************************************************************************** + * * + * Project Name : Westwood Auto Registration App * + * * + * File Name : FIELD.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 04/22/96 * + * * + * Last Update : April 22, 1996 [PWG] * + * * + * This module takes care of maintaining the field list used to process * + * packets. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef __FIELD_H +#define __FIELD_H + +#include +#include + +#define FIELD_HEADER_SIZE (sizeof(FieldClass) - (sizeof(void *) * 2)) + +#define TYPE_CHAR 1 +#define TYPE_UNSIGNED_CHAR 2 +#define TYPE_SHORT 3 +#define TYPE_UNSIGNED_SHORT 4 +#define TYPE_LONG 5 +#define TYPE_UNSIGNED_LONG 6 +#define TYPE_STRING 7 +#define TYPE_CHUNK 20 + +class PacketClass; + +class FieldClass { + + public: + friend class PacketClass; + // + // Define constructors to be able to create all the different kinds + // of fields. + // + FieldClass(void) {}; + FieldClass(char *id, char data); + FieldClass(char *id, unsigned char data); + FieldClass(char *id, short data); + FieldClass(char *id, unsigned short data); + FieldClass(char *id, long data); + FieldClass(char *id, unsigned long data); + FieldClass(char *id, char *data); + FieldClass(char *id, void *data, int length); + + void Host_To_Net(void); + void Net_To_Host(void); + + private: + char ID[4]; // id value of this field + unsigned short DataType; // id of the data type we are using + unsigned short Size; // size of the data portion of this field + void *Data; // pointer to the data portion of this field + FieldClass *Next; // pointer to the next field in the field list +}; + +#endif diff --git a/FINDPATH.CPP b/FINDPATH.CPP new file mode 100644 index 0000000..ec7a10c --- /dev/null +++ b/FINDPATH.CPP @@ -0,0 +1,1546 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\findpath.cpv 2.17 16 Oct 1995 16:51:04 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FINDPATH.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : May 25, 1995 [PWG] * + * * + * The path algorithm works by following a LOS path to the target. If it * + * collides with an impassable spot, it uses an Edge following routine to * + * get around it. The edge follower moves along the edge in a clockwise or * + * counter clockwise fashion until finding the destination spot. The * + * destination is determined by Find_Path. It is the first passable that * + * can be reached (so it will handle the doughnut case, where there is * + * a passable in the center of an unreachable area). * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Clear_Path_Overlap -- clears the path overlap list * + * Find_Path -- Find a path from point a to point b. * + * Find_Path_Cell -- Finds a given cell on a specified path * + * Follow_Edge -- Follow an edge to get around an impassable spot. * + * FootClass::Unravel_Loop -- Unravels a loop in the movement path * + * Get_New_XY -- Get the new x,y based on current position and direction. * + * Optimize_Moves -- Optimize the move list. * + * Set_Path_Overlap -- Sets the overlap bit for given cell * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +//#include + +/* +** When an edge search is started, it can be performed CLOCKwise or +** COUNTERCLOCKwise direction. +*/ +#define CLOCK (FacingType)1 // Clockwise. +#define COUNTERCLOCK (FacingType)-1 // Counterclockwise. + +/* +** If defined, diagonal moves are allowed, else no diagonals. +*/ +#define DIAGONAL + +/* +** This is the marker to signify the end of the path list. +*/ +#define END FACING_NONE + +/* +** If memory is more important than speed, set this define to +** true. It will then perform intermediate optimizations to get the most +** milage out of a limited movement list staging area. If this value +** is true then it figures paths a bit more intelligently. +*/ +#define SAVEMEM true + +/* +** Modify this macro so that given two cell values, it will return +** a value between 0 and 7, with 0 being North and moving +** clockwise (just like map degrees). +*/ +#define CELL_FACING(a,b) Dir_Facing(::Direction((a),(b))) + + +/*-------------------------------------------------------------------------*/ +/* +** Cells values are really indexes into the 'map'. The following value is +** the X width of the map. +*/ +#define MODULO MAP_CELL_W + +/* +** Maximum lookahead cells. Twice this value in bytes will be +** reserved on the stack. The smaller this number, the faster the processing. +*/ +#define MAX_MLIST_SIZE 300 +#define THREAT_THRESHOLD 5 + +#ifdef NEVER +typedef enum { + FACING_N, // North + FACING_NE, // North-East + FACING_E, // East + FACING_SE, // South-East + FACING_S, // South + FACING_SW, // South-West + FACING_W, // West + FACING_NW, // North-West + + FACING_COUNT // Total of 8 directions (0..7). +} FacingType; +#endif + + +/*-------------------------------------------------------------------------*/ +static bool DrawPath; + +inline FacingType Opposite(FacingType face) +{ + return( (FacingType) (face ^ 4)); +} + +static inline void Draw_Cell_Point(CELL cell, bool passable, int threat_stage, int overide = 0) +{ + if (DrawPath) { + if (!Debug_Find_Path) { + int x, y; + + if (Map.Coord_To_Pixel(Cell_Coord(cell), x, y)) { + if (threat_stage>2) { + SeenBuff.Put_Pixel(x, y, (passable) ? LTGREEN : RED); + } else { + SeenBuff.Put_Pixel(x, y, (passable) ? 9+threat_stage : RED); + } + } + } else { + int x = cell & 63; + int y = cell / 64; + if (!overide) { + SeenBuff.Put_Pixel(64 + (x * 3) + 1, 8 + (y * 3) + 1, (passable) ? WHITE : BLACK); + } else { + SeenBuff.Put_Pixel(64 + (x * 3) + 1, 8 + (y * 3) + 1, overide); + } + } + } +} + +inline static FacingType Next_Direction(FacingType facing, FacingType dir) +{ + facing = facing + dir; + #ifndef DIAGONAL + facing = (FacingType)(facing & 0x06); + #endif + return(facing); +} + +/*=========================================================================*/ +/* Define a couple of variables which are private to the module they are */ +/* declared in. */ +/*=========================================================================*/ +static unsigned long MainOverlap[MAP_CELL_TOTAL/32]; // overlap list for the main path +static unsigned long LeftOverlap[MAP_CELL_TOTAL/32]; // overlap list for the left path +static unsigned long RightOverlap[MAP_CELL_TOTAL/32]; // overlap list for the right path + + +//static CELL MoveMask = 0; +static CELL DestLocation; +static CELL StartLocation; + +/*************************************************************************** + * Point_Relative_To_Line -- Relation between a point and a line * + * * + * If a point is on a line then the following function holds true: * + * (x - x2)(z1 - z2) = (z - z2)(x1 - x2) given x,z a point on the * + * line (x1,z1),(x2,z2). * + * If the right side is > then the left side then the point is on one * + * side of the line and if the right side is < the the left side, then* + * the point is on the other side of the line. By subtracting one side* + * from the other we can determine on what side (if any) the point is on* + * by testing the side of the resulting subtraction. * + * * + * INPUT: * + * int x - x pos of point. * + * int z - z pos of point. * + * int x1 - x pos of first end of line segment. * + * int z1 - z pos of first end of line segment. * + * int x1 - x pos of second end of line segment. * + * int z1 - z pos of second end of line segment. * + * * + * OUTPUT: * + * Assuming (x1,z1) is north, (x2,z2) is south: * + * 0 : point is on line. * + * > 0 : point is east of line. * + * < 0 : point is west of line. * + * * + * WARNINGS: * + * Remember that int means that is assumes 16 bits of persision. * + * * + * HISTORY: * + * 10/28/1994 SKB : Created. * + *=========================================================================*/ +int Point_Relative_To_Line(int x, int z, int x1, int z1, int x2, int z2) +{ + return((((long)x - (long)x2) * ((long)z1 - (long)z2)) - (((long)z - (long)z2) * ((long)x1 - (long)x2))); +} + + +/*************************************************************************** + * FootClass::Unravel_Loop -- Unravels a loop in the movement path * + * * + * While in the midst of the Follow Edge logic, it is possible (due to the * + * fact that we support diagonal movement) to begin looping around a * + * column of some type. The Unravel loop function will scan backward * + * through the list and fixup the path to try to prevent the loop. * + * * + * INPUT: path - pointer to the generated path so we can pull the * + * commands out of it. * + * cell - the cell we tried to enter that generated the * + * double overlap condition. * + * dir - the direction we tried to enter from when we * + * generated the double overlap condition * + * startx - the start x position of this path segment * + * starty - the start y position of this path segment * + * destx - the dest x position for this path segment * + * desty - the dest y position for this path segment * + * * + * OUTPUT: TRUE - loop has been sucessfully unravelled * + * FALSE - loop can not be unravelled so abort follow edge * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/25/1995 PWG : Created. * + *=========================================================================*/ +bool FootClass::Unravel_Loop(PathType *path, CELL &cell, FacingType &dir, int sx, int sy, int dx, int dy, MoveType threshhold) +{ + /* + ** Walk back to the actual cell before we advanced our position + */ + FacingType curr_dir = dir; + CELL curr_pos = Adjacent_Cell(cell, Opposite(curr_dir)); + int idx = path->Length; // start at the last position + FacingType *list = &path->Command[idx-1]; // point to the last command + int checkx; + int checky; + int last_was_line = false; + + /* + ** loop backward through the list searching for a point that is + ** on the line. If the point was a diagonal move then adjust + ** it. + */ + while (idx) { + checkx = Cell_X(curr_pos); + checky = Cell_Y(curr_pos); + + if (!Point_Relative_To_Line(checkx, checky, sx, sy, dx, dy) || last_was_line) { + + /* + ** We have now found a point on the line. Now we must check to see + ** if we left the line on a diagonal. If we did then we need to fix + ** it up. + */ + if (curr_dir & 1 && curr_pos != path->LastFixup) { + cell = curr_pos; + dir = *(list-1); + path->Length = idx; + path->LastFixup = curr_pos; + Draw_Cell_Point(curr_pos, true, -1, CYAN); + return(true); + } + + last_was_line = !last_was_line; + } + + /* + ** Since this cell will not be in the list, then pull out its cost + */ + path->Cost -= Passable_Cell(curr_pos, *list, -1, threshhold); + + /* + ** Remove this cells flag from the overlap list for the path + */ + path->Overlap[curr_pos >> 5] &= ~(1 << ((curr_pos & 31) - 1)); + + /* + ** Mark cell on the map + */ + Draw_Cell_Point(curr_pos, true, -1, LTCYAN); + + /* + ** Adjust to the next list position and direction. + */ + curr_dir = *list--; + curr_pos = Adjacent_Cell(curr_pos, Opposite(curr_dir)); + idx--; + } + + /* + ** If we can't modify the list to eliminate the problem, then we have + ** a larger problem in that we have deleted all of the cells in the + ** list. + */ + return(false); +} + + +/*************************************************************************** + * Register_Cell -- registers a cell on our path and check for backtrack * + * * + * This function adds a new cell to our path. If the cell has already * + * been recorded as part of our path, then this function moves back down * + * the list truncating it at the point we registered that cell. This * + * function will elliminate all backtracking from the list. * + * * + * INPUT: long * list - the list to set the overlap bit for * + * CELL cell - the cell to mark on the overlap list * + * * + * OUTPUT: BOOL - TRUE if bit has been set, FALSE if bit already set * + * * + * HISTORY: * + * 05/23/1995 PWG : Created. * + *=========================================================================*/ +bool FootClass::Register_Cell(PathType *path, CELL cell, FacingType dir, int cost, MoveType threshhold) +{ + FacingType *list; + int pos = cell >> 5; + int bit = (cell & 31) - 1; + + /* + ** See if this point has already been registered as on the list. If so + ** we need to truncate the list back to this point and register the + ** new direction. + */ + if (path->Overlap[pos] & (1 << bit)) { + /* + ** If this is not a case of immediate back tracking then handle + ** by searching the list to see what we find. However is this is + ** an immediate back track, then pop of the last direction + ** and unflag the cell we are in (not the cell we are moving to). + ** Note: That we do not check for a zero length cell because we + ** could not have a duplicate unless there are cells in the list. + */ + + if (path->Command[path->Length - 1] == Opposite(dir)) { + CELL pos = Adjacent_Cell(cell, Opposite(dir)); + path->Overlap[pos >> 5] &= ~(1 << ((pos & 31) - 1)); + path->Length--; + Draw_Cell_Point(pos, true, -1, BLUE); + } else { + /* + ** If this overlap is in the same place as we had our last overlap + ** then we are in a loop condition. We need to signify that we + ** cannot register this cell. + */ + if (path->LastOverlap == cell) { + return(false); + } else { + path->LastOverlap = cell; + } + + CELL pos = path->Start; + int newlen = 0; + int idx = 0; + list = path->Command; + + /* + ** Note that the cell has to be in this list, so theres no sense + ** in checking whether we found it (famous last words). + ** + ** PWG 8/16/95 - However there is no sense searching the list if + ** the cell we have overlapped on is the cell we + ** started in. + */ + + if (pos != cell) { + while (idx < path->Length) { + pos = Adjacent_Cell(pos, *list); + if (pos == cell) { + idx++; + list++; + break; + } + idx++; + list++; + } + newlen = idx; + } + + /* + ** Now we are pointing at the next command in the list. From here on + ** out we need to unmark the fact that we have entered these cells and + ** adjust the cost of our path to reflect that we have not entered + ** then. + */ + while (idx < path->Length) { + pos = Adjacent_Cell(pos, *list); + path->Cost -= Passable_Cell(pos, *list, -1, threshhold); + path->Overlap[pos >> 5] &= ~(1 << ((pos & 31) - 1)); + Draw_Cell_Point(pos, true, -1, LTBLUE); + idx++; + list++; + } + path->Length = newlen; + } + } else { + /* + ** Now we need to register the new direction, updating the cell structure + ** and the cost. + */ + int cpos = path->Length++; + path->Command[cpos] = dir; // save of the direction we moved + path->Cost += cost; // figure new cost for cell + path->Overlap[pos] |= (1 << bit); // mark the we have entered point + } + return(true); +} +#ifdef OBSOLETE +bool FootClass::Register_Cell(PathType *path, CELL cell, FacingType dir, int cost, MoveType threshhold) +{ + FacingType *list; + int pos = cell >> 5; + int bit = (cell & 31) - 1; + int idx; + + /* + ** See if this point has already been registered as on the list. If so + ** we need to truncate the list back to this point and register the + ** new direction. + */ + if (path->Overlap[pos] & (1 << bit)) { + /* + ** If this is not a case of immediate back tracking then handle + ** by searching the list to see what we find. However is this is + ** an immediate back track, then pop of the last direction + ** and unflag the cell we are in (not the cell we are moving to). + ** Note: That we do not check for a zero length cell because we + ** could not have a duplicate unless there are cells in the list. + */ + + if (path->Command[path->Length - 1] == Opposite(dir)) { + CELL pos = Adjacent_Cell(cell, Opposite(dir)); + path->Overlap[pos >> 5] &= ~(1 << ((pos & 31) - 1)); + path->Length--; + Draw_Cell_Point(pos, true, -1, BLUE); + } else { + /* + ** If this overlap is in the same place as we had our last overlap + ** then we are in a loop condition. We need to signify that we + ** cannot register this cell. + */ + if (path->LastOverlap == cell) { + return(false); + } else { + path->LastOverlap = cell; + } + + CELL pos = path->Start; + int newlen = 0; + + /* + ** Note that the cell has to be in this list, so theres no sense + ** in checking whether we found it (famous last words) + */ + for (idx = 0, list = path->Command; idx < path->Length; idx++, list++) { + pos = Adjacent_Cell(pos, *list); + if (pos == cell) { + idx++; + list++; + break; + } + } + newlen = idx; + + /* + ** Now we are pointing at the next command in the list. From here on + ** out we need to unmark the fact that we have entered these cells and + ** adjust the cost of our path to reflect that we have not entered + ** then. + */ + while (idx < path->Length) { + pos = Adjacent_Cell(pos, *list); + path->Cost -= Passable_Cell(pos, *list, -1, threshhold); + path->Overlap[pos >> 5] &= ~(1 << ((pos & 31) - 1)); + Draw_Cell_Point(pos, true, -1, LTBLUE); + idx++; + list++; + } + path->Length = newlen; + } + } else { + /* + ** Now we need to register the new direction, updating the cell structure + ** and the cost. + */ + int cpos = path->Length++; + path->Command[cpos] = dir; // save of the direction we moved + path->Cost += cost; // figure new cost for cell + path->Overlap[pos] |= (1 << bit); // mark the we have entered point + } + return(true); +} +#endif + + +/*********************************************************************************************** + * Find_Path -- Find a path from point a to point b. * + * * + * INPUT: int source x,y, int destination x,y, char *final moves * + * array to store moves, int maximum moves we may attempt * + * * + * OUTPUT: int number of moves it took (IMPOSSIBLE_MOVES if we could * + * not reach the destination * + * * + * WARNINGS: This algorithm assumes that the target is NOT situated * + * inside an impassable. If this case may arise, the do-while * + * statement inside the inner while (true) must be changed * + * to include a check to se if the next_x,y is equal to the * + * dest_x,y. If it is, then return(IMPOSSIBLE_MOVES). * + * * + * HISTORY: * + * 07/08/1991 CY : Created. * + *=============================================================================================*/ +PathType * FootClass::Find_Path(CELL dest, FacingType *final_moves, int maxlen, MoveType threshhold) +{ + CELL source = Coord_Cell(Coord); // Source expressed as cell + static PathType path; // Main path control. + CELL next; // Next cell to enter + CELL startcell; // Cell we started in + FacingType direction; // Working direction of look ahead. + FacingType newdir; // Tentative facing value. + + bool left=false, // Was leftward path legal? + right=false; // Was rightward path legal? + + int len; // Length of detour command list. + int unit_threat; // Calculated unit threat rating + int cost; // Cost to enter the square + FacingType moves_left[MAX_MLIST_SIZE+2], // Counterclockwise move list. + moves_right[MAX_MLIST_SIZE+2]; // Clockwise move list. + PathType pleft,pright; // Path control structures. + PathType *which; // Which path to actually use. + int threat; + int threat_stage; + + /* + ** If we have been provided an illegal place to store our final moves + ** then forget it. + */ + if (!final_moves) return(NULL); +// IsFindPath = true; + + /* + ** Set the draw path variable to draw the path of the selected unit + ** if necessary. + */ + if (!Debug_Find_Path) { + DrawPath = IsSelected && Special.IsShowPath; + } else { + DrawPath = IsSelected; + } + Debug_Draw_Map("Initial Draw", source, dest, false); + +// MoveMask = flags; + if (Team && Team->Class->IsRoundAbout) { + unit_threat = (Team) ? Team->Risk : Risk(); + threat_stage = 0; + threat = 0; + } else { + unit_threat = threat = -1; + } + + StartLocation = source; + DestLocation = dest; + + /* + ** Initialize the path structure so that we can keep track of the + ** path. + */ + path.Start = source; + path.Cost = 0; + path.Length = 0; + path.Command = final_moves; + path.Command[0] = END; + path.Overlap = MainOverlap; + path.LastOverlap = -1; + path.LastFixup = -1; + + memset(path.Overlap, 0, sizeof(MainOverlap)); + + /* + ** Clear the over lap list and then make sure that our starting position is marked + ** on the overlap list. (Otherwise the harvesters will drive in circles... ) + */ +// memset(path.Overlap, 0, 512); + path.Overlap[source >> 5] |= (1 << ((source & 31) - 1)); + + startcell = source; + + /* + ** Account for trailing end of list command, so reduce the maximum + ** allowed legal commands to reflect this. + */ + maxlen--; + + /* + ** As long as there is room to put commands in the movement command list, + ** then put commands in it. We build the path using the following + ** methodology. + ** + ** 1. Scan through the desired strait line path until we eiter hit an + ** impassable or have created a valid path. + ** + ** 2. If we have hit an impassable, walk through the impassable to make + ** sure that there is a passable on the other side. If there is not + ** and we can not change the impassable, then this list is dead. + ** + ** 3. Walk around the impassable on both the left and right edges and + ** take the shorter of the two paths. + ** + ** 4. Taking the new location as our start location start again with + ** step #1. + */ + while (path.Length < maxlen) { + +top_of_list: + /* + ** Have we reached the destination already? If so abort any further + ** command building. + */ + if (startcell == dest) { + break; + } + + /* + ** Find the absolute correct direction to reach the next straight + ** line cell and what cell it is. + */ + direction = CELL_FACING(startcell, dest); + next = Adjacent_Cell(startcell, direction); + + /* + ** If we can move here, then make this our next move. + */ + cost = Passable_Cell(next, direction, threat, threshhold); + if (cost) { + Draw_Cell_Point(next, true, threat_stage); + Register_Cell(&path, next, direction, cost, threshhold); + } else { + if (Debug_Find_Path && DrawPath) { + Debug_Draw_Map("Walk Through Obstacle", startcell, dest, true); + } + Draw_Cell_Point(next, false, threat_stage); + + /* + ** If the impassable location is actually the destination, + ** then stop here and consider this "good enough". + */ + if (next == dest) break; + + /* + ** We could not move to the next cell, so follow through the + ** impassable until we find a passable spot that can be reached. + ** Once we find a passable, figure out the shortest path to it. + ** Since we have variable passable conditions this is not as + ** simple as it used to be. The limiter loop below allows us to + ** step through ten donuts before we give up. + */ + for (int limiter = 0; limiter < 5; limiter++) { + + /* + ** Get the next passable position by zipping through the + ** impassable positions until a passable position is found + ** or the destination is reached. + */ + for (;;) { + + /* + ** Move one step closer toward destination. + */ + newdir = CELL_FACING(next, dest); + next = Adjacent_Cell(next, newdir); + + /* + ** If the cell is passable then we have been completely + ** sucessful. If the cell is not passable then continue. + */ + if ((Passable_Cell(next, FACING_NONE, threat, threshhold)) || (next == dest)) { + Draw_Cell_Point(next, true, threat_stage); + break; + } else { + Draw_Cell_Point(next, false, threat_stage); + } + + /* + ** If we reached destination while in this loop, we + ** know that either the destination is impassible (if + ** we are ignoring) or that we need to up our threat + ** tolerance and try again. + */ + if (next == dest) { + if (threat != -1) { + switch (threat_stage++) { + case 0: + threat = unit_threat >> 1; + break; + + case 1: + threat += unit_threat; + break; + + case 2: + threat = -1; + break; + } + goto top_of_list; + } + goto end_of_list; + } + } + + /* + ** Try to find a path to the passable position by following + ** the edge of the blocking object in both CLOCKwise and + ** COUNTERCLOCKwise fashions. + */ + int follow_len = maxlen + (maxlen >> 1); + + Debug_Draw_Map("Follow left edge", startcell,next,true); + Mem_Copy(&path, &pleft, sizeof(PathType)); + pleft.Command = &moves_left[0]; + pleft.Overlap = LeftOverlap; + Mem_Copy(path.Command, pleft.Command, path.Length); + Mem_Copy(path.Overlap, pleft.Overlap, sizeof(LeftOverlap)); + left = Follow_Edge(startcell, next, &pleft, COUNTERCLOCK, direction, threat, threat_stage, sizeof(moves_left), threshhold); +// left = Follow_Edge(startcell, next, &pleft, COUNTERCLOCK, direction, threat, threat_stage, follow_len, threshhold); + + if (left) { + follow_len = MIN(maxlen, pleft.Length + (pleft.Length >> 1)); + } + + /* + ** If we are in debug mode then let us know how well our left path + ** did. + */ + if (Debug_Find_Path && DrawPath) { + Fancy_Text_Print(" Left", 0, 92, WHITE, BLACK, TPF_6POINT); + Fancy_Text_Print("Total Steps", 0, 100, WHITE, BLACK, TPF_6POINT); + if (left) { + Fancy_Text_Print(" %d", 0, 108, WHITE, BLACK, TPF_6POINT, pleft.Length); + } else { + Fancy_Text_Print(" FAIL", 0, 108, WHITE, BLACK, TPF_6POINT); + } + } + + Debug_Draw_Map("Follow right edge", startcell, next, true); + Mem_Copy(&path, &pright, sizeof(PathType)); + pright.Command = &moves_right[0]; + pright.Overlap = RightOverlap; + Mem_Copy(path.Command, pright.Command, path.Length); + Mem_Copy(path.Overlap, pright.Overlap, sizeof(RightOverlap)); + right = Follow_Edge(startcell, next, &pright, CLOCK, direction, threat, threat_stage, sizeof(moves_right), threshhold); +// right = Follow_Edge(startcell, next, &pright, CLOCK, direction, threat, threat_stage, follow_len, threshhold); + + /* + ** If we are in debug mode then let us know how well our right path + ** did. + */ + if (Debug_Find_Path && DrawPath) { + Fancy_Text_Print(" Right", 0, 92, WHITE, BLACK, TPF_6POINT); + Fancy_Text_Print("Total Steps", 0, 100, WHITE, BLACK, TPF_6POINT); + if (right) { + Fancy_Text_Print(" %d", 0, 108, WHITE, BLACK, TPF_6POINT, pright.Length); + } else { + Fancy_Text_Print(" FAIL", 0, 108, WHITE, BLACK, TPF_6POINT); + } + } + + /* + ** If we could find a path, break from this loop. Otherwise this + ** means that we have found a "hole" of passable terrain that + ** cannot be reached by normal means. Scan forward looking for + ** the other side of the "doughnut". + */ + if (left || right) break; + + /* + ** If no path can be found to the intermediate cell, then + ** presume we have found a doughnut of some sort. Scan + ** forward until the next impassable is found and then + ** process this loop again. + */ + do { + + /* + ** If we reached destination while in this loop, we + ** know that either the destination is impassible (if + ** we are ignoring) or that we need to up our threat + ** tolerance and try again. + */ + if (next == dest) { + if (threat != -1) { + switch (threat_stage++) { + case 0: + threat = unit_threat >> 1; + break; + + case 1: + threat += unit_threat; + break; + + case 2: + threat = -1; + break; + } + goto top_of_list; + } + goto end_of_list; + } + + newdir = CELL_FACING(next, dest); + next = Adjacent_Cell(next, newdir); + } while (Passable_Cell(next, newdir, threat, threshhold)); + } + + if (!left && !right) break; + + /* + ** We found a path around the impassable locations, so figure out + ** which one was the smallest and copy those moves into the + ** path.Command array. + */ + which = &pleft; + if (right) { + which = &pright; + if (left) { + if (pleft.Length < pright.Length) { + which = &pleft; + } else { + which = &pright; + } + } + } + + /* + ** Record as much as possible of the shorter of the two + ** paths. The trailing EOL command is not copied because + ** this may not be the end of the find path logic. + */ + len = which->Length; + len = MIN(len, maxlen); + if (len > 0) { + memcpy(&path.Overlap[0], &which->Overlap[0], sizeof(LeftOverlap)); + memcpy(&path.Command[0], &which->Command[0], len); + path.Length = len; + path.Cost = which->Cost; + path.LastOverlap = -1; + path.LastFixup = -1; + } else { + break; + } + Debug_Draw_Map("Walking to next obstacle", next, dest, true); + } + startcell = next; + } + +end_of_list: + /* + ** Poke in the stop command. + */ + if (path.Length < maxlen) { + path.Command[path.Length++] = END; + } + if (Debug_Find_Path && DrawPath) { + Map.Flag_To_Redraw(true); + } + /* + ** Optimize the move list but only necessary if + ** diagonal moves are allowed. + */ + #ifdef DIAGONAL + Optimize_Moves(&path, threshhold); + #endif + if (Debug_Find_Path && DrawPath) { + Debug_Draw_Map("Final Generated Path", startcell,dest,false); + Debug_Draw_Path(&path); + Get_Key_Num(); + } +// IsFindPath = false; + return(&path); +} + + +/*********************************************************************************************** + * Follow_Edge -- Follow an edge to get around an impassable spot. * + * * + * INPUT: start -- cell to head from * + * * + * target -- Target cell to head to. * + * * + * path -- Pointer to path list structure. * + * * + * search -- Direction of search (1=clock, -1=counterclock). * + * * + * olddir -- Facing impassible direction from start. * + * * + * callback -- Function pointer for determining if a cell is * + * passable or not. * + * * + * OUTPUT: bool: Could a path be found to the desired cell? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1991 CY : Created. * + * 06/01/1992 JLB : Optimized & commented. * + *=============================================================================================*/ +bool FootClass::Follow_Edge(CELL start, CELL target, PathType *path, FacingType search, FacingType olddir, int threat, int threat_stage, int max_cells, MoveType threshhold) +{ + FacingType newdir; // Direction of facing before surrounding cell check. + CELL oldcell, // Current cell. + newcell; // Tentative new cell. + int cost; // Working cost value. + int startx; + int starty; + int online=true; + int targetx; + int targety; + int oldval = 0; + int cellcount=0; + int forceout = false; + FacingType firstdir = (FacingType)-1; + CELL firstcell = -1; + bool stepped_off_line = false; + startx = Cell_X(start); + starty = Cell_Y(start); + targetx = Cell_X(target); + targety = Cell_Y(target); + + if (!path) return(false); + path->LastOverlap = -1; + path->LastFixup = -1; + + #ifndef DIAGONAL + /* + ** The edge following algorithm doesn't "do" diagonals. Force initial facing + ** to be an even 90 degree value. Adjust it in the direction it should be + ** rotating. + */ + if (olddir & 0x01) { + olddir = Next_Direction(olddir, search); + } + #endif + + newdir = Next_Direction(olddir, search); + oldcell = start; + newcell = Adjacent_Cell(oldcell, newdir); + + /* + ** Continue until we find our target, find our original starting spot, + ** or run out of moves. + */ + while (path->Length < max_cells) { + + /* + ** Look in all the adjacent cells to determine a passable one that + ** most closely matches the desired direction (working in the specified + ** direction). + */ + newdir = olddir; + for (;;) { + bool forcefail; // Is failure forced? + + forcefail = false; + + #ifdef DIAGONAL + /* + ** Rotate 45/90 degrees in desired direction. + */ + newdir = Next_Direction(newdir, search); + + /* + ** If facing a diagonal we must check the next 90 degree location + ** to make sure that we don't walk right by the destination. This + ** will happen if the destination it is at the corner edge of an + ** impassable that we are moving around. + */ + if (newdir & FACING_NE) { + CELL checkcell; // Non-diagonal check cell. + //int x,y; + + checkcell = Adjacent_Cell(oldcell, Next_Direction(newdir, search)); + + if (checkcell == target) { + + /* + ** This only works if in fact, it is possible to move to the + ** cell from the current location. + */ + cost = Passable_Cell(checkcell, Next_Direction(newdir, search), threat, threshhold); + if (cost) { + Draw_Cell_Point(checkcell, true, threat_stage); + + /* + ** YES! The destination is at the corner of an impassable, so + ** set the direction to point directly at it and then the + ** scanning will terminate later. + */ + newdir = Next_Direction(newdir, search); + newcell = Adjacent_Cell(oldcell, newdir); + break; + } else { + Draw_Cell_Point(checkcell, false, threat_stage); + } + } + + /* + ** Perform special diagonal check. If the edge follower would cross the + ** diagonal or fall on the diagonal line from the source, then consider + ** that cell impassible. Otherwise, the find path algorithm will fail + ** when there are two impassible locations located on a diagonal + ** that is lined up between the source and destination location. + ** + ** P.S. It might help if you check the right cell rather than using + ** the value that just happened to be in checkcell. + */ + + checkcell = Adjacent_Cell(oldcell, newdir); + + int checkx = Cell_X(checkcell); + int checky = Cell_Y(checkcell); + int checkval = Point_Relative_To_Line(checkx, checky, startx, starty, targetx, targety); + if (checkval && !online) { + forcefail = ((checkval ^ oldval) < 0); + } else { + forcefail = false; + } + /* + ** The only exception to the above is when we are directly backtracking + ** because we could be trying to escape from a culdesack! + */ + if (forcefail && path->Length > 0 && (FacingType)(newdir ^ 4) == path->Command[path->Length - 1]) { +// ST - 12/18/96 5:15PM if (forcefail && (FacingType)(newdir ^ 4) == path->Command[path->Length - 1]) { + forcefail = false; + } + } + + #else + newdir = Next_Direction(newdir, search*2); + #endif + + /* + ** If we have just checked the same heading we started with, + ** we are surrounded by impassable characters and we exit. + */ + if (newdir == olddir) { + return(false); + } + + /* + ** Get the new cell. + */ + newcell = Adjacent_Cell(oldcell, newdir); + + /* + ** If we found a passable position, this is where we should move. + */ + if (!forcefail && ((cost = Passable_Cell(newcell, newdir, threat, threshhold)) != 0)) { + Draw_Cell_Point(newcell, true, threat_stage); + break; + } else { + Draw_Cell_Point(newcell, false, threat_stage, (forcefail) ? BROWN : 0); + if (newcell == target) { + forceout = true; + break; + } + } + + } + + /* + ** Record the direction. + */ + if (!forceout) { + /* + ** Mark the cell because this is where we need to be. If register + ** cell fails then the list has been shortened and we need to adjust + ** the new direction. + */ + if (!Register_Cell(path, newcell, newdir, cost, threshhold)) { + /* + ** The only reason we could not register a cell is that we are in + ** a looping situation. So we need to try and unravel the loop if + ** we can. + */ + if (!Unravel_Loop(path, newcell, newdir, startx, starty, targetx, targety, threshhold)) { + return(false); + } + /* + ** Since we need to eliminate a diagonal we must pretend the upon + ** attaining this square, we were moving turned farther in the + ** search direction then we really were. + */ + newdir = Next_Direction(newdir, (FacingType)(search*2)); + } + /* + ** Find out which side of the line this cell is on. If it is on + ** a side, then store off that side. + */ + int newx = Cell_X(newcell); + int newy = Cell_Y(newcell); + int val = Point_Relative_To_Line(newx, newy, startx, starty, targetx, targety); + if (val) { + oldval = val; + online = false; + } else { + online = true; + } + cellcount++; + if (cellcount==100) { +// DrawPath = true; +// Debug_Find_Path = true; +// Debug_Draw_Map("Loop failure", start, target, false); +// Debug_Draw_Path(path); + return(false); + } + } + + /* + ** If we have found the target spot, we are done. + */ + if (newcell == target) { + path->Command[path->Length] = END; + return(true); + } + + /* + ** If we make a full circle back to our original spot, get out. + */ + if (newcell == firstcell && newdir == firstdir) { + return(false); + } + + if (firstcell == -1) { + firstcell = newcell; + firstdir = newdir; + } + + /* + ** Because we moved, our facing is now incorrect. We want to face toward + ** the impassable edge we are following (well, not actually toward, but + ** a little past so that we can turn corners). We have to turn 45/90 degrees + ** more than expected in anticipation of the pending 45/90 degree turn at + ** the start of this loop. + */ + #ifdef DIAGONAL + olddir = Next_Direction(newdir, (FacingType)(-(int)search*3)); + #else + olddir = Next_Direction(newdir, (FacingType)(-(int)search*4)); + #endif + oldcell = newcell; + } + + /* + ** The maximum search path is exhausted... abort with a failure. + */ + return(false); +} + + +/*********************************************************************************************** + * Optimize_Moves -- Optimize the move list. * + * * + * INPUT: char *moves to optimize * + * * + * OUTPUT: none (list is optimized) * + * * + * WARNINGS: EMPTY moves are used to hold the place of eliminated * + * commands. Also, NEVER call this routine with a list that * + * contains illegal commands. The list MUST be terminated * + * with a EOL command * + * * + * HISTORY: * + * 07/08/1991 CY : Created. * + * 06/01/1992 JLB : Optimized and commented. * + *=============================================================================================*/ +#define EMPTY (FacingType)-2 +int FootClass::Optimize_Moves(PathType *path, MoveType threshhold) +//int Optimize_Moves(PathType *path, int (*callback)(CELL, FacingType), int threshold) +{ + /* + ** Facing command pair adjustment table. Compare the facing difference between + ** the two commands. 0 means no optimization is possible. 3 means backtracking + ** so eliminate both commands. Any other value adjusts the first command facing. + */ +#ifdef DIAGONAL + static FacingType _trans[FACING_COUNT] = {(FacingType)0, (FacingType)0, (FacingType)1, (FacingType)2, (FacingType)3, (FacingType)-2, (FacingType)-1, (FacingType)0}; // Smoothing. +#else + static FacingType _trans[FACING_COUNT] = {(FacingType)0, (FacingType)0, (FacingType)0, (FacingType)2, (FacingType)3, (FacingType)-2, (FacingType)0, (FacingType)0}; +#endif + FacingType *cmd1, // Floating first command pointer. + *cmd2, // Floating second command pointer. + newcmd; // Calculated new optimized command. + FacingType newdir; // Tentative new direction for smoothing. + CELL cell; // Working cell (as it moves along path). + + /* + ** Abort if there is any illegal parameter. + */ + if (!path || !path->Command) return(0); + + /* + ** Optimization loop -- start scanning with the + ** first pair of commands (if there are at least two + ** in the command list). + */ + path->Command[path->Length] = END; // Force end of list. + cell = path->Start; + if (path->Length > 1) { + cmd2 = path->Command + 1; + while (*cmd2 != END) { + + /* + ** Set the cmd1 pointer to point to the valid command closest, but + ** previous to cmd2. Be sure not to go previous to the head of the + ** command list. + */ + cmd1 = cmd2-1; + while (*cmd1 == EMPTY && cmd1 != path->Command) { + cmd1--; + } + + /* + ** If there isn't any valid previous command, then bump the + ** cmd pointers to the next command pair and continue... + */ + if (*cmd1 == EMPTY) { + cmd2++; + continue; + } + + /* + ** Fetch precalculated command change value. 0 means leave + ** command set alone, 3 means backtrack and eliminate two + ** commands. Any other value is new direction and eliminate + ** one command. + */ + newcmd = (FacingType)(*cmd2 - *cmd1); + if (newcmd < FACING_N) newcmd = (FacingType)(newcmd + FACING_COUNT); + newcmd = _trans[newcmd]; + + /* + ** Check for backtracking. If this occurs, then eliminate the + ** two commands. This is the easiest optimization. + */ + if (newcmd == FACING_SE) { + *cmd1 = EMPTY; + *cmd2++ = EMPTY; + continue; + } + + /* + ** If an optimization code was found the process it. The command is a facing + ** offset to more directly travel toward the immediate destination cell. + */ + if (newcmd) { + + /* + ** Optimizations differ when dealing with diagonals. Especially when dealing + ** with diagonals of 90 degrees. In such a case, 90 degree optimizations can + ** only be optimized if the intervening cell is passable. The distance travelled + ** is the same, but the path is less circuitous. + */ + if (*cmd1 & FACING_NE) { + + /* + ** Diagonal optimizations are always only 45 + ** degree adjustments. + */ + newdir = Next_Direction(*cmd1, (newcmd < FACING_N) ? (FacingType)-1 : (FacingType)1); + + /* + ** Diagonal 90 degree changes can be smoothed, although + ** the path isn't any shorter. + */ + if (ABS((int)newcmd) == 1) { + if (Passable_Cell(Adjacent_Cell(cell, newdir), newdir, -1, threshhold)) { + *cmd2 = newdir; + *cmd1 = newdir; + } + // BOB 16.12.92 + cell = Adjacent_Cell(cell, *cmd1); + cmd2++; + continue; + } + } else { + newdir = Next_Direction(*cmd1, newcmd); + } + + /* + ** Allow shortening turn only on right angle moves that are based on + ** 90 degrees. Always allow 135 degree optimizations. + */ + *cmd2 = newdir; + *cmd1 = EMPTY; + + /* + ** Backup what it thinks is the current cell. + */ + while (*cmd1 == EMPTY && cmd1 != path->Command) { + cmd1--; + } + if (*cmd1 != EMPTY) { + cell = Adjacent_Cell(cell, Next_Direction(*cmd1, FACING_S)); + } else { + cell = path->Start; + } + continue; + } + + /* + ** Since we could not make an optimization, we move our + ** head pointer forward. + */ + cell = Adjacent_Cell(cell, *cmd1); + cmd2++; + } + } + + /* + ** Pack the command list to remove any EMPTY command entries. + */ + cmd1 = path->Command; + cmd2 = path->Command; + cell = path->Start; + path->Cost = 0; + path->Length = 0; + while (*cmd2 != END) { + if (*cmd2 != EMPTY) { + +#ifdef NEVER + if (Debug_ShowPath) { + int x,y,x1,y1; + + if (Map.Coord_To_Pixel(Cell_Coord(cell), x, y)) { + Map.Coord_To_Pixel(Cell_Coord(Adjacent_Cell(cell, *cmd2)), x1, y1); + Set_Logic_Page(SeenBuff); + LogicPage->Draw_Line(x, y+8, x1, y1+8, DKGREY); + } + } +#endif + + cell = Adjacent_Cell(cell, *cmd2); + path->Cost+= Passable_Cell(cell, *cmd2, -1, threshhold); + path->Length++; + *cmd1++ = *cmd2; + } + cmd2++; + } + path->Length++; + *cmd1 = END; + return(path->Length); +} + + +CELL FootClass::Safety_Point(CELL src, CELL dst, int start, int max) +{ + FacingType dir; + CELL next; + int lp; + + dir = (FacingType)(CELL_FACING(src, dst) ^ 4) - 1; + + /* + ** Loop through the different acceptable distances. + */ + for (int dist = start; dist < max; dist ++) { + + /* + ** Move to the starting location. + */ + next = dst; + + for (lp = 0; lp < dist; lp ++) { + next = Adjacent_Cell(next, dir); + } + + if (dir & 1) { + /* + ** If our direction is diagonal than we need to check + ** only one side which is as long as both of the old sides + ** together. + */ + for (lp = 0; lp < dist << 1; lp ++) { + next = Adjacent_Cell(next, dir + 3); + if (!Can_Enter_Cell(next)) { + return(next); + } + } + } else { + /* + ** If our direction is not diagonal than we need to check two + ** sides so that we are checking a corner like location. + */ + for (lp = 0; lp < dist; lp ++) { + next = Adjacent_Cell(next, dir + 2); + if (!Can_Enter_Cell(next)) { + return(next); + } + } + + for (lp = 0; lp < dist; lp ++) { + next = Adjacent_Cell(next, dir + 4); + if (!Can_Enter_Cell(next)) { + return(next); + } + } + } + } + return(-1); +} + + + + +int FootClass::Passable_Cell(CELL cell, FacingType face, int threat, MoveType threshhold) +{ + MoveType move = Can_Enter_Cell(cell, face); + + if (move < MOVE_MOVING_BLOCK && Distance(cell) > 1) threshhold = MOVE_MOVING_BLOCK; + + if (move > threshhold) return(0); + + if (GameToPlay == GAME_NORMAL) { + if (threat != -1) { + if (Map.Cell_Distance(cell, DestLocation) > THREAT_THRESHOLD) { + if (Map.Cell_Threat(cell, Owner()) > threat) + return(0); + } + } + } + + static int _value[MOVE_COUNT] = { + 1, // MOVE_OK + 1, // MOVE_CLOAK + 3, // MOVE_MOVING_BLOCK + 8, // MOVE_DESTROYABLE + 10, // MOVE_TEMP + 0 // MOVE_NO + }; + return(_value[move]); + +#ifdef NEVER + int can; + int retval; + + int temp_move_mask = MoveMask; + + if (!House->IsHuman) { + temp_move_mask &= ~MOVEF_TEMP; + } + +#ifdef NEVER + if ((!(MoveMask & MOVEF_MOVING_BLOCK)) && Map.Cell_Distance(StartLocation, cell) > 2) { + temp_move_mask |= MOVEF_MOVING_BLOCK; + } +#endif + + can = (temp_move_mask & Can_Enter_Cell(cell, face)); + if (can & MOVEF_NO) return(0); + + retval = 1; + if (can & MOVEF_MOVING_BLOCK) retval += 3; + if (can & MOVEF_DESTROYABLE) retval += 10; + if (can & MOVEF_TEMP) retval += 10; + + if (threat != -1) { + if (Map.Cell_Distance(cell, DestLocation) > THREAT_THRESHOLD) { + if (Map.Cell_Threat(cell, Owner()) > threat) + return(0); + } + } + + return(retval); +#endif +} + + +void FootClass::Debug_Draw_Map(char *txt, CELL start, CELL dest, bool pause) +{ + if ((!Debug_Find_Path) || (!DrawPath)) return; + + if (pause) Get_Key_Num(); + GraphicViewPortClass * page = Set_Logic_Page(SeenBuff); + + VisiblePage.Clear(); + Fancy_Text_Print(txt, 160, 0, WHITE, BLACK, TPF_8POINT|TPF_CENTER); + for (int x = 0; x < 64; x++) { + for (int y = 0; y < 64; y++) { + int color = 0; + + switch (Can_Enter_Cell( (CELL)((y << 6) + x))) { + case MOVE_OK: + color = GREEN; + break; + case MOVE_MOVING_BLOCK: + color = LTGREEN; + break; + + case MOVE_DESTROYABLE: + color = YELLOW; + break; + case MOVE_TEMP: + color = BROWN; + break; + default: + color = RED; + break; + } + if ((CELL)((y << 6) + x) == start) + color = LTBLUE; + if ((CELL)((y << 6) + x) == dest) + color = BLUE; + Fat_Put_Pixel(64 + (x*3), 8 + (y*3), color, 3, SeenBuff); + } + } + Set_Logic_Page(page); +} + +void FootClass::Debug_Draw_Path(PathType *path) +{ + if (!path) return; + + FacingType *list = path->Command; + CELL pos = path->Start; + + for (int idx = 0; idx < path->Length; idx++) { + pos = Adjacent_Cell(pos, *list++); + Draw_Cell_Point(pos, true, -1, 0); + } +} diff --git a/FLASHER.CPP b/FLASHER.CPP new file mode 100644 index 0000000..a155aee --- /dev/null +++ b/FLASHER.CPP @@ -0,0 +1,97 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\flasher.cpv 2.18 16 Oct 1995 16:49:24 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FLASHER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 28, 1994 * + * * + * Last Update : October 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FlasherClass::Process -- Performs the logic processing for the flashing ability. * + * FlasherClass::Debug_Dump -- Displays current status to the monochrome screen. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * FlasherClass::Debug_Dump -- Displays current status to the monochrome screen. * + * * + * This utility function will output the current status of the FlasherClass to the mono * + * screen. It is through this display that bugs may be fixed or detected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void FlasherClass::Debug_Dump(MonoClass *mono) const +{ + mono->Set_Cursor(50, 7); + mono->Printf("%2d", FlashCount); +} +#endif + + +/*********************************************************************************************** + * FlasherClass::Process -- Performs the logic processing for the flashing ability. * + * * + * The ability for an object to flash is controlled by this logic processing routine. It * + * should be called once per game tick per unit. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Should the associated object be redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + * 06/20/1994 JLB : Is now independent of object it represents. * + *=============================================================================================*/ +bool FlasherClass::Process(void) +{ + if (FlashCount) { + FlashCount--; + IsBlushing = false; + + if (FlashCount & 0x01) { + IsBlushing = true; + } + return(true); + } + return(false); +} + + diff --git a/FLASHER.H b/FLASHER.H new file mode 100644 index 0000000..be89421 --- /dev/null +++ b/FLASHER.H @@ -0,0 +1,71 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\flasher.h_v 2.17 16 Oct 1995 16:45:12 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FLASHER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 28, 1994 * + * * + * Last Update : May 28, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FLASHER_H +#define FLASHER_H + +class FlasherClass { + public: + /* + ** When this object is targeted, it will flash a number of times. This is the + ** flash control number. It counts down to zero and then stops. Odd values + ** cause the object to be rendered in a lighter color. + */ + unsigned FlashCount:7; + + /* + ** When an object is targeted, it flashes several times to give visual feedback + ** to the player. Every other game "frame", this flag is true until the flashing + ** is determined to be completed. + */ + unsigned IsBlushing:1; + + FlasherClass(void) {FlashCount = 0; IsBlushing = false;}; + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const; + #endif + bool Process(void); + + /* + ** File I/O. + */ + void Code_Pointers(void); + void Decode_Pointers(void); + +}; + +#endif diff --git a/FLY.CPP b/FLY.CPP new file mode 100644 index 0000000..b53eed0 --- /dev/null +++ b/FLY.CPP @@ -0,0 +1,133 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\fly.cpv 2.18 16 Oct 1995 16:50:40 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FLY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 24, 1994 * + * * + * Last Update : June 5, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FlyClass::Physics -- Performs vector physics (movement). * + * FlyClass::Fly_Speed -- Sets the flying object to the speed specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * FlyClass::Physics -- Performs vector physics (movement). * + * * + * This routine performs movement (vector) physics. It takes the * + * specified location and moves it according to the facing and speed * + * of the vector. It returns the status of the move. * + * * + * INPUT: coord -- Reference to the coordinate that the vector will * + * be applied to. * + * * + * OUTPUT: Returns with the status of the vector physics. This could * + * range from no effect, to exiting the edge of the world. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/24/1994 JLB : Created. * + * 06/05/1995 JLB : Simplified to just do movement. * + *=============================================================================================*/ +ImpactType FlyClass::Physics(COORDINATE & coord, DirType facing) +{ + if (SpeedAdd != MPH_IMMOBILE) { + int actual = (int)SpeedAdd + SpeedAccum; + div_t result = div(actual, PIXEL_LEPTON_W); + SpeedAccum = result.rem; + actual -= result.rem; + COORDINATE old = coord; + + /* + ** If movement occurred that is at least one + ** pixel, then check update the coordinate and + ** check for edge of world collision. + */ + if (result.quot) { + COORDINATE newcoord; // New working coordinate. + + newcoord = Coord_Move(coord, facing, actual); + + /* + ** If no movement occurred, then presume it hasn't moved at all + ** and return immediately with this indication. + */ + if (newcoord == coord) { + return(IMPACT_NONE); + } + + /* + ** Remember the new position. + */ + coord = newcoord; + + /* + ** If the new coordinate is off the edge of the world, then report + ** this. + */ + if (newcoord & 0xC000C000L /*|| !Map.In_Radar(Coord_Cell(newcoord))*/) { + coord = old; + return(IMPACT_EDGE); + } + + return(IMPACT_NORMAL); + } + } + return(IMPACT_NONE); +} + + +/*********************************************************************************************** + * FlyClass::Fly_Speed -- Sets the flying object to the speed specified. * + * * + * This sets the speed of the projectile. It basically functions like a throttle value * + * where 0 equals stop and 255 equals maximum speed (whatever that is for the particular * + * object). * + * * + * INPUT: speed -- Speed setting from 0 to 255. * + * * + * maximum -- The maximum speed of the object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + * 07/26/1994 JLB : Added maximum speed as guiding value. * + *=============================================================================================*/ +void FlyClass::Fly_Speed(int speed, MPHType maximum) +{ + SpeedAdd = (MPHType)Fixed_To_Cardinal((int)maximum, speed); +} + diff --git a/FLY.H b/FLY.H new file mode 100644 index 0000000..962755e --- /dev/null +++ b/FLY.H @@ -0,0 +1,83 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\fly.h_v 2.19 16 Oct 1995 16:45:18 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FLY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 24, 1994 * + * * + * Last Update : April 24, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FLY_H +#define FLY_H + +typedef enum ImpactType { + IMPACT_NONE, // No movement (of significance) occurred. + IMPACT_NORMAL, // Some (non eventful) movement occurred. + IMPACT_EDGE // The edge of the world was reached. +} ImpactType; + + +/**************************************************************************** +** Flying objects are handled by this class definition. +*/ +class FlyClass { + public: + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + FlyClass(void) { + SpeedAdd = MPH_IMMOBILE; + SpeedAccum = 0; + }; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + void Fly_Speed(int speed, MPHType maximum); + ImpactType Physics(COORDINATE &coord, DirType facing); + MPHType Get_Speed(void) const {return(SpeedAdd);}; + + /* + ** File I/O. + */ + void Code_Pointers(void); + void Decode_Pointers(void); + + private: + /* + ** Object movement consists of incrementing the accumulator until enough "distance" + ** has accumulated so that moving the object becomes reasonable. + */ + unsigned SpeedAccum; // Lepton accumulator. + MPHType SpeedAdd; // Lepton add (per frame). +}; + +#endif diff --git a/FOOT.CPP b/FOOT.CPP new file mode 100644 index 0000000..64e5e2f --- /dev/null +++ b/FOOT.CPP @@ -0,0 +1,2015 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\foot.cpv 2.17 16 Oct 1995 16:51:42 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FOOT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 22, 1994 * + * * + * Last Update : August 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FootClass::Active_Click_With -- Intiates attack or move according to target clicked on. * + * FootClass::Active_Click_With -- Performs action as a result of left mouse click. * + * FootClass::Approach_Target -- Sets the navigation computer to approach target object. * + * FootClass::Assign_Destination -- Assigns specified destination to NavCom. * + * FootClass::Assign_Mission -- Assign mission to foot class object. * + * FootClass::Basic_Path -- Finds the basic path for a ground object. * + * FootClass::Body_Facing -- Set the body rotation/facing. * + * FootClass::Can_Demolish -- Checks to see if this object can be sold back. * + * FootClass::Can_Enter_Cell -- Checks to see if the object can enter cell specified. * + * FootClass::Death_Announcement -- Announces the death of a unit. * + * FootClass::Debug_Dump -- Displays the status of the FootClass to the mono monitor. * + * FootClass::Detach -- Detaches a target from tracking systems. * + * FootClass::Detach_All -- Removes this object from the game system. * + * FootClass::Enters_Building -- When unit enters a building for some reason. * + * FootClass::FootClass -- Default constructor for foot class objects. * + * FootClass::FootClass -- Normal constructor for the foot class object. * + * FootClass::Greatest_Threat -- Fetches the greatest threat to this object. * + * FootClass::Likely_Coord -- Fetches the coordinate the object will be at shortly. * + * FootClass::Limbo -- Intercepts limbo event and handles FootClass processing. * + * FootClass::Mark -- Unit interface to map rendering system. * + * FootClass::Mission_Attack -- AI for heading towards and firing upon target. * + * FootClass::Mission_Capture -- Handles the capture mission. * + * FootClass::Mission_Enter -- Enter (cooperatively) mission handler. * + * FootClass::Mission_Guard_Area -- Causes unit to guard an area about twice weapon range. * + * FootClass::Mission_Hunt -- Handles the default hunt order. * + * FootClass::Mission_Move -- AI process for moving a vehicle to its destination. * + * FootClass::Offload_Tiberium_Bail -- Fetches the Tiberium to offload per step. * + * FootClass::Override_Mission -- temporarily overides a units mission * + * FootClass::Per_Cell_Process -- Perform action based on once-per-cell condition. * + * FootClass::Receive_Message -- Movement related radio messages are handled here. * + * FootClass::Rescue_Mission -- Calls this unit to the rescue. * + * FootClass::Restore_Mission -- Restores an overidden mission * + * FootClass::Sell_Back -- Causes this object to be sold back. * + * FootClass::Set_Speed -- Initiate unit movement physics. * + * FootClass::Sort_Y -- Determine the sort coordinate for foot class objects. * + * FootClass::Start_Driver -- This starts the driver heading to the destination desired. * + * FootClass::Stop_Driver -- This routine clears the driving state of the object. * + * FootClass::Stun -- Prepares a ground travelling object for removal. * + * FootClass::Take_Damage -- Handles taking damage to this object. * + * FootClass::Unlimbo -- Unlimbos object and performs special fixups. * + * FootClass::~FootClass -- Default destructor for foot class objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * FootClass::FootClass -- Default constructor for foot class objects. * + * * + * This is the default constructor for FootClass objects. It sets the foot class values to * + * their default starting settings. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/23/1994 JLB : Created. * + *=============================================================================================*/ +FootClass::FootClass(void) : Speed(0) +{ + ArchiveTarget = TARGET_NONE; + IsDriving = false; + IsInitiated = false; + IsPlanningToLook = false; + IsDeploying = false; + IsNewNavCom = false; + IsFiring = false; + IsRotating = false; + IsUnloading = false; + NavCom = TARGET_NONE; + SuspendedNavCom = TARGET_NONE; + Path[0] = FACING_NONE; + HeadToCoord = NULL; + Member = 0; + Team = 0; + PathDelay = 0; + TryTryAgain = PATH_RETRY; + if (House) { + House->CurUnits++; + } + Group = -1; +} + + +/*********************************************************************************************** + * FootClass::~FootClass -- Default destructor for foot class objects. * + * * + * At this level of the destruction process, the house record of the number of units * + * currently in inventory is decremented to reflect this units destruction. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/10/1995 JLB : Created. * + *=============================================================================================*/ +FootClass::~FootClass(void) +{ + if (GameActive && House) { + House->CurUnits--; + } +} + + +/*********************************************************************************************** + * FootClass::FootClass -- Normal constructor for the foot class object. * + * * + * This is the normal constructor used when creating a foot class object. * + * * + * INPUT: house -- The house that owns this object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +FootClass::FootClass(HousesType house) : + TechnoClass(house), + Speed(0) +{ + ArchiveTarget = TARGET_NONE; + Member = 0; + Team = 0; + Path[0] = FACING_NONE; + NavCom = TARGET_NONE; + SuspendedNavCom = TARGET_NONE; + IsUnloading = false; + IsDriving = false; + IsInitiated = false; + IsRotating = false; + IsFiring = false; + IsDeploying = false; + IsNewNavCom = false; + IsPlanningToLook = false; + HeadToCoord = 0L; + PathDelay = 0; + Group = -1; + TryTryAgain = PATH_RETRY; + House->CurUnits++; +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * FootClass::Debug_Dump -- Displays the status of the FootClass to the mono monitor. * + * * + * This routine is used to output the current status of the foot class to the mono * + * monitor. Through this display bugs may be tracked down or eliminated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + * 07/04/1995 JLB : Handles aircraft special case. * + *=============================================================================================*/ +void FootClass::Debug_Dump(MonoClass *mono) const +{ + static char const * _p2c[9] = {"-","0","1","2","3","4","5","6","7"}; +#define Path_To_String(a) _p2c[((ABS((int)a+1))%9)] + + /* + ** Display the common data for all objects that inherity from FootClass. + */ + mono->Set_Cursor(63, 7); + if (Team) { + mono->Printf("%s(%d)", Team->Class->IniName, Teams.ID(Team)); + } else { + mono->Printf("(none)"); + } + mono->Set_Cursor(73, 7);mono->Printf("%04X", ArchiveTarget); + mono->Set_Cursor(42, 1);mono->Printf("%04X", NavCom); + mono->Set_Cursor(44, 3);mono->Printf("%d", Speed); + + /* + ** Although aircraft inherit from FootClass, some of the variables are not + ** used and thus should not be displayed. + */ + if (What_Am_I() != RTTI_AIRCRAFT) { + mono->Set_Cursor(50, 3); + mono->Printf("%s%s%s%s%s%s%s%s%s%s%s%s", + Path_To_String(Path[0]), + Path_To_String(Path[1]), + Path_To_String(Path[2]), + Path_To_String(Path[3]), + Path_To_String(Path[4]), + Path_To_String(Path[5]), + Path_To_String(Path[6]), + Path_To_String(Path[7]), + Path_To_String(Path[8]), + Path_To_String(Path[9]), + Path_To_String(Path[10]), + Path_To_String(Path[11]), + Path_To_String(Path[12])); + + mono->Set_Cursor(65, 1);mono->Printf("%08lX", Head_To_Coord()); + mono->Text_Print("X", 16 + (IsDeploying?2:0), 12); + mono->Text_Print("X", 16 + (IsRotating?2:0), 13); + mono->Text_Print("X", 16 + (IsDriving?2:0), 15); + mono->Text_Print("X", 16 + (IsFiring?2:0), 14); + mono->Text_Print("X", 16 + (IsPlanningToLook?2:0), 16); + } + TechnoClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * FootClass::Set_Speed -- Initiate unit movement physics. * + * * + * This routine is used to set a unit's velocity control structure. * + * The game will then process the unit's movement during the momentum * + * physics calculation. * + * * + * INPUT: unit -- Pointer to the unit to alter. * + * * + * speed -- Throttle setting (0=stop, 255=full throttle). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/07/1992 JLB : Created. * + * 09/24/1993 JLB : Revised for faster speed. * + * 04/02/1994 JLB : Revised for new system. * + * 04/15/1994 JLB : Converted to member function. * + * 07/21/1994 JLB : Simplified. * + *=============================================================================================*/ +void FootClass::Set_Speed(int speed) +{ + speed &= 0xFF; + ((unsigned char &)Speed) = speed; +} + + +/*********************************************************************************************** + * FootClass::Mark -- Unit interface to map rendering system. * + * * + * This routine is the interface function for units as they relate to * + * the map rendering system. Whenever a unit's imagery changes, this * + * function is called. * + * * + * INPUT: mark -- Type of image change (MARK_UP, _DOWN, _CHANGE) * + * MARK_UP -- Unit is removed. * + * MARK_CHANGE -- Unit alters image but doesn't move. * + * MARK_DOWN -- Unit is overlaid onto existing icons. * + * * + * OUTPUT: bool; Did the marking operation succeed? Failure could be the result of marking * + * down when it is already down, or visa versa. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/14/1991 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + * 12/23/1994 JLB : Performs low level check before processing. * + *=============================================================================================*/ +bool FootClass::Mark(MarkType mark) +{ + if (TechnoClass::Mark(mark)) { + CELL cell = Coord_Cell(Coord); + + /* + ** Inform the map of the refresh, occupation, and overlap + ** request. + */ + switch (mark) { + case MARK_UP: + Map.Pick_Up(cell, this); + break; + + case MARK_DOWN: + Map.Place_Down(cell, this); + break; + + default: + Map.Refresh_Cells(cell, Overlap_List()); + Map.Refresh_Cells(cell, Occupy_List()); + break; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Basic_Path -- Finds the basic path for a ground object. * + * * + * This is a common routine used by both infantry and other ground travelling units. It * + * will fill in the unit's basic path to the NavCom destination. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was a path found? A failure to find a path means either the target cannot * + * be found or the terrain prohibits the unit's movement. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Basic_Path(void) +{ + PathType *path; // Pointer to path control structure. + CELL cell; + int skip_path = false; + + Path[0] = FACING_NONE; + + if (Target_Legal(NavCom)) { + cell = As_Cell(NavCom); + + /* + ** When the navigation computer is set to a location that is impassible, then + ** find a nearby cell that can be entered and try to head toward that instead. + ** EXCEPT when that cell is very close -- then just bail. + */ +// IsFindPath = true; + if (Can_Enter_Cell(cell) == MOVE_NO && Distance(NavCom) > 0x0300) { + static int _faceadjust[8] = {0, 1, -1, 2, -2, 3, -3, 4}; + FacingType f2 = (FacingType)(((unsigned)::Direction(cell, Coord_Cell(Coord)))>>5); + + for (unsigned index = 0; index < (sizeof(_faceadjust) / sizeof(_faceadjust[0])); index++) { + CELL cell2; + + cell2 = Adjacent_Cell(cell, (FacingType)((f2+_faceadjust[index])&0x7)); + if (Can_Enter_Cell(cell2, FACING_NONE) <= MOVE_CLOAK) { + cell = cell2; + break; + } + } + } +// IsFindPath = false; + +#ifdef SPECIAL + if (What_Am_I() == RTTI_INFANTRY) { + CELL mycell = Coord_Cell(Center_Coord()); +// Mark(MARK_UP); + ObjectClass *obj = Map[mycell].Cell_Occupier(); + while (obj) { + if (obj != this && obj->What_Am_I() == RTTI_INFANTRY) { + InfantryClass *inf = (InfantryClass *)obj; + if (inf->NavCom == NavCom && inf->Path[0] != FACING_NONE) { + if (Coord_Cell(inf->Head_To_Coord()) == Coord_Cell(inf->Coord)) { + Mem_Copy(&inf->Path[1], Path, sizeof(Path)-sizeof(Path[0])); + } else { + Mem_Copy(inf->Path, Path, sizeof(Path)); + } + if (Path[0] != FACING_NONE) { + skip_path = true; + } + break; + } + } + obj = obj->Next; + } +// Mark(MARK_DOWN); + } +#endif + + if (!skip_path) { + Mark(MARK_UP); + Path[0] = FACING_NONE; // Probably not necessary, but... + + /* + ** Try to find a path to the destination. If a failure occurs, then keep trying + ** with greater determination until either a complete failure occurs, or a decent + ** path was found. + */ + bool found1=false; // Found a best path yet? + PathType path1; + FacingType workpath1[200]; // Staging area for path list. + FacingType workpath2[200]; // Staging area for path list. + MoveType maxtype = MOVE_TEMP; + if (!House->IsHuman) { + maxtype = MOVE_DESTROYABLE; + } else { + + /* + ** For simple movement missions by the human player, then don't + ** consider friendly units as passable if close to the destination. + ** This will prevent a human controlled unit from just sitting next + ** to a destination just because there is another friendly unit + ** occupying the destination location. + */ + if (Mission == MISSION_MOVE && Distance(NavCom) < 0x0280) { + maxtype = MOVE_DESTROYABLE; + } + } + + /* + ** Determine if ANY path could be calculated by first examining the most + ** aggressive case. If this fails, then no path will succeed. Further + ** scanning is unnecessary. + */ + path = Find_Path(cell, &workpath1[0], sizeof(workpath1), maxtype); + if (path && path->Cost) { + memcpy(&path1, path, sizeof(path1)); + found1 = true; + + /* + ** Scan for the best path possible. If this succeeds, then do a simple + ** comparison with the most agressive path. If they are very close, then + ** go with the best (easiest) path method. + */ + path = Find_Path(cell, &workpath2[0], sizeof(workpath2), MOVE_CLOAK); + if (path && path->Cost && path->Cost < MAX((path1.Cost + (path1.Cost/2)), 3)) { + memcpy(&path1, path, sizeof(path1)); + memcpy(workpath1, workpath2, sizeof(workpath1)); + } else { + + /* + ** The easiest path method didn't result in a satisfactory path. Scan through + ** the rest of the path options, looking for the best one. + */ + for (MoveType move = MOVE_MOVING_BLOCK; move < maxtype; move++) { + path = Find_Path(cell, &workpath2[0], sizeof(workpath2), move); + if (path && path->Cost && path->Cost < MAX((path1.Cost + (path1.Cost/2)), 3)) { + memcpy(&path1, path, sizeof(path1)); + memcpy(workpath1, workpath2, sizeof(workpath1)); + } + } + } + } + +#ifdef OBSOLETE + for (MoveType move = MOVE_CLOAK; move <= maxtype; move++) { + if (!found1) { + path = Find_Path(cell, &workpath1[0], sizeof(workpath1), move); + if (path && path->Cost) { + memcpy(&path1, path, sizeof(path1)); + found1 = true; + if (path1.Cost < 5) break; + } + } else { + path = Find_Path(cell, &workpath2[0], sizeof(workpath2), move); + + if (path) { + if (path->Cost && path->Cost <= path1.Cost/2) { + memcpy(&path1, path, sizeof(path1)); + memcpy(workpath1, workpath2, sizeof(workpath1)); + } + } + } + } +#endif + + /* + ** If a good path was found, then record it in the object's path + ** list. + */ + if (found1) { + Fixup_Path(&path1); + memcpy(&Path[0], &workpath1[0], MIN(path->Length, (int)sizeof(Path))); + } + + Mark(MARK_DOWN); + } + + +#ifdef NEVER + /* + ** Patch at this point to see if we are moving directly into a + ** MOVE_TEMP. This allows units to bunch up at a bridge even if there + ** is an enormously long way around. This also allows units to give + ** up trying to move into the MOVE_TEMP using the normal movement + ** retry logic. + */ + CELL cell = Adjacent_Cell(Coord_Cell(Coord), Path[0]); + if (Can_Enter_Cell(cell, FACING_NONE) == MOVEF_TEMP) { + Path[0] = FACING_NONE; + } +#endif + + PathDelay = PATH_DELAY; + if (Path[0] != FACING_NONE) return(true); + + /* + ** If a basic path couldn't be determined, then abort the navigation process. + */ +// NavCom = TARGET_NONE; + Stop_Driver(); + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Mission_Move -- AI process for moving a vehicle to its destination. * + * * + * This simple AI script handles moving the vehicle to its desired destination. Since * + * simple movement is handled directly by the engine, this routine merely waits until * + * the unit has reached its destination, and then causes the unit to enter idle mode. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Move(void) +{ + if (!Target_Legal(NavCom) && !IsDriving && MissionQueue == MISSION_NONE) { + Enter_Idle_Mode(); + } + if (!Target_Legal(TarCom) && !House->IsHuman) { + Target_Something_Nearby(THREAT_RANGE); + } + return(TICKS_PER_SECOND+3); +} + + +/*********************************************************************************************** + * FootClass::Mission_Capture -- Handles the capture mission. * + * * + * Capture missions are nearly the same as normal movement missions. The only difference * + * is that the final destination is handled in a special way so that it is not marked as * + * impassable. This allows the object (usually infantry) the ability to walk onto the * + * object and thus capture it. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game ticks to delay before calling this routine. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Capture(void) +{ + if (!Target_Legal(NavCom) && !In_Radio_Contact()) { + Enter_Idle_Mode(); + if (Map[Coord_Cell(Center_Coord())].Cell_Building()) { + Scatter(0, true); + } + } + return(TICKS_PER_SECOND-2); +} + + +/*********************************************************************************************** + * FootClass::Mission_Attack -- AI for heading towards and firing upon target. * + * * + * This AI routine handles heading to within range of the target and then firing upon * + * it until it is destroyed. If the target is destroyed, then the unit will change * + * missions to match its "idle mode" of operation (usually guarding). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Attack(void) +{ + if (Target_Legal(TarCom)) { + Approach_Target(); + } else { + Enter_Idle_Mode(); + } + return(TICKS_PER_SECOND+2); +} + + +/*********************************************************************************************** + * FootClass::Mission_Guard -- Handles the AI for guarding in place. * + * * + * Units that are performing stationary guard duty use this AI process. They will sit * + * still and target any enemies that get within range. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Guard(void) +{ + if (!Target_Something_Nearby(THREAT_RANGE)) { + Random_Animate(); + } + return(TICKS_PER_SECOND+Random_Picky((int)0, (int)4, (char*)NULL, (int)0)); +} + + +/*********************************************************************************************** + * FootClass::Mission_Hunt -- Handles the default hunt order. * + * * + * This routine is the default hunt order for game objects. It handles searching for a * + * nearby object and heading toward it. The act of targetting will cause it to attack * + * the target it selects. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the game tick delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Hunt(void) +{ + if (!Target_Something_Nearby(THREAT_NORMAL)) { + Random_Animate(); + } else { + if (What_Am_I() == RTTI_INFANTRY && ((InfantryTypeClass const &)Class_Of()).Type == INFANTRY_E7) { + Assign_Destination(TarCom); + Assign_Mission(MISSION_CAPTURE); + } else { + Approach_Target(); + } + } + return(TICKS_PER_SECOND+5); +} + + +/*********************************************************************************************** + * FootClass::Mission_Timed_Hunt -- This is the AI process for multiplayer computer units. * + * * + * For multiplayer games, the computer AI can't just blitz the human players; the humans * + * need a little time to set up their base, or whatever. This state just waits for * + * a certain period of time, then goes into hunt mode. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Timed_Hunt(void) +{ + int rndmax; + int changed = 0; // has the unit changed into Hunt mode? + + if (!House->IsHuman) { + + /* + ** Jump into HUNT mode if we're supposed to Blitz, and the EndCountDown + ** has expired, or if our owning house has lost more than 1/4 of its units + ** (it gets mad at you) + */ + if ( (MPlayerBlitz && House->BlitzTime==0) || + House->CurUnits < ((House->MaxUnit * 4) / 5)) { + Assign_Mission(MISSION_HUNT); + changed = 1; + } + + /* + ** Jump into HUNT mode on a random die roll; the computer units will periodically + ** "snap out" of their daze, and begin hunting. Try to time it so that all + ** units will be hunting within 10 minutes (600 calls to this routine). + */ + if (MPlayerBases) { + rndmax = 5000; + } else { + rndmax = 1000; + } + + if (IRandom(0,rndmax) == 1) { + Assign_Mission(MISSION_HUNT); + changed = 1; + } + + /* + ** If this unit is still just sitting in Timed Hunt mode, call Guard Area + ** so it doesn't just sit there stupidly. + */ + if (!changed) { + Mission_Guard_Area(); + } + } + + return(TICKS_PER_SECOND+Random_Pick(0, 4)); // call me back in 1 second. + +} + + +/*********************************************************************************************** + * FootClass::Stop_Driver -- This routine clears the driving state of the object. * + * * + * This is the counterpart routine to the Start_Driver function. It clears the driving * + * status flags and destination coordinate record. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was driving stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 12/12/1994 JLB : Greatly simplified. * + *=============================================================================================*/ +bool FootClass::Stop_Driver(void) +{ + if (HeadToCoord) { + HeadToCoord = NULL; + Set_Speed(0); + IsDriving = false; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Start_Driver -- This starts the driver heading to the destination desired. * + * * + * Before a unit can move it must be started by this routine. This routine handles * + * reserving the cell and setting the driving flag. * + * * + * INPUT: headto -- The coordinate of the immediate drive destination. This is one cell * + * away from the unit's current location. * + * * + * OUTPUT: bool; Was driving initiated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 12/12/1994 JLB : Uses simple spot index finder. * + *=============================================================================================*/ +bool FootClass::Start_Driver(COORDINATE &headto) +{ + Stop_Driver(); + if (headto) { + HeadToCoord = headto; + IsDriving = true; + + /* + ** Check for crate goodie finder here. + */ + if (Map[Coord_Cell(headto)].Goodie_Check(this)) { + return(true); + } + + HeadToCoord = NULL; + IsDriving = false; + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Sort_Y -- Determine the sort coordinate for foot class objects. * + * * + * This routine will determine the sort coordinate for foot class object. This coordinate * + * is usually the coordinate of the object. The exception is if the object is teathered. * + * In this case (presumes offloading to the north), the sorting coordinate is adjusted * + * so that the object will be drawn on top of the transport unit. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to use for sorting. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 11/04/1994 JLB : Sort value is different when unloading from aircraft. * + *=============================================================================================*/ +COORDINATE FootClass::Sort_Y(void) const +{ + if (IsUnloading) { + return(Coord_Add(Coord, 0x01000000L)); + } + if (In_Radio_Contact() && IsTethered && Contact_With_Whom()->What_Am_I() == RTTI_UNIT) { + return(Coord_Add(Coord, 0x01000000L)); + } + return(Coord_Add(Coord, 0x00300000L)); +} + + +/*********************************************************************************************** + * FootClass::Stun -- Prepares a ground travelling object for removal. * + * * + * This routine clears the units' navigation computer in preparation for removal from the * + * game. This is probably called as a result of unit destruction in combat. Clearing the * + * navigation computer ensures that the normal AI process won't start it moving again while * + * the object is undergoing any death animations. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +void FootClass::Stun(void) +{ + Assign_Destination(TARGET_NONE); + Path[0] = FACING_NONE; + Stop_Driver(); + TechnoClass::Stun(); +} + + +/*********************************************************************************************** + * FootClass::Approach_Target -- Sets the navigation computer to approach target object. * + * * + * This routine will set the navigation computer to approach the target indicated by the * + * targeting computer. It is through this function that the unit nears the target so * + * that weapon firing may occur. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 12/13/1994 JLB : Made part of TechnoClass. * + * 12/22/1994 JLB : Enhanced search algorithm. * + * 05/20/1995 JLB : Always approaches if the object is off the map. * + *=============================================================================================*/ +void FootClass::Approach_Target(void) +{ + /* + ** Determine that if there is an existing target it is still legal + ** and within range. + */ + if (Target_Legal(TarCom)) { + + /* + ** If the target is too far away then head toward it. + */ + int maxrange = MAX(Weapon_Range(0), Weapon_Range(1)); + + if (!Target_Legal(NavCom) && (!In_Range(TarCom) || !IsLocked)) { +// if (!Target_Legal(NavCom) && (Distance(TarCom) > maxrange || !IsLocked)) { + + /* + ** If the object that we are attacking is a building adjust the units + ** max range so that people can stand far away from the buildings and + ** hit them. + */ + BuildingClass * obj = As_Building(TarCom); + if (obj) { + maxrange += ((obj->Class->Width() + obj->Class->Height()) * (0x100 / 4)); + } + + /* + ** Adjust the max range of an infantry unit for where he is standing + ** in the room. + */ + maxrange -= 0x00B7; +#ifdef OBSOLETE + if (What_Am_I() == RTTI_INFANTRY) { + maxrange -= 0x0111; + } else { + maxrange -= 0x00B7; + } +#endif + maxrange = MAX(maxrange, 0); + + COORDINATE tcoord = ::As_Coord(TarCom); + COORDINATE trycoord = 0; + CELL tcell = Coord_Cell(tcoord); + CELL trycell = tcell; + DirType dir = Direction256(tcoord, Center_Coord()); + bool found = false; + + /* + ** Sweep through the cells between the target and the unit, looking for + ** a cell that the unit can enter but which is also within weapon range + ** of the target. If after a reasonable search, no appropriate cell could + ** be found, then the target will be assigned as the movement destination + ** and let "the chips fall where they may." + */ + for (int range = maxrange; range > 0x0080; range -= 0x0100) { + static int _angles[] = {0, 8, -8, 16, -16, 24, -24, 32, -32, 48, -48, 64, -64}; + + for (int index = 0; index < (sizeof(_angles)/sizeof(_angles[0])); index++) { + trycoord = Coord_Move(tcoord, (DirType)(dir + _angles[index]), range); + + if (::Distance(trycoord, tcoord) < range) { + trycell = Coord_Cell(trycoord); + if (Can_Enter_Cell(trycell) <= MOVE_CLOAK && Map.In_Radar(trycell)) { + found = true; + break; + } + } + } + if (found) break; + } + + /* + ** If a suitable intermediate location was found, then head toward it. + ** Otherwise, head toward the enemy unit directly. + */ + if (found) { + Assign_Destination(::As_Target(trycell)); + } else { + Assign_Destination(TarCom); + } + } + } +} + + +/*********************************************************************************************** + * FootClass::Mission_Guard_Area -- Causes unit to guard an area about twice weapon range. * + * * + * This mission routine causes the unit to scan for targets out to twice its weapon range * + * from the home point. If a target was found, then it will be attacked. The unit will * + * chase the target until it gets up to to its weapon range from the home position. * + * In that case, it will return to home position and start scanning for another target. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with time delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + * 07/27/1995 JLB : Greatly simplified. * + *=============================================================================================*/ +int FootClass::Mission_Guard_Area(void) +{ + if (What_Am_I() == RTTI_UNIT && ((UnitClass *)this)->Class->IsToHarvest) { + Assign_Mission(MISSION_HARVEST); + return(1+Random_Pick(1, 10)); + } + + /* + ** Ensure that the archive target is valid. + */ + if (!Target_Legal(ArchiveTarget)) { + ArchiveTarget = ::As_Target(Coord_Cell(Coord)); + } + + /* + ** Make sure that the unit has not strayed too far from the home position. + ** If it has, then race back to it. + */ + int maxrange = MAX(Weapon_Range(0), Weapon_Range(1))+0x0100; + if (!Target_Legal(NavCom) && (Distance(ArchiveTarget) > maxrange || (!Target_Legal(TarCom) && Distance(ArchiveTarget) > 0x0200))) { + Assign_Target(TARGET_NONE); + Assign_Destination(ArchiveTarget); + } + + if (!Target_Legal(TarCom)) { + COORDINATE old = Coord; + Coord = As_Coord(ArchiveTarget); + Target_Something_Nearby(THREAT_AREA); + Coord = old; + if (Target_Legal(TarCom)) return(1); + } else { + Approach_Target(); + } + return(TICKS_PER_SECOND+Random_Picky((int)0, (int)4, (char*)NULL, (int)0)); +} + + +/*********************************************************************************************** + * FootClass::Unlimbo -- Unlimbos object and performs special fixups. * + * * + * This routine will make sure that the home position for the foot class object gets * + * reset. This is necessary since the home position may change depending on the unit's * + * transition between limbo and non-limbo condition. * + * * + * INPUT: coord -- The coordinate to unlimbo the unit at. * + * * + * dir -- The initial direction to give the unit. * + * * + * OUTPUT: bool; Was the unit unlimboed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Unlimbo(COORDINATE coord, DirType dir) +{ + /* + ** Try to unlimbo the unit. + */ + if (TechnoClass::Unlimbo(coord, dir)) { + + /* + ** Mobile units are always revealed to the house that owns them. + */ + Revealed(House); + + /* + ** Start in a still (non-moving) state. + */ + Path[0] = FACING_NONE; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Assign_Mission -- Assign mission to foot class object. * + * * + * When a new mission is assigned, any precalculated path should be truncated. This is * + * in anticipation that the new mission will result in a change of path. * + * * + * INPUT: order -- The new mission to assign to the unit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +void FootClass::Assign_Mission(MissionType order) +{ + if (What_Am_I() != RTTI_UNIT || *(UnitClass*)this != UNIT_GUNBOAT) { + Path[0] = FACING_NONE; + } + TechnoClass::Assign_Mission(order); +} + + +/*********************************************************************************************** + * FootClass::Limbo -- Intercepts limbo event and handles FootClass processing. * + * * + * When an object of FootClass type is limboed, then it must be removed from any team * + * it may be a member of. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the object successfully limboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Limbo(void) +{ + if (!IsInLimbo) { + if (Team) { + Team->Remove(this); + } + } + return(TechnoClass::Limbo()); +} + + +/*********************************************************************************************** + * FootClass::Take_Damage -- Handles taking damage to this object. * + * * + * This routine intercepts the damage assigned to this object and if this object is * + * a member of a team, it informs the team that the damage has occurred. The team may * + * change it's priority or action based on this event. * + * * + * INPUT: damage -- The damage points inflicted on the unit. * + * * + * distance -- The distance from the point of damage to the unit itself. * + * * + * warhead -- The type of damage that is inflicted. * + * * + * source -- The purpitrator of the damage. By knowing who caused the damage, * + * the team know's who to "get even with". * + * * + * OUTPUT: Returns with the result type of the damage. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/30/1994 JLB : Created. * + *=============================================================================================*/ +ResultType FootClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source) +{ + ResultType result = TechnoClass::Take_Damage(damage, distance, warhead, source); + + if (result != RESULT_NONE && Team) { + + if (GameToPlay != GAME_NORMAL || (source && !House->Is_Ally(source))) { + Team->Took_Damage(this, result, source); + } + + } else { + + if (result != RESULT_DESTROYED) { + /* + ** Determine if the target that is current being attacked has a weapon that can + ** do harm to a ground based unit. This information is needed so that an appropriate + ** response will occur when damage is taken. + */ + WeaponType weap = WEAPON_NONE; + if (As_Techno(TarCom)) { + weap = As_Techno(TarCom)->Techno_Type_Class()->Primary; + } + bool tweap = (weap != WEAPON_NONE && weap != WEAPON_NIKE); + + /* + ** This ensures that if a unit is in sticky mode, then it will snap out of + ** it when it takes damage. + */ + if (source && Mission == MISSION_STICKY) { + Enter_Idle_Mode(); + } + + /* + ** If this object is not part of a team and it can retaliate for the damage, then have + ** it try to do so. This prevents it from just sitting there and taking damage. + */ + if ( + source && + !House->Is_Ally(source) && + Techno_Type_Class()->Primary != WEAPON_NONE && + (source->What_Am_I() != RTTI_AIRCRAFT || BulletTypeClass::As_Reference(Weapons[Techno_Type_Class()->Primary].Fires).IsAntiAircraft) && + (!Target_Legal(TarCom) || ((!House->IsHuman || Special.IsSmartDefense) && (!tweap || !In_Range(TarCom)))) && +// !Target_Legal(NavCom) && + (Mission == MISSION_AMBUSH || + Mission == MISSION_GUARD || + Mission == MISSION_RESCUE || + Mission == MISSION_GUARD_AREA || + Mission == MISSION_ATTACK || + Mission == MISSION_TIMED_HUNT)) { + + /* + ** Assign the source of the damage as the new target. This occurs for the computer + ** controled units. For the player, this only occurs if the source of the damage + ** is within range. + */ + if (!House->IsHuman) { + + /* + ** If this unit is in TIMED_HUNT (multiplayer computer-controlled) + ** mode, "snap out of it" into HUNT mode; otherwise, assign + ** HUNT as the next mission through the normal mission queue. + */ + if (Mission == MISSION_TIMED_HUNT) { + Set_Mission(MISSION_HUNT); + } else { + Assign_Mission(MISSION_HUNT); + } + Assign_Target(source->As_Target()); + } else { + if (In_Range(source)) { + Assign_Target(source->As_Target()); + } else { + + /* + ** Simple retaliation cannot occur because the source of the damage + ** is too far away. If scatter logic is enabled, then scatter now. + */ + if (Special.IsScatter) { + Scatter(0, true); + } + } + } + } else { + + /* + ** If this object isn't doing anything important, then scatter. + */ + if (!IsDriving && !Target_Legal(TarCom) && !Target_Legal(NavCom) && Special.IsScatter && What_Am_I() != RTTI_AIRCRAFT) { + Scatter(0, true); + } + } + } + } + return(result); +} + + +/*********************************************************************************************** + * FootClass::Active_Click_With -- Intiates attack or move according to target clicked on. * + * * + * At this level, the object is known to have the ability to attack or move to the * + * target specified (in theory). Perform the attack or move as indicated. * + * * + * INPUT: target -- The target clicked upon that will precipitate action. * + * * + * OUTPUT: Returns with the type of action performed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/06/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + switch (action) { + case ACTION_GUARD_AREA: + if (Can_Player_Fire() && Can_Player_Move()) { + Player_Assign_Mission(MISSION_GUARD_AREA, object->As_Target()); + } + break; + + case ACTION_SELF: + Player_Assign_Mission(MISSION_UNLOAD); + break; + + case ACTION_ATTACK: + if (Can_Player_Fire()) { + Player_Assign_Mission(MISSION_ATTACK, object->As_Target()); + } + break; + + case ACTION_ENTER: + if (Can_Player_Move() && object && object->Is_Techno() && !((RadioClass *)object)->In_Radio_Contact()) { + Player_Assign_Mission(MISSION_ENTER, TARGET_NONE, object->As_Target()); + } + break; + + case ACTION_CAPTURE: + if (Can_Player_Move()) { + Player_Assign_Mission(MISSION_CAPTURE, TARGET_NONE, object->As_Target()); + } + break; + + case ACTION_SABOTAGE: + if (Can_Player_Move()) { + Player_Assign_Mission(MISSION_SABOTAGE, TARGET_NONE, object->As_Target()); + } + break; + + case ACTION_NOMOVE: + case ACTION_MOVE: + if (Can_Player_Move()) { + Player_Assign_Mission(MISSION_MOVE, TARGET_NONE, object->As_Target()); + } + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * FootClass::Active_Click_With -- Performs action as a result of left mouse click. * + * * + * This routine performs the action requested when the left mouse button was clicked over * + * a cell. Typically, this is just a move command. * + * * + * INPUT: action -- The predetermined action that should occur. * + * * + * cell -- The cell number that the action should occur at. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Active_Click_With(ActionType action, CELL cell) +{ + switch (action) { + case ACTION_HARVEST: + Player_Assign_Mission(MISSION_HARVEST, TARGET_NONE, ::As_Target(cell)); + break; + + case ACTION_MOVE: + if (AllowVoice) { + COORDINATE coord = Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()); + OutList.Add(EventClass(ANIM_MOVE_FLASH, PlayerPtr->Class->House, coord)); + } + // Fall into next case. + + case ACTION_NOMOVE: + if (What_Am_I() != RTTI_AIRCRAFT || Map[cell].IsVisible) { + Player_Assign_Mission(MISSION_MOVE, TARGET_NONE, ::As_Target(cell)); + } + break; + + case ACTION_ATTACK: + Player_Assign_Mission(MISSION_ATTACK, ::As_Target(cell)); + break; + } +} + + +/*********************************************************************************************** + * FootClass::Per_Cell_Process -- Perform action based on once-per-cell condition. * + * * + * This routine is called as this object moves from cell to cell. When the center of the * + * cell is reached, check to see if any trigger should be sprung. For moving units, reduce * + * the path to the distance to the target. This forces path recalculation in an effort to * + * avoid units passing each other. * + * * + * INPUT: center -- Is this the center of the cell? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 07/08/1995 JLB : Handles generic enter trigger event. * + * 07/16/1995 JLB : If next to a scanner and cloaked, then shimmer. * + *=============================================================================================*/ +void FootClass::Per_Cell_Process(bool center) +{ +// if (center) { + + /* + ** Clear any unloading flag if necessary. + */ + IsUnloading = false; + + /* + ** If adjacent to an enemy building that has the ability to reveal a stealth tank, + ** then shimmer the cloaked object. + */ + if (Cloak == CLOAKED) { + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + CELL cell = Adjacent_Cell(Coord_Cell(Coord), face); + + if (Map.In_Radar(cell)) { + TechnoClass const * techno = Map[cell].Cell_Techno(); + + if (techno && !House->Is_Ally(techno) && techno->Techno_Type_Class()->IsScanner) { + Do_Shimmer(); + break; + } + } + } + } + + /* + ** Shorten the path if the target is now within weapon range of this + ** unit and this unit is on an attack type mission. + */ + if (What_Am_I() != RTTI_UNIT || *((UnitClass *)this) != UNIT_GUNBOAT) { + bool inrange = In_Range(TarCom); + TechnoClass const * techno = As_Techno(TarCom); + if (techno && techno->What_Am_I() != RTTI_BUILDING) { + inrange = In_Range(((FootClass const *)techno)->Likely_Coord()); + } + + if (Target_Legal(TarCom) && (Mission == MISSION_RESCUE || Mission == MISSION_GUARD_AREA || Mission == MISSION_ATTACK || Mission == MISSION_HUNT) && inrange) { + Assign_Destination(TARGET_NONE); + Path[0] = FACING_NONE; + } + } + + /* + ** Trigger event associated with the player entering the cell. + */ + TriggerClass * trigger = Map[Coord_Cell(Coord)].Get_Trigger(); + if (Cloak != CLOAKED && trigger && trigger->House == Owner()) { + trigger->Spring(EVENT_PLAYER_ENTERED, Coord_Cell(Coord)); + } +// } + + TechnoClass::Per_Cell_Process(center); +} + + +/*************************************************************************** + * FootClass::Override_Mission -- temporarily overides a units mission * + * * + * * + * * + * INPUT: MissionType mission - the mission we want to overide * + * TARGET tarcom - the new target we want to overide * + * TARGET navcom - the new navigation point to overide * + * * + * OUTPUT: none * + * * + * WARNINGS: If a mission is already overidden, the current mission is * + * just re-assigned. * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=========================================================================*/ +void FootClass::Override_Mission(MissionType mission, TARGET tarcom, TARGET navcom) +{ + SuspendedNavCom = NavCom; + TechnoClass::Override_Mission(mission, tarcom, navcom); + + Assign_Destination(navcom); +} + + +/*************************************************************************** + * FootClass::Restore_Mission -- Restores an overidden mission * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=========================================================================*/ +bool FootClass::Restore_Mission(void) +{ + if (TechnoClass::Restore_Mission()) { + Assign_Destination(SuspendedNavCom); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Receive_Message -- Movement related radio messages are handled here. * + * * + * This routine handles radio message that are related to movement. These are used for * + * complex coordinated maneuvers. * + * * + * INPUT: from -- Pointer to the originator of this radio message. * + * * + * message -- The radio message that is being received. * + * * + * param -- The optional parameter (could be a movement destination). * + * * + * OUTPUT: Returns with the radio response appropriate to the message received. Usually the * + * response is RADIO_ROGER. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1995 JLB : Created. * + *=============================================================================================*/ +RadioMessageType FootClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + switch (message) { + + /* + ** Intercept the repair request and if this object is moving, then no repair + ** is possible. + */ + case RADIO_REPAIR: + if (Target_Legal(NavCom)) return(RADIO_NEGATIVE); + break; + + /* + ** Something bad has happened to the object in contact with. Abort any coordinated + ** activity with this object. Basically, ... run away! Run away! + */ + case RADIO_RUN_AWAY: + if (In_Radio_Contact()) { + if (NavCom == Contact_With_Whom()->As_Target()) { + Assign_Destination(TARGET_NONE); + } + } + break; + + /* + ** Checks to see if this unit needs to move somewhere. If it is already in motion, + ** then it doesn't need furthur movement instructions. + */ + case RADIO_NEED_TO_MOVE: + param = (long)NavCom; + if (!Target_Legal(NavCom)) { + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + + /* + ** Radio request to move to location specified. Typically this is used + ** for complex loading and unloading missions. + */ + case RADIO_MOVE_HERE: + if (NavCom != (TARGET)param) { + if (::As_Target(Coord_Cell(Coord)) == (TARGET)param) { + return(RADIO_YEA_NOW_WHAT); + } else { + if (Mission == MISSION_GUARD && MissionQueue == MISSION_NONE) { + Assign_Mission(MISSION_MOVE); + } + Assign_Destination((TARGET)param); + } + } + return(RADIO_ROGER); + + /* + ** Requests if this unit is trying to cooperatively load up. Typically, this occurs + ** for passengers and when vehicles need to be repaired. + */ + case RADIO_TRYING_TO_LOAD: + if (Mission == MISSION_ENTER || MissionQueue == MISSION_ENTER) { + TechnoClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + } + break; + } + return(TechnoClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * FootClass::Mission_Enter -- Enter (cooperatively) mission handler. * + * * + * This mission handler will cooperatively coordinate the object to maneuver into the * + * object it is in radio contact with. This is used by infantry when they wish to load * + * into an APC as well as by vehicles when they wish to enter a repair facility. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of game ticks before this routine should be called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/15/1995 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Enter(void) +{ + /* + ** If radio contact has not yet been established with the transport, try to + ** establish contact now. + */ + if (!In_Radio_Contact()) { + TechnoClass * techno = As_Techno(ArchiveTarget); + if (!techno) techno = As_Techno(NavCom); + if (techno) { + + /* + ** If the transport is already in radio contact, do nothing. Try to + ** establish radio contact later. + */ + if (Transmit_Message(RADIO_HELLO, techno) != RADIO_ROGER) { + ArchiveTarget = TARGET_NONE; + Enter_Idle_Mode(); + } else { + Assign_Destination(TARGET_NONE); + } + } else { + ArchiveTarget = TARGET_NONE; + Enter_Idle_Mode(); + } + + } else { + + /* + ** Since radio contact exists with the transport, maintain a dialogue so that + ** the transport can give proper instructions to the passenger. + */ + if (Transmit_Message(RADIO_DOCKING) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT); + Enter_Idle_Mode(); + } + } + return(TICKS_PER_SECOND/2); +} + + +/*********************************************************************************************** +z * FootClass::Assign_Destination -- Assigns specified destination to NavCom. * + * * + * This routine will assign the specified target to the navigation computer. No legality * + * checks are performed. * + * * + * INPUT: target -- The target value to assign to the navigation computer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Assign_Destination(TARGET target) +{ + NavCom = target; +} + + +/*********************************************************************************************** + * FootClass::Detach_All -- Removes this object from the game system. * + * * + * This routine will remove this object from the game system. This routine is called when * + * this object is about to be deleted. All other objects should no longer reference this * + * object in that case. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Detach_All(bool all) +{ + if (Team) Team->Remove(this); + Team = NULL; + + TechnoClass::Detach_All(all); +} + + +/*********************************************************************************************** + * FootClass::Rescue_Mission -- Calls this unit to the rescue. * + * * + * This routine is called when the house determines that it should attack the specified * + * target. This routine will determine if it can attack the target specified and if so, * + * the amount of power it can throw at it. This returned power value is used to allow * + * intelligent distribution of retaliation. * + * * + * INPUT: target -- The target that this object just might be assigned to attack and thus * + * how much power it can bring to bear should be returned. * + * * + * OUTPUT: Returns with the amount of power that this object can bring to bear against the * + * potential target specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +int FootClass::Rescue_Mission(TARGET tarcom) +{ + /* + ** If the target specified is not legal, then it cannot be attacked. Always return + ** zero in this case. + */ + if (!Target_Legal(tarcom)) return(0); + + /* + ** If the unit is already assigned to destroy the tarcom then we need + ** to return a negative value which tells the computer to lower the + ** desired threat rating. + */ + if (TarCom == tarcom) { + return(-Risk()); + } + + /* + ** If the unit is currently attacking a target that has a weapon then we + ** cannot abandon it as it will destroy us if we return to base. + */ + if (Target_Legal(TarCom)) { + TechnoClass * techno = As_Techno(TarCom); + if (techno && techno->Techno_Type_Class()->Primary != WEAPON_NONE) { + return(0); + } + } + + /* + ** If the unit is in a harvest mission or is currently attacking + ** something, or is not very effective, then it will be of no help + ** at all. + */ + if (Team || Mission == MISSION_HARVEST || !Risk()) { + return(0); + } + + /* + ** Find the distance to the target modified by the range. If the + ** the distance is 0, then things are ok. + */ + int dist = Distance(tarcom) - Weapon_Range(0); + int threat = Risk() * 256; + int speed = -1; + if (dist > 0) { + + /* + ** Next we need to figure out how fast the unit moves because this + ** decreases the distance penalty. + */ + speed = Max((unsigned)Techno_Type_Class()->MaxSpeed, (unsigned)1); + + int ratio = (speed > 0) ? Max(dist / speed, 1) : 1; + + /* + ** Finally modify the threat by the distance the unit is away. + */ + threat = Max(threat/ratio, 1); + } + return(threat); +} + + +/*********************************************************************************************** + * FootClass::Death_Announcement -- Announces the death of a unit. * + * * + * This routine is called when a unit (infantry, vehicle, or aircraft) is destroyed. * + * * + * INPUT: source -- The purpetrator of this death. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Death_Announcement(TechnoClass const * source) const +{ + if (IsDiscoveredByPlayer || IsOwnedByPlayer) { + if (!source || source->What_Am_I() != RTTI_INFANTRY || *((InfantryClass const *)source) != INFANTRY_RAMBO) { + if (What_Am_I() == RTTI_INFANTRY && ((InfantryTypeClass const &)Class_Of()).IsCivilian && !((InfantryClass *)this)->IsTechnician) { + if (Options.IsDeathAnnounce) Speak(VOX_DEAD_CIV); + } else { + if (House != PlayerPtr && GameToPlay != GAME_NORMAL) { + if (Options.IsDeathAnnounce) Speak(VOX_ENEMY_UNIT); + } else { + if (House == PlayerPtr || Options.IsDeathAnnounce) { + if (!Options.IsDeathAnnounce) { + Speak(VOX_UNIT_LOST); + } else { + switch (House->ActLike) { + case HOUSE_GOOD: + Speak(VOX_DEAD_GDI); + break; + + case HOUSE_BAD: + Speak(VOX_DEAD_NOD); + break; + + default: + break; + } + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * FootClass::Greatest_Threat -- Fetches the greatest threat to this object. * + * * + * This routine will return with the greatest threat (best target) for this object. For * + * movable ground object, they won't automatically return ANY target if this object is * + * cloaked. Otherwise, cloaking is relatively useless. * + * * + * INPUT: method -- The request method (bit flags) to use when scanning for a target. * + * * + * OUTPUT: Returns with the best target to attack. If there is no target that qualifies, then * + * TARGET_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +TARGET FootClass::Greatest_Threat(ThreatType method) const +{ + /* + ** If this object can cloak, then it won't select a target automatically. + */ + if (House->IsHuman && IsCloakable && Mission == MISSION_GUARD) { + return(TARGET_NONE); + } + + if (Techno_Type_Class()->Primary != WEAPON_NONE && BulletTypeClass::As_Reference(Weapons[Techno_Type_Class()->Primary].Fires).IsAntiAircraft) { + method = method | THREAT_AIR; + } + if (Techno_Type_Class()->Secondary != WEAPON_NONE && BulletTypeClass::As_Reference(Weapons[Techno_Type_Class()->Secondary].Fires).IsAntiAircraft) { + method = method | THREAT_AIR; + } + + return(TechnoClass::Greatest_Threat(method|THREAT_GROUND)); +} + + +/*********************************************************************************************** + * FootClass::Detach -- Detaches a target from tracking systems. * + * * + * This routine will detach the specified target from the tracking systems of this object. * + * It will be removed from the navigation computer and any queued mission record. * + * * + * INPUT: target -- The target to be removed from this object. * + * * + * all -- Is the unit really about to be eliminated? If this is true then even * + * friendly contact (i.e., radio) must be eliminated. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Detach(TARGET target, bool all) +{ + TechnoClass::Detach(target, all); + + if (!SpecialFlag) { + if (ArchiveTarget == target) { + ArchiveTarget = TARGET_NONE; + } + } + + if (SuspendedNavCom == target) { + SuspendedNavCom = TARGET_NONE; + SuspendedMission = MISSION_NONE; + } + + /* + ** If the navigation computer is assigned to the target, then the navigation + ** computer must be cleared. + */ + if (NavCom == target) { + NavCom = TARGET_NONE; + Path[0] = FACING_NONE; + Restore_Mission(); + } + + /* + ** If targeting the specified object and this unit is obviously heading + ** toward the target to get within range, then abort the path. + */ + if (TarCom == target && House->IsHuman) { + Path[0] = FACING_NONE; + } +} + + +/*********************************************************************************************** + * FootClass::Offload_Tiberium_Bail -- Fetches the Tiberium to offload per step. * + * * + * This routine is called when a packet/package/bail of Tiberium needs to be offloaded * + * from the object. This function is overridden for those objects that can contain * + * Tiberium. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of credits offloaded from the object. * + * * + * WARNINGS: This routine must be called multiple times in order to completely offload the * + * Tiberium. When this routine return 0, all Tiberium has been offloaded. * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +int FootClass::Offload_Tiberium_Bail(void) +{ + return(0); +} + + +/*********************************************************************************************** + * FootClass::Can_Enter_Cell -- Checks to see if the object can enter cell specified. * + * * + * This routine examines the specified cell to see if the object can enter it. This * + * function is to be overridden for objects that could have the possibility of not being * + * allowed to enter the cell. Typical objects at the FootClass level always return * + * MOVE_OK. * + * * + * INPUT: cell -- The cell to examine. * + * * + * facing -- The direction that this cell might be entered from. * + * * + * OUTPUT: Returns with the move check result type. This will be MOVE_OK if there is not * + * blockage. There are various other values that represent other blockage types. * + * The value returned will indicatd the most severe reason why entry into the cell * + * is blocked. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +MoveType FootClass::Can_Enter_Cell(CELL , FacingType) const +{ + return MOVE_OK; +} + + +/*********************************************************************************************** + * FootClass::Can_Demolish -- Checks to see if this object can be sold back. * + * * + * This routine determines if it is legal to sell the object back. A foot class object can * + * only be sold back if it is sitting on a repair bay. * + * * + * INPUT: none * + * * + * OUTPUT: Was the object successfully sold back? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Can_Demolish(void) const +{ + switch (What_Am_I()) { + case RTTI_UNIT: + case RTTI_AIRCRAFT: + if (In_Radio_Contact() && + Contact_With_Whom()->What_Am_I() == RTTI_BUILDING && + *((BuildingClass *)Contact_With_Whom()) == STRUCT_REPAIR && + Distance(Contact_With_Whom()) < 0x0080) { + + return(true); + } + break; + + default: + break; + } + return(TechnoClass::Can_Demolish()); +} + + +/*********************************************************************************************** + * FootClass::Sell_Back -- Causes this object to be sold back. * + * * + * When an object is sold back, a certain amount of money is refunded to the owner and then * + * the object is removed from the game system. * + * * + * INPUT: control -- The action to perform. The only supported action is "1", which means * + * to sell back. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Sell_Back(int control) +{ + if (control != 0) { + if (House == PlayerPtr) { + Sound_Effect(VOC_CASHTURN); + } + House->Refund_Money(Refund_Amount()); + Stun(); + Limbo(); + delete this; + } +} + + +/*********************************************************************************************** + * FootClass::Likely_Coord -- Fetches the coordinate the object will be at shortly. * + * * + * This routine comes in handy when determining where a travelling object will be at * + * when considering the amount of time it would take for a normal unit to travel one cell. * + * Using this information, an intelligent "approach target" logic can be employed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate the object is at or soon will be. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE FootClass::Likely_Coord(void) const +{ + if (Head_To_Coord()) { + return(Head_To_Coord()); + } + return(Target_Coord()); +} diff --git a/FOOT.H b/FOOT.H new file mode 100644 index 0000000..9069df3 --- /dev/null +++ b/FOOT.H @@ -0,0 +1,311 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\foot.h_v 2.20 16 Oct 1995 16:47:14 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FOOT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FOOT_H +#define FOOT_H + +#include "target.h" +#include "type.h" +#include "techno.h" +#include "ftimer.h" + +class UnitClass; +class BuildingClass; + + +/**************************************************************************** +** Movable objects are handled by this class definition. Moveable objects +** cover everything except buildings. +*/ +class FootClass : public TechnoClass +{ + public: + /* + ** If this unit has officially joined the team's group, then this flag is + ** true. A newly assigned unit to a team is not considered part of the + ** team until it actually reaches the location where the team is. By + ** using this flag, it allows a team to continue to intelligently attack + ** a target without falling back to regroup the moment a distant member + ** joins. + */ + unsigned IsInitiated:1; + + /* + ** When the player gives this object a navigation target AND that target + ** does not result in any movement of the unit, then a beep should be + ** sounded. This typically occurs when selecting an invalid location for + ** movement. This flag is cleared if any movement was able to be performed. + ** It never gets set for computer controlled units. + */ + unsigned IsNewNavCom:1; + + /* + ** There are certain cases where a unit should perform a full scan rather than + ** the more efficient "ring scan". This situation occurs when a unit first + ** appears on the map or when it finishes a multiple cell movement track. + */ + unsigned IsPlanningToLook:1; + + /* + ** Certain units have the ability to metamorphize into a building. When this + ** operation begins, certain processes must occur. During these operations, this + ** flag will be true. This ensures that any necessary special case code gets + ** properly executed for this unit. + */ + unsigned IsDeploying:1; + + /* + ** This flag tells the system that the unit is doing a firing animation. This is + ** critical to the firing logic. + */ + unsigned IsFiring:1; + + /* + ** This unit could be either rotating its body or rotating its turret. During the + ** process of rotation, this flag is set. By examining this flag, unnecessary logic + ** can be avoided. + */ + unsigned IsRotating:1; + + /* + ** If this object is current driving to a short range destination, this flag is + ** true. A short range destination is either the next cell or the end of the + ** current "curvy" track. An object that is driving is not allowed to do anything + ** else until it reaches its destination. The exception is when infantry wish to + ** head to a different destination, they are allowed to start immediately. + */ + unsigned IsDriving:1; + + /* + ** If this object is unloading from a hover transport, then this flag will be + ** set to true. This handles the unusual case of an object disembarking from the + ** hover lander yet not necessarily tethered but still located in an overlapping + ** position. This flag will be cleared automatically when the object moves to the + ** center of a cell. + */ + unsigned IsUnloading:1; + + /* + ** This is the "throttle setting" of the unit. It is a fractional value with 0 = stop + ** and 255 = full speed. + */ + unsigned char const Speed; + + /* + ** For units in area guard mode, this is the recorded home position. The guarding + ** unit will try to stay near this location in the course of it's maneuvers. This is + ** also used to record a pending transport for those passengers that are waiting for + ** the transport to become available. It is also used by harvesters so that they know + ** where to head back to after unloading. + */ + TARGET ArchiveTarget; + + /* + ** + ** This is the desired destination of the unit. The unit will attempt to head + ** toward this target (avoiding intervening obstacles). + */ + TARGET NavCom; + TARGET SuspendedNavCom; + + /* + ** This points to the team that "owns" this object. This pointer is used to + ** quickly process the team when this object is the source of the change. An + ** example would be if this object were to be destroyed, it would inform the + ** team of this fact by using this pointer. + */ + TeamClass * Team; + + /* + ** If this object is part of a pseudo-team that the player is managing, then + ** this will be set to the team number (0 - 9). If it is not part of any + ** pseudo-team, then the number will be -1. + */ + unsigned char Group; + + /* + ** This points to the next member in the team that this object is part of. This + ** is used to quickly process each team member when the team class is the source + ** of the change. An example would be if the team decided that everyone is going + ** to move to a new location, it would inform each of the objects by chaining + ** through this pointer. + */ + FootClass * Member; + + /* + ** Since all objects derived from this class move according to a path list. + ** This is the path list. It specifies, as a simple list of facings, the + ** path that the object should follow in order to reach its destination. + ** This path list is limited in size, so it might require several generations + ** of path lists before the ultimate destination is reached. The game logic + ** handles regenerating the path list as necessary. + */ + FacingType Path[CONQUER_PATH_MAX]; + + /* + ** When there is a complete findpath failure, this timer is initialized so + ** that a findpath won't be calculated until this timer expires. + */ + TCountDownTimerClass PathDelay; + enum {PATH_DELAY=15,PATH_RETRY=10}; + int TryTryAgain; // Number of retry attempts remaining. + + /* + ** If the object has recently attacked a base, then this timer will not + ** have expired yet. It is used so a building does not keep calling + ** for help from the same attacker. + */ + TCountDownTimerClass BaseAttackTimer; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + FootClass(void); + virtual ~FootClass(void); + FootClass(HousesType house); + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + bool Basic_Path(void); + + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual bool Can_Demolish(void) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Sort_Y(void) const; + virtual COORDINATE Likely_Coord(void) const; + + /* + ** Driver control support functions. These are used to control cell + ** occupation flags and driver instructions. + */ + COORDINATE Head_To_Coord(void) const {return (HeadToCoord);}; + virtual bool Start_Driver(COORDINATE &headto); + virtual bool Stop_Driver(void); + virtual void Assign_Destination(TARGET target); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual bool Unlimbo(COORDINATE , DirType dir = DIR_N); + virtual bool Limbo(void); + virtual bool Mark(MarkType mark); + + /* + ** User I/O. + */ + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual void Active_Click_With(ActionType action, CELL cell); + + /* + ** Combat related. + */ + virtual void Stun(void); + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0); + virtual void Death_Announcement(TechnoClass const * source=0) const; + + /* + ** AI. + */ + virtual void Sell_Back(int control); + virtual int Offload_Tiberium_Bail(void); + virtual TARGET Greatest_Threat(ThreatType method) const; + virtual void Detach(TARGET target, bool all); + virtual void Detach_All(bool all=true); + virtual void Assign_Mission(MissionType order); + virtual int Mission_Enter(void); + virtual int Mission_Move(void); + virtual int Mission_Capture(void); + virtual int Mission_Attack(void); + virtual int Mission_Guard(void); + virtual int Mission_Hunt(void); + virtual int Mission_Timed_Hunt(void); + virtual int Mission_Guard_Area(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** Movement and animation. + */ + virtual void Per_Cell_Process(bool center); + virtual void Approach_Target(void); + virtual void Fixup_Path(PathType *) {}; + virtual void Set_Speed(int speed); + virtual MoveType Can_Enter_Cell(CELL , FacingType =FACING_NONE) const; + int Optimize_Moves(PathType *path, MoveType threshhold); + virtual void Override_Mission(MissionType mission, TARGET tarcom, TARGET navcom); + virtual bool Restore_Mission(void); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + CELL Safety_Point(CELL src, CELL dst, int start, int max); + int Rescue_Mission(TARGET tarcom); + + private: + int Passable_Cell(CELL cell, FacingType face, int threat, MoveType threshhold); + PathType * Find_Path(CELL dest, FacingType *final_moves, int maxlen, MoveType threshhold); + void Debug_Draw_Map(char *txt, CELL start, CELL dest, bool pause); + void Debug_Draw_Path(PathType *path); + bool Follow_Edge(CELL start, CELL target, PathType *path, FacingType search, FacingType olddir, int threat, int threat_stage, int max_cells, MoveType threshhold); + bool Register_Cell(PathType *path, CELL cell, FacingType dir, int cost, MoveType threshhold); + bool Unravel_Loop(PathType *path, CELL &cell, FacingType &dir, int sx, int sy, int dx, int dy, MoveType threshhold); + + /* + ** This is the coordinate that the unit is heading to + ** as an immediate destination. This coordinate is never further + ** than once cell (or track) from the unit's location. When this coordinate + ** is reached, then the next location in the path list becomes the + ** next HeadTo coordinate. + */ + COORDINATE HeadToCoord; +}; + +#endif diff --git a/FTIMER.H b/FTIMER.H new file mode 100644 index 0000000..641a457 --- /dev/null +++ b/FTIMER.H @@ -0,0 +1,91 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\ftimer.h_v 2.14 16 Oct 1995 16:47:28 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FTIMER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/16/95 * + * * + * Last Update : March 16, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FTIMER_H +#define FTIMER_H + +/* +** This timer class is based around an external tick system. As such, it is inherently +** in sync with any connected system (through network or modem) that also keeps the external +** tick system in sync. The game frame number is a good sync value. +*/ +class TCountDownTimerClass { + public: + // Constructor. Timers set before low level init has been done will not + // be able to be 'Started' or 'on' until timer system is in place. + TCountDownTimerClass(long set=0) { + Set(set); + }; + + // No destructor. + ~TCountDownTimerClass(void) {} + + operator long(void) const {return Time();}; + + // Public functions + void Set(long set) { + Started = Frame; + DelayTime = set; + }; // Set count down value. + + void Clear(void) { + Started = -1; + DelayTime = 0; + }; + long Get_Start(void) const { + return(Started); + }; + long Get_Delay(void) const { + return(DelayTime); + }; + bool Active(void) const { + return(Started != -1); + }; + int Expired(void) const {return (Time() == 0);}; + long Time(void) const { + long remain = DelayTime - (Frame-Started); + if (remain < 0) remain = 0; + return(remain); + }; // Fetch current count down value. + + protected: + long Started; // Initial frame time start. + long DelayTime; // Ticks remaining before countdown timer expires. +}; + + +#endif diff --git a/FUNCTION.H b/FUNCTION.H new file mode 100644 index 0000000..9ec6791 --- /dev/null +++ b/FUNCTION.H @@ -0,0 +1,926 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\function.h_v 2.21 16 Oct 1995 16:46:44 JOE_BOSTIC $*/ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FUNCTION.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 27, 1994 * + * * + * Last Update : May 27, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FUNCTION_H +#define FUNCTION_H + +#ifdef NEVER +Map (screen) class heirarchy. + + MapeditClass (most derived class) -- scenario editor + ³ + MouseClass -- handles mouse animation and display control + ³ + ScrollClass -- map scroll handler + ³ + HelpClass -- pop-up help text handler + ³ + TabClass -- file folder tab screen mode control dispatcher + ³ + SidebarClass -- displays and controls construction list sidebar + ³ + PowerClass -- display power production/consumption bargraph + ³ + RadarClass -- displays and controls radar map + ³ + DisplayClass -- general tactical map display handler + ³ + MapClass -- general tactical map data handler + ³ + GScreenClass (pure virtual base class) -- generic screen control + + AbstractClass + ³ + ³ + ³ + ³ + ObjectClass + ³ + ÚÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄ¿ + AnimClass ³ TemplateClass ³ ÃÄ FuseClass ³ TerrainClass + ³ ³ ÃÄ FlyClass ³ + ³ ³ BulletClass ³ + OverlayClass MissionClass SmudgeClass + ³ + RadioClass + ³ + ÃÄ CrewClass + ÃÄ FlasherClass + ÃÄ StageClass + ÃÄ CargoClass + TechnoClass + ³ + ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + FootClass BuildingClass + ³ + ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + DriveClass InfantryClass ÃÄ FlyClass + ³ AircraftClass + TurretClass + ³ + TarComClass + ³ + UnitClass + + + AbstractTypeClass + ³ + ObjectTypeClass + ³ + ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ ³ ³ ³ + TechnoTypeClass ³ ³ ³ + ³ BulletTypeClass ³ ³ + ³ TemplateTypeClass ³ + ÚÄÄÄÄÄÄÄÄÁÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ TerrainTypeClass + ³ ³ ³ ³ +UnitTypeClass ³ BuildingTypeClass ³ + ³ InfantryTypeClass + AircraftTypeClass +#endif + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#define TRUE_FALSE_DEFINED +#endif //TRUE_FALSE_DEFINED + + +#define _WIN32 +#define WIN32 =1 //_LEAN_AND_MEAN +#include + + +/********************************************************************** +** If the following define is enabled, then the memory checking code +** will be disabled. +*/ +#define NOMEMCHECK + +#include "watcom.h" +#define FILE_H +#define WWMEM_H +#include "compat.h" +#include +#include "jshell.h" + + + +// Should be part of WWLIB.H. This is used in JSHELL.CPP. +typedef struct { + unsigned char SourceColor; + unsigned char DestColor; + unsigned char Fading; + unsigned char reserved; +} TLucentType; + + +// Don't complain if these headers aren't referenced. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* +** VQ player specific includes. +*/ +#include +#include + +extern bool GameActive; +extern long LParam; + +#include "vector.h" +#include "heap.h" +#include "ccfile.h" +#include "monoc.h" +#include "conquer.h" +//#include "debug.h" +#include "special.h" +#include "defines.h" + + +/* +** Greenleaf specific includes. +*/ +#include +#include + + +extern long Frame; +inline CELL Coord_XCell(COORDINATE coord) {return (CELL)(*(((unsigned char*)&coord)+1));} +inline CELL Coord_YCell(COORDINATE coord) {return (CELL)(*(((unsigned char*)&coord)+3));} +//inline CELL Coord_Cell(COORD coord){return (CELL)(((*(((unsigned short *)&coord)+1) & 0xFF00) >> 2) | *(((unsigned char *)&coord)+1));} +CELL Coord_Cell(COORDINATE coord); +#pragma aux Coord_Cell parm [eax] \ + modify [ebx] \ + value [ax] = \ + "mov ebx,eax" \ + "shr eax,010h" \ + "xor al,al" \ + "shr eax,2" \ + "or al,bh" + + +#include "utracker.h" +#include "facing.h" +#include "ftimer.h" +#include "theme.h" +#include "link.h" +#include "gadget.h" +#include "control.h" +#include "toggle.h" +#include "checkbox.h" +#include "shapebtn.h" +#include "textbtn.h" +#include "slider.h" +#include "list.h" +#include "cheklist.h" +#include "colrlist.h" +#include "edit.h" +#include "gauge.h" +#include "msgbox.h" +#include "dial8.h" +#include "txtlabel.h" +#include "super.h" +#include "house.h" +#include "gscreen.h" +#include "map.h" +#include "display.h" +#include "radar.h" +#include "power.h" +#include "sidebar.h" +#include "tab.h" +#include "help.h" +#include "mouse.h" +//#include "mapedit.h" +#include "help.h" +#include "target.h" +#include "theme.h" +#include "team.h" // Team objects. +#include "teamtype.h" // Team type objects. +#include "trigger.h" // Trigger event objects. +#include "mapedit.h" // ??? +#include "abstract.h" +#include "object.h" +#include "mission.h" +#include "door.h" +#include "bullet.h" // Bullet objects. +#include "terrain.h" // Terrain objects. +#include "anim.h" // Animation objects. +#include "template.h" // Icon template objects. +#include "overlay.h" // Overlay objects. +#include "smudge.h" // Stains on the terrain objects. +#include "aircraft.h" // Aircraft objects. +#include "unit.h" // Ground unit objects. +#include "infantry.h" // Infantry objects. +#include "credits.h" // Credit counter class. +#include "score.h" // Scoring system class. +#include "factory.h" // Production manager class. +#include "intro.h" +#include "ending.h" +#include "logic.h" +#include "queue.h" +#include "event.h" +#include "base.h" // defines the AI's pre-built base +#include "ipxmgr.h" +#include "combuf.h" +#include "connect.h" +#include "connmgr.h" +#include "noseqcon.h" +#include "msglist.h" +#include "nullconn.h" +#include "nullmgr.h" +#include "phone.h" +#include "loaddlg.h" +#include "ipxaddr.h" +/**************************************************************************** +** This is a "node", used for the lists of available games & players. The +** 'Game' structure is used for games; the 'Player' structure for players. +*/ +typedef struct NodeNameTag { + char Name[MPLAYER_NAME_MAX]; + IPXAddressClass Address; + union { + struct { + int Version; + unsigned char IsOpen; + unsigned long LastTime; + } Game; + struct { + HousesType House; + unsigned char Color; + } Player; + }; +} NodeNameType; + + +#include "externs.h" + + +extern int Get_CD_Drive(void); +extern void Fatal(char const *message, ...); + + +/* +** ANIM.CPP +*/ +void Shorten_Attached_Anims(ObjectClass * obj); + +/* +** AUDIO.CPP +*/ +int Sound_Effect(VocType voc, VolType volume, int variation=1, signed short panvalue=0); +void Speak(VoxType voice); +void Speak_AI(void); +void Stop_Speaking(void); +void Sound_Effect(VocType voc, COORDINATE coord=NULL, int variation=1); +bool Is_Speaking(void); + +/* +** COMBAT.CPP +*/ +int Modify_Damage(int damage, WarheadType warhead, ArmorType armor, int distance); +void Explosion_Damage(COORDINATE coord, unsigned strength, TechnoClass * source, WarheadType warhead); + +/* +** CONQUER.CPP +*/ +void Center_About_Objects(void); +bool Force_CD_Available(int cd); +void Handle_View(int view, int action=0); +void Handle_Team(int team, int action=0); +TechnoTypeClass const * Fetch_Techno_Type(RTTIType type, int id); +char const * Fading_Table_Name(char const * base, TheaterType theater); +void Unselect_All(void); +void Play_Movie(char const * name, ThemeType theme=THEME_NONE, bool clrscrn=true); +bool Main_Loop(); +TheaterType Theater_From_Name(char const *name); +//DirType Rotation_Calc(DirType current, DirType desired, int rate); +void Main_Game(int argc, char *argv[]); +long VQ_Call_Back(unsigned char * buffer=NULL, long frame=0); +void Call_Back(void); +char const *Language_Name(char const *basename); +SourceType Source_From_Name(char const *name); +char const *Name_From_Source(SourceType source); +FacingType KN_To_Facing(int input); +void const *Get_Radar_Icon(void const *shapefile, int shapenum, int frames, int zoomfactor); +void CC_Draw_Shape(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata=0, void const * ghostdata=0); +void Go_Editor(bool flag); +long MixFileHandler(VQAHandle *vqa, long action, void *buffer, long nbytes); + +char *CC_Get_Shape_Filename(void const *shapeptr ); +void CC_Add_Shape_To_Global(void const *shapeptr, char *filename, char code ); + +void Bubba_Print(char *format,...); + +void Heap_Dump_Check( char *string ); +void Dump_Heap_Pointers( void ); +unsigned long Disk_Space_Available(void); + +void Validate_Error(char *name); +void const * Hires_Retrieve(char *name); +int Get_Resolution_Factor(void); + + +/* +** INTERPAL.CPP +*/ +#define SIZE_OF_PALETTE 256 +extern "C" unsigned char *InterpolationPalette; +extern BOOL InterpolationPaletteChanged; +extern void Interpolate_2X_Scale( GraphicBufferClass *source, GraphicViewPortClass *dest ,char const *palette_file_name); +void Read_Interpolation_Palette (char const *palette_file_name); +void Write_Interpolation_Palette (char const *palette_file_name); +void Increase_Palette_Luminance(unsigned char *InterpolationPalette , int RedPercentage ,int GreenPercentage ,int BluePercentage ,int cap); +extern "C"{ + extern unsigned char PaletteInterpolationTable[SIZE_OF_PALETTE][SIZE_OF_PALETTE]; + extern unsigned char *InterpolationPalette; + void __cdecl Asm_Create_Palette_Interpolation_Table(void); +} + + +/* +** COORD.CPP +*/ +void Move_Point(short &x, short &y, register DirType dir, unsigned short distance); +COORDINATE Adjacent_Cell(COORDINATE coord, FacingType dir); +COORDINATE Coord_Move(COORDINATE start, DirType facing, unsigned short distance); +COORDINATE Coord_Scatter(COORDINATE coord, unsigned distance, bool lock=false); +DirType Direction(CELL cell1, CELL cell2); +DirType Direction(COORDINATE coord1, COORDINATE coord2); +DirType Direction256(COORDINATE coord1, COORDINATE coord2); +DirType Direction8(COORDINATE coord1, COORDINATE coord2); +int Distance(CELL coord1, CELL coord2); +int Distance(COORDINATE coord1, COORDINATE coord2); +short const * Coord_Spillage_List(COORDINATE coord, int maxsize); +//void Move_Point(unsigned short &x, unsigned short &y, DirType dir, unsigned short distance); + +/* +** COORDA.CPP +*/ +//extern "C" { +//unsigned Cardinal_To_Fixed(unsigned base, unsigned cardinal); +//unsigned Fixed_To_Cardinal(unsigned base, unsigned fixed); +//} + +/* +** DEBUG.CPP +*/ +void Log_Event(char const *text, ...); +void Debug_Key(unsigned input); +void Self_Regulate(void); + +/* +** DIALOG.CPP +*/ +int Format_Window_String(char * string, int maxlinelen, int & width, int & height); +extern void Dialog_Box(int x, int y, int w, int h); +void Conquer_Clip_Text_Print(char const *, unsigned x, unsigned y, unsigned fore, unsigned back=(unsigned)TBLACK, TextPrintType flag=TPF_8POINT|TPF_DROPSHADOW, unsigned width=-1, int const * tabs=0); +void Draw_Box(int x, int y, int w, int h, BoxStyleEnum up, bool filled); +int __cdecl Dialog_Message(char *errormsg, ...); +void Window_Box(WindowNumberType window, BoxStyleEnum style); +void Fancy_Text_Print(char const *text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...); +void Fancy_Text_Print(int text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...); +void Simple_Text_Print(char const *text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag); + +/* +** DISPLAY.CPP +*/ + +/* +** ENDING.CPP +*/ +void GDI_Ending(void); +void Nod_Ending(void); + +/* +** EXPAND.CPP +*/ +bool Expansion_Present(void); +bool Expansion_Dialog(void); +bool Bonus_Dialog(void); + +/* +** FINDPATH.CPP +*/ +//PathType * Find_Path(CELL source, CELL dest, FacingType *final_moves, int maxlen, int (*callback)(CELL, FacingType), int threshhold); +int Optimize_Moves(PathType *path, int (*callback)(CELL, FacingType), int threshhold); + +/* +** GOPTIONS.CPP +*/ +void Draw_Caption(int text, int x, int y, int w); + +/* +** INI.CPP +*/ +void Set_Scenario_Name(char *buf, int scenario, ScenarioPlayerType player, ScenarioDirType dir = SCEN_DIR_NONE, ScenarioVarType var = SCEN_VAR_NONE); +void Write_Scenario_Ini(char *root); +bool Read_Scenario_Ini(char *root, bool fresh=true); +int Scan_Place_Object(ObjectClass *obj, CELL cell); + +/* +** INIT.CPP +*/ +void Uninit_Game(void); +long Obfuscate(char const * string); +void Anim_Init(void); +bool Init_Game(int argc, char *argv[]); +bool Select_Game(bool fade = false); +bool Parse_Command_Line(int argc, char *argv[]); +void Parse_INI_File(void); +int Version_Number(void); +void Save_Recording_Values(void); +void Load_Recording_Values(void); + +/* +** JSHELL.CPP +*/ +void * Small_Icon(void const * iconptr, int iconnum); +void Set_Window(int window, int x, int y, int w, int h); +void * Load_Alloc_Data(FileClass &file); +long Load_Uncompress(FileClass &file, BuffType &uncomp_buff, BuffType &dest_buff, void *reserved_data); +long Translucent_Table_Size(int count); +void *Build_Translucent_Table(void const *palette, TLucentType const *control, int count, void *buffer); +void *Conquer_Build_Translucent_Table(void const *palette, TLucentType const *control, int count, void *buffer); + +/* +** KEYFBUFF.ASM +*/ +#ifdef __cplusplus +extern "C" { +#endif +long __cdecl Buffer_Frame_To_Page(int x, int y, int w, int h, void *Buffer, GraphicViewPortClass &view, int flags, ...); +#ifdef __cplusplus +} +#endif + +/* +** KEYFRAME.CPP +*/ +int Get_Last_Frame_Length(void); +unsigned long Build_Frame(void const *dataptr, unsigned short framenumber, void *buffptr); +unsigned short Get_Build_Frame_Count(void const *dataptr); +unsigned short Get_Build_Frame_X(void const *dataptr); +unsigned short Get_Build_Frame_Y(void const *dataptr); +unsigned short Get_Build_Frame_Width(void const *dataptr); +unsigned short Get_Build_Frame_Height(void const *dataptr); +bool Get_Build_Frame_Palette(void const *dataptr, void *palette); + +/* +** MAP.CPP +*/ +int Terrain_Cost(CELL cell, FacingType facing); +int Coord_Spillage_Number(COORDINATE coord, int maxsize); + +/* +** MENUS.CPP +*/ +void Setup_Menu(int menu, char const *text[], unsigned long field, int index, int skip); +int Check_Menu(int menu, char const *text[], char *selection, long field, int index); +int Do_Menu(char const **strings, bool blue); +extern int UnknownKey; +int Main_Menu(unsigned long timeout); + +/* +** MPLAYER.CPP +*/ +GameType Select_MPlayer_Game (void); +void Read_MultiPlayer_Settings (void); +void Write_MultiPlayer_Settings (void); +void Read_Scenario_Descriptions (void); +void Free_Scenario_Descriptions(void); +void Computer_Message(void); +int Surrender_Dialog(void); + +/* +** NETDLG.CPP +*/ +bool Init_Network (void); +void Shutdown_Network (void); +bool Remote_Connect (void); +void Destroy_Connection(int id, int error); +bool Process_Global_Packet(GlobalPacketType *packet, IPXAddressClass *address); +unsigned long Compute_Name_CRC(char *name); +void Net_Reconnect_Dialog(int reconn, int fresh, int oldest_index, unsigned long timeval); + +/* +** NULLDLG.CPP +*/ +int Init_Null_Modem( SerialSettingsType *settings ); +void Shutdown_Modem( void ); +void Modem_Signoff( void ); +int Test_Null_Modem( void ); +int Reconnect_Modem( void ); +void Destroy_Null_Connection(int id, int error); +GameType Select_Serial_Dialog( void ); +int Com_Scenario_Dialog(void); +int Com_Show_Scenario_Dialog(void); + +void Smart_Printf( char *format, ... ); +void Hex_Dump_Data( char *buffer, int length ); +void itoh( int i, char *s); + +/* +** PROFILE.CPP +*/ +int WWGetPrivateProfileInt(char const *section, char const *entry, int def, char *profile); +bool WWWritePrivateProfileInt(char const *section, char const *entry, int value, char *profile); +bool WWWritePrivateProfileString(char const *section, char const *entry, char const *string, char *profile); +char * WWGetPrivateProfileString(char const *section, char const *entry, char const *def, char *retbuffer, int retlen, char *profile); +unsigned WWGetPrivateProfileHex (char const *section, char const *entry, char *profile); + +/* +** QUEUE.CPP +*/ +bool Queue_Target(TARGET whom, TARGET target); +bool Queue_Destination(TARGET whom, TARGET target); +bool Queue_Mission(TARGET whom, MissionType mission); +bool Queue_Mission(TARGET whom, MissionType mission, TARGET target, TARGET destination); +bool Queue_Options(void); +bool Queue_Exit(void); +void Queue_AI(void); +void Add_CRC(unsigned long *crc, unsigned long val); + +/* +** RAND.CPP +*/ +int Sim_IRandom(int minval, int maxval); +int Sim_Random(void); + +/* +** REINF.CPP +*/ +bool Do_Reinforcements(TeamTypeClass *team); +bool Create_Special_Reinforcement(HouseClass * house, TechnoTypeClass const * type, TechnoTypeClass const * another, TeamMissionType mission = TMISSION_NONE, int argument =0); +int Create_Air_Reinforcement(HouseClass *house, AircraftType air, int number, MissionType mission, TARGET tarcom, TARGET navcom); + +/* +** SAVELOAD.CPP +*/ +bool Load_Misc_Values(FileClass &file); +bool Save_Misc_Values(FileClass &file); +bool Get_Savefile_Info(int id, char *buf, unsigned *scenp, HousesType *housep); +bool Load_Game(int id); +bool Read_Object (void *ptr, int base_size, int class_size, FileClass & file, void * vtable); +bool Save_Game(int id,char *descr); +bool Write_Object (void *ptr, int class_size, FileClass & file); +TARGET TechnoType_To_Target(TechnoTypeClass const * ptr); +TechnoTypeClass const * Target_To_TechnoType(TARGET target); +void * Get_VTable(void *ptr, int base_size); +void Code_All_Pointers(void); +void Decode_All_Pointers(void); +void Dump(void); +void Set_VTable(void *ptr, int base_size, void *vtable); + +/* +** SCENARIO.CPP +*/ +bool End_Game(void); +bool Read_Scenario(char *root); +bool Start_Scenario(char *root, bool briefing=true); +HousesType Select_House(void); +void Clear_Scenario(void); +void Do_Briefing(char const * text); +void Do_Lose(void); +void Do_Win(void); +void Do_Restart(void); +void Fill_In_Data(void); +bool Restate_Mission(char const * name, int button1, int button2); + +/* +** SCORE.CPP +*/ +void Map_Selection(void); +void Bit_It_In_Scale(int x, int y, int w, int h, GraphicBufferClass *src, GraphicBufferClass *dest, GraphicViewPortClass *seen , int delay=0, int dagger=0); +void Bit_It_In(int x, int y, int w, int h, GraphicBufferClass *src, GraphicBufferClass *dest, int delay=0, int dagger=0); +void Call_Back_Delay(int time); +int Alloc_Object(ScoreAnimClass *obj); +extern GraphicBufferClass *PseudoSeenBuff; + +void Window_Dialog_Box(HANDLE hinst, LPCTSTR lpszTemplate, HWND hwndOwner, DLGPROC dlgprc); + +/* +** SPECIAL.CPP +*/ +void Special_Dialog(void); + +/* +** SUPPORT.ASM +*/ +#ifdef __cplusplus +extern "C" { +#endif +void __cdecl Remove_From_List(void **list, int *index, void * ptr); +void * __cdecl Conquer_Build_Fading_Table(void const *palette, void *dest, int color, int frac); +void __cdecl Fat_Put_Pixel(int x, int y, int color, int size, GraphicViewPortClass &); +void __cdecl strtrim(char *buffer); +long __cdecl Get_EAX( void ); +#ifdef __cplusplus +} +#endif + +/* +** TARCOM.CPP +*/ + +/* +** TARGET.CPP +*/ +COORDINATE As_Movement_Coord(TARGET target); +AircraftClass * As_Aircraft(TARGET target); +AnimClass * As_Animation(TARGET target); +BuildingClass * As_Building(TARGET target); +BulletClass * As_Bullet(TARGET target); +CELL As_Cell(TARGET target); +COORDINATE As_Coord(TARGET target); +InfantryClass * As_Infantry(TARGET target); +TeamClass * As_Team(TARGET target); +TeamTypeClass * As_TeamType(TARGET target); +TechnoClass * As_Techno(TARGET target); +//TerrainClass * As_Terrain(TARGET target); +TriggerClass * As_Trigger(TARGET target); +UnitClass * As_Unit(TARGET target); +inline bool Target_Legal(TARGET target) {return(target != TARGET_NONE);}; +ObjectClass * As_Object(TARGET target); + +/* +** ULOGIC.CPP +*/ +int Terrain_Cost(CELL cell, FacingType facing); + +/* +** Inline miscellaneous functions. +*/ +#define XYP_COORD(x,y) (((x)*ICON_LEPTON_W)/CELL_PIXEL_W + ((((y)*ICON_LEPTON_H)/CELL_PIXEL_H)<<16)) +inline FacingType Dir_Facing(DirType facing) {return (FacingType)(((unsigned char)(facing+0x10)&0xFF)>>5);} +inline DirType Facing_Dir(FacingType facing) {return (DirType)((int)facing << 5);} +inline int Cell_To_Lepton(int cell) {return cell<<8;} +inline int Lepton_To_Cell(int lepton) {return ((unsigned)(lepton + 0x0080))>>8;} +inline CELL XY_Cell(int x, int y) {return ((CELL)(((y)<<6)|(x)));} +inline COORDINATE XY_Coord(int x, int y) {return ((COORDINATE)MAKE_LONG(y, x));} +inline int Coord_X(COORDINATE coord) {return (short)(LOW_WORD(coord));} +inline int Coord_Y(COORDINATE coord) {return (short)(HIGH_WORD(coord));} +inline int Cell_X(CELL cell) {return (int)(((unsigned)cell) & 0x3F);} +inline int Cell_Y(CELL cell) {return (int)(((unsigned)cell) >> 6);} +inline int Dir_Diff(DirType dir1, DirType dir2) {return (int)(*((signed char*)&dir2) - *((signed char*)&dir1));} +inline CELL Coord_XLepton(COORDINATE coord) {return (CELL)(*((unsigned char*)&coord));} +inline CELL Coord_YLepton(COORDINATE coord) {return (CELL)(*(((unsigned char*)&coord)+2));} +//inline COORD CellXY_Coord(unsigned x, unsigned y) {return (COORD)(MAKE_LONG(y<<8, x<<8));} +inline COORDINATE Coord_Add(COORDINATE coord1, COORDINATE coord2) {return (COORDINATE)MAKE_LONG((*((short*)(&coord1)+1) + *((short*)(&coord2)+1)), (*((short*)(&coord1)) + *((short*)(&coord2))));} +inline COORDINATE Coord_Sub(COORDINATE coord1, COORDINATE coord2) {return (COORDINATE)MAKE_LONG((*((short*)(&coord1)+1) - *((short*)(&coord2)+1)), (*((short*)(&coord1)) - *((short*)(&coord2))));} +inline COORDINATE Coord_Snap(COORDINATE coord) {return (COORDINATE)MAKE_LONG((((*(((unsigned short *)&coord)+1))&0xFF00)|0x80), (((*((unsigned short *)&coord))&0xFF00)|0x80));} +inline COORDINATE Coord_Mid(COORDINATE coord1, COORDINATE coord2) {return (COORDINATE)MAKE_LONG((*((unsigned short *)(&coord1)+1) + *((unsigned short *)(&coord2)+1))>>1, (*((unsigned short *)(&coord1)) + *((unsigned short *)(&coord2)))>>1);} +inline COORDINATE Cell_Coord(CELL cell) {return (COORDINATE) MAKE_LONG( (((cell & 0x0FC0)<<2)|0x80), ((((cell & 0x003F)<<1)+1)<<7) );} +inline COORDINATE XYPixel_Coord(int x, int y) {return ((COORDINATE)MAKE_LONG((int)(((long)y*(long)ICON_LEPTON_H)/(long)ICON_PIXEL_H)/*+LEPTON_OFFSET_Y*/, (int)(((long)x*(long)ICON_LEPTON_W)/(long)ICON_PIXEL_W)/*+LEPTON_OFFSET_X*/));} +//inline int Facing_To_16(int facing) {return Facing16[facing];} +inline int Facing_To_32(DirType facing) {return Facing32[facing];} +inline DirType Direction256(COORDINATE coord1, COORDINATE coord2) {return ((DirType)Desired_Facing256(Coord_X(coord1), Coord_Y(coord1), Coord_X(coord2), Coord_Y(coord2)));} +inline DirType Direction(COORDINATE coord1, COORDINATE coord2) {return ((DirType)Desired_Facing256(Coord_X(coord1), Coord_Y(coord1), Coord_X(coord2), Coord_Y(coord2)));} +inline DirType Direction8(COORDINATE coord1, COORDINATE coord2) {return ((DirType)Desired_Facing8(Coord_X(coord1), Coord_Y(coord1), Coord_X(coord2), Coord_Y(coord2)));} +//inline int Direction16(COORDINATE coord1, COORD coord2) {return (Desired_Facing16(Coord_X(coord1), Coord_Y(coord1), Coord_X(coord2), Coord_Y(coord2)));} +inline DirType Direction(CELL cell1, CELL cell2) {return (DirType)(Desired_Facing8(Cell_X(cell1), Cell_Y(cell1), Cell_X(cell2), Cell_Y(cell2)));} +inline COORDINATE Adjacent_Cell(COORDINATE coord, FacingType dir) {return (Coord_Snap(Coord_Add(AdjacentCoord[dir & 0x07], coord)));} +inline COORDINATE Adjacent_Cell(COORDINATE coord, DirType dir) {return Adjacent_Cell(coord, Dir_Facing(dir));} +inline CELL Adjacent_Cell(CELL cell, FacingType dir) {return (CELL)(cell + AdjacentCell[dir]);} +inline CELL Adjacent_Cell(CELL cell, DirType dir) {return (CELL)(cell + AdjacentCell[Dir_Facing(dir)]);} +inline int Lepton_To_Pixel(int lepton) {return ((lepton * ICON_PIXEL_W) + (ICON_LEPTON_W / 2)) / ICON_LEPTON_W;} +inline int Pixel_To_Lepton(int pixel) {return ((pixel * ICON_LEPTON_W) + (ICON_PIXEL_W / 2)) / ICON_PIXEL_W;} +//inline FacingType Facing_To_8(DirType facing) {return (FacingType)(((unsigned char)(facing|0x10))>>5);} +inline COORDINATE XYP_Coord(int x,int y) {return XY_Coord(Pixel_To_Lepton(x), Pixel_To_Lepton(y));}; +inline char const * Text_String(int string) {return(Extract_String(SystemStrings, string));}; + + +template inline T Random_Picky(T a, T b, char *sfile, int line) +{ + sfile = sfile; + line = line; + return (T)IRandom((int)a, (int)b); //, sfile, line); +}; + +#define Random_Pick(low, high) Random_Picky ( (low), (high), __FILE__, __LINE__) + + +template inline T Sim_Random_Pick(T a, T b) +{ + return (T)Sim_IRandom((int)a, (int)b); +}; + + +#ifdef CHEAT_KEYS +#define Check_Ptr(ptr,file,line) \ +{ \ + if (!ptr) { \ + Mono_Clear_Screen(); \ + Mono_Printf("NULL Pointer, Module:%s, line:%d!\n",file,line); \ + Prog_End(); \ + exit(EXIT_SUCCESS); \ + } \ +} +#else +#define Check_Ptr(ptr,file,line) +#endif + +/* +** These routines are for coding & decoding multiplayer ID's +*/ +inline PlayerColorType MPlayerID_To_ColorIndex(unsigned short id) {return (PlayerColorType)(id >> 4);} +inline HousesType MPlayerID_To_HousesType(unsigned short id) {return ((HousesType)(id & 0x000f)); } +inline unsigned short Build_MPlayerID(int c_idx, HousesType htype) { return ((c_idx << 4) | htype); } + + + + +// +// True if we are the currently in focus windows app +// +extern bool GameInFocus; + +extern int ScreenWidth; +extern int ScreenHeight; +extern "C" void ModeX_Blit (GraphicBufferClass *source); +extern void Colour_Debug (int call_number); + + +extern unsigned char *InterpolatedPalettes[100]; +extern BOOL PalettesRead; +extern unsigned PaletteCounter; + +extern "C"{ + extern unsigned char PaletteInterpolationTable[SIZE_OF_PALETTE][SIZE_OF_PALETTE]; + extern unsigned char *InterpolationPalette; +} + +extern void Free_Interpolated_Palettes(void); +extern int Load_Interpolated_Palettes(char const *filename, BOOL add=FALSE); + + +#define CELL_BLIT_ONLY 1 +#define CELL_DRAW_ONLY 2 + +/*********************************************************************************************** + * Distance -- Determines the lepton distance between two coordinates. * + * * + * This routine is used to determine the distance between two coordinates. It uses the * + * Dragon Strike method of distance determination and thus it is very fast. * + * * + * INPUT: coord1 -- First coordinate. * + * * + * coord2 -- Second coordinate. * + * * + * OUTPUT: Returns the lepton distance between the two coordinates. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +int Distance_Coord(COORDINATE coord1, COORDINATE coord2); +#pragma aux Distance_Coord parm [eax] [ebx] \ + modify [edx ebx] \ + value [eax] = \ + "mov dx,ax" \ + "sub dx,bx" \ + "jg okx" \ + "neg dx" \ + "okx:" \ + "shr eax,16" \ + "shr ebx,16" \ + "sub ax,bx" \ + "jg oky" \ + "neg ax" \ + "oky:" \ + "cmp ax,dx" \ + "jg ok" \ + "xchg ax,dx" \ + "ok:" \ + "shr dx,1" \ + "add ax,dx" + +inline int Distance(COORDINATE coord1, COORDINATE coord2) +{ +#ifdef NEVER + int diff1, diff2; + + diff1 = Coord_Y(coord1) - Coord_Y(coord2); + if (diff1 < 0) diff1 = -diff1; + diff2 = Coord_X(coord1) - Coord_X(coord2); + if (diff2 < 0) diff2 = -diff2; + if (diff1 > diff2) { + return(diff1 + (diff2>>1)); + } + return(diff2 + (diff1>>1)); +#else + return(Distance_Coord(coord1, coord2)); +#endif +} + + + +/*********************************************************************************************** + * Distance -- Determines the cell distance between two cells. * + * * + * Use this routine to determine the distance between the two cells specified. The distance * + * is returned in cells. * + * * + * INPUT: cell1, cell2 -- The two cells to determine the distance between. * + * * + * OUTPUT: Returns with the distance between the two cells in units of cell size. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +inline int Distance(CELL coord1, CELL coord2) +{ + int diff1, diff2; + + diff1 = Cell_Y(coord1) - Cell_Y(coord2); + if (diff1 < 0) diff1 = -diff1; + diff2 = Cell_X(coord1) - Cell_X(coord2); + if (diff2 < 0) diff2 = -diff2; + if (diff1 > diff2) { + return(diff1 + (diff2>>1)); + } + return(diff2 + (diff1>>1)); +} + + +/*********************************************************************************************** + * CellClass::Cell_Number -- Returns the cell ID number for this cell object. * + * * + * Call this routine if you wish to determine what the cell number ID is for the currrent * + * cell object. This ID number is the index number into the cell array. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the cell number for this cell object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +inline CELL CellClass::Cell_Number(void) const +{ + return(Map.ID(this)); +} +#if(0) +#ifndef NOMEMCHECK +#define NO_INTERCEPT +#include "memcheck.h" +#endif +#endif + +void WWDOS_Shutdown(void); + +#endif diff --git a/FUNCTION.I b/FUNCTION.I new file mode 100644 index 0000000..872a43c --- /dev/null +++ b/FUNCTION.I @@ -0,0 +1,19 @@ +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : Command & Conquer * +;* * +;* File Name : FUNCTION.I * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : November 10, 1993 * +;* * +;* Last Update : November 10, 1993 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + diff --git a/FUSE.CPP b/FUSE.CPP new file mode 100644 index 0000000..5762148 --- /dev/null +++ b/FUSE.CPP @@ -0,0 +1,196 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\fuse.cpv 2.18 16 Oct 1995 16:50:46 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FUSE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 24, 1994 * + * * + * Last Update : October 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FuseClass::Arm_Fuse -- Sets up fuse for detonation check. * + * FuseClass::Fuse_Checkup -- Determines if the fuse triggers. * + * FuseClass::Fuse_Write -- Writes the fuse data to the save game file. * + * FuseClass::Fuse_Read -- Reads the fuse class data from the save game file. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * FuseClass::FuseClass -- Constructor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1995 BRR : Created. Gosh, what a lotta work. * + *=============================================================================================*/ +FuseClass::FuseClass(void) +{ + Timer = 0; + Arming = 0; + HeadTo = 0; + Proximity = 0; +} + + +/*********************************************************************************************** + * FuseClass::Arm_Fuse -- Sets up fuse for detonation check. * + * * + * This starts a fuse. Fuses are proximity detonation variety but * + * can be modified to have a minimum time to elapse before detonation * + * and a maximum time to exist before detonation. Typically, the * + * timing values are used for missiles that have a minimum arming * + * distance and a limited amount of fuel. * + * * + * INPUT: location -- The coordinate where the projectile start. This * + * is needed for proper proximity tracking. * + * * + * target -- The actual impact point. Fuses are based on real * + * word coordinates. * + * * + * time -- The maximum time that the fuse may work before * + * explosion is forced. * + * * + * arming -- The minimum time that must elapse before the * + * fuse may explode. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/24/1994 JLB : Created. * + *=============================================================================================*/ +void FuseClass::Arm_Fuse(COORDINATE location, COORDINATE target, int timeto, int arming) +{ + timeto = MAX(timeto, arming); + Timer = MIN(timeto, 0xFF); + Arming = MIN(arming, 0xFF); + HeadTo = target; + Proximity = Distance(location, target); +} + + +/*********************************************************************************************** + * FuseClass::Fuse_Checkup -- Determines if the fuse triggers. * + * * + * This will process the fuse and update the internal clocks as well * + * as check to see if the fuse should trigger (explode) or not. * + * * + * INPUT: newlocation -- The new location of the fuse. This is needed * + * to determine proximity explosions. * + * * + * OUTPUT: bool; Was the fuse triggered to explode now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/24/1994 JLB : Created. * + *=============================================================================================*/ +bool FuseClass::Fuse_Checkup(COORDINATE newlocation) +{ + int proximity; + + /* + ** Always decrement the fuse timer. + */ + if (Timer) Timer--; + + /* + ** If the arming countdown has not expired, then do nothing. + */ + if (Arming) { + Arming--; + } else { + + /* + ** If the timer has run out, then the warhead explodes. + */ + if (!Timer) return(true); + + proximity = Distance(newlocation, HeadTo); + if (proximity < 0x0010) return(true); + if (proximity < ICON_LEPTON_W && proximity > Proximity) { + return(true); + } + Proximity = proximity; + } + return(false); +} + + +/*********************************************************************************************** + * FuseClass::Fuse_Write -- Writes the fuse data to the save game file. * + * * + * Use this routine to output the fuse class data to the save game file specified. * + * * + * INPUT: file -- The file to output the data to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void FuseClass::Fuse_Write(FileClass & file) +{ + file.Write(&Timer, sizeof(Timer)); + file.Write(&Arming, sizeof(Arming)); + file.Write(&HeadTo, sizeof(HeadTo)); + file.Write(&Proximity, sizeof(Proximity)); +} + + +/*********************************************************************************************** + * FuseClass::Fuse_Read -- Reads the fuse class data from the save game file. * + * * + * Use this routine to input the fuse class data from the save game file specified. * + * * + * INPUT: file -- The file to input the data from. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void FuseClass::Fuse_Read(FileClass & file) +{ + file.Read(&Timer, sizeof(Timer)); + file.Read(&Arming, sizeof(Arming)); + file.Read(&HeadTo, sizeof(HeadTo)); + file.Read(&Proximity, sizeof(Proximity)); +} + diff --git a/FUSE.H b/FUSE.H new file mode 100644 index 0000000..cd48083 --- /dev/null +++ b/FUSE.H @@ -0,0 +1,95 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\fuse.h_v 2.17 16 Oct 1995 16:46:18 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FUSE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 24, 1994 * + * * + * Last Update : April 24, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FUSE_H +#define FUSE_H + +/**************************************************************************** +** The fuse is used by projectiles to determine whether detonation should +** occur. This is usually determined by tracking the distance to the +** designated target reaches zero or when the timer expires. +*/ +class FuseClass { + public: + FuseClass(void); + void Arm_Fuse(COORDINATE location, COORDINATE target, int time=0xFF, int arming=0); + bool Fuse_Checkup(COORDINATE newlocation); + void Fuse_Write(FileClass & file); + void Fuse_Read(FileClass & file); + COORDINATE Fuse_Target(void); + + /* + ** File I/O. + */ + void Code_Pointers(void); + void Decode_Pointers(void); + + /* + ** Fuses can detonate if enough time has elapsed. This value counts + ** down. When it reaches zero, detonation occurs. + */ + unsigned char Timer; + + private: + + /* + ** Some fuses need a certain amount of time before detonation can + ** occur. This counts down and when it reaches zero, normal fuse + ** detonation checking can occur. + */ + unsigned char Arming; + + /* + ** This is the designated impact point of the projectile. The fuse + ** will trip when the closest point to this location has been reached. + */ + COORDINATE HeadTo; + + /* + ** This is the running proximity value to the impact point. This value + ** will progressively get smaller. Detonation occurs when it reaches + ** zero or when it starts to grow larger. + */ + short Proximity; +}; + +inline COORDINATE FuseClass::Fuse_Target(void) +{ + return(HeadTo); +} + +#endif diff --git a/GADGET.CPP b/GADGET.CPP new file mode 100644 index 0000000..5dd5cb6 --- /dev/null +++ b/GADGET.CPP @@ -0,0 +1,804 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\gadget.cpv 2.18 16 Oct 1995 16:49:40 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GADGET.CPP * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : 01/03/95 * + * * + * Last Update : July 8, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * GadgetClass::Action -- Base action for gadget. * + * GadgetClass::Clear_Focus -- Clears the focus if this gadget has it. * + * GadgetClass::Delete_List -- Deletes all gadget objects in list. * + * GadgetClass::Disable -- Disables the gaget from input processing. * + * GadgetClass::Draw_All -- Forces all gadgets in list to be redrawn. * + * GadgetClass::Draw_Me -- Gadget redraw action (flag control). * + * GadgetClass::Enable -- Enables the gadget. * + * GadgetClass::Extract_Gadget -- Sweeps through the gadget chain to find gadget specified. * + * GadgetClass::Flag_To_Redraw -- Flags this gadget to be redrawn. * + * GadgetClass::GadgetClass -- Constructor for gadget object. * + * GadgetClass::GadgetClass -- Default constructor for a gadget class object. * + * GadgetClass::Get_Next -- Returns a pointer to the next gadget in the chain. * + * GadgetClass::Get_Prev -- Fetches a pointer to the previous gadget. * + * GadgetClass::Has_Focus -- Checks if this object currently has the keyboard focus. * + * GadgetClass::Remove -- Removes the specified gagdet from the list. * + * GadgetClass::Set_Focus -- Sets focus to this gadget. * + * GadgetClass::Sticky_Process -- Handles the sticky flag processing. * + * GadgetClass::~GadgetClass -- Destructor for gadget object. * + * GadgetClass::Is_List_To_Redraw -- tells if any gadget in the list needs redrawing * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include +#include + +/* +** This records the current gadget the the gadget system is "stuck on". Such a +** gadget will be processed to the exclusion of all others until the mouse button +** is no longer pressed. +*/ +GadgetClass * GadgetClass::StuckOn = 0; + +/* +** This is a copy of a pointer to the last list used by the gadget input system. +** If a change of list is detected, then all gadgets are forced to be redrawn. +*/ +GadgetClass * GadgetClass::LastList = 0; + + +/* +** This points to the gadget that is intercepting all keyboard events. +*/ +GadgetClass * GadgetClass::Focused = 0; + + +/*********************************************************************************************** + * GadgetClass::GadgetClass -- Constructor for gadget object. * + * * + * This is the normal constructor for gadget objects. A gadget object is only concerned * + * with the region on the screen to considered "its own" as well as the flags that tell * + * what mouse action should be recognized when the mouse is over this screen area. * + * * + * INPUT: x,y -- Coordinates (in pixels) of the upper left hand corner of the region that * + * will be "owned" by this gadget. * + * * + * w,h -- Width and height (in pixels) of this gadget's region. * + * * + * flags -- The flags (mouse conditions) that will cause this gadget's action * + * function to be called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + *=============================================================================================*/ +GadgetClass::GadgetClass(int x, int y, int w, int h, unsigned flags, int sticky) +{ + X = x; + Y = y; + Width = w; + Height = h; + Flags = flags; + IsToRepaint = false; + IsSticky = sticky; + IsDisabled = false; + + if (IsSticky) { + Flags |= LEFTPRESS|LEFTRELEASE; + } +} + + +/*********************************************************************************************** + * GadgetClass::~GadgetClass -- Destructor for gadget object. * + * * + * This is the destructor for the gadget object. It will clear the focus from this gadget * + * if this gadget currently has the focus. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +GadgetClass::~GadgetClass(void) +{ + if (Has_Focus()) { + Clear_Focus(); + } +} + + +/*************************************************************************** + * GADGETCLASS::CLICKEDON -- If a mouse click is detected within gadget's * + * area and the appropriate flag is set, then call Action(). * + * * + * INPUT: int key, int mousex, int mousey * + * * + * OUTPUT: true or false * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/03/1995 MML : Created. * + *=========================================================================*/ +int GadgetClass::Clicked_On(KeyNumType & key, unsigned flags, int mousex, int mousey) +{ + /* + ** Set flags to match only those events that occur AND are being looked for. If + ** the result is NULL, then we know that this button should be ignored. + */ + flags &= Flags; + + /* + ** If keyboard input should be processed by this "gadget" and keyboard input is + ** detected, then always call the action function. It is up to the action function + ** in this case to either ignore the keyboard input or not. + ** + ** For mouse actions, check to see if the mouse is in the region of the button + ** before calling the associated action function. This is the typical action for + ** buttons. + */ + if (this == StuckOn || + (flags & KEYBOARD) || + (flags && (unsigned)(mousex - X) < Width && (unsigned)(mousey - Y) < Height)) { + + return(Action(flags, key)); + } + return(false); +} + + +/*********************************************************************************************** + * GadgetClass::Enable -- Enables the gadget. * + * * + * This function enables the gadget. An enabled gadget will be processed for input * + * purposes. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Enable(void) +{ + IsDisabled = false; + IsToRepaint = true; + Clear_Focus(); +} + + +/*********************************************************************************************** + * GadgetClass::Disable -- Disables the gaget from input processing. * + * * + * This routine will disable the gadget. A disabled gadget might be rendered, but is * + * ignored for input processing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Disable(void) +{ + IsDisabled = true; + IsToRepaint = true; + Clear_Focus(); +} + + +/*********************************************************************************************** + * GadgetClass::Remove -- Removes the specified gagdet from the list. * + * * + * Use this routine if an individual gadget needs to be removed from the list of gadgets. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the specified gadget found and removed? A false indicates that the * + * gadget wasn't in the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +GadgetClass * GadgetClass::Remove(void) +{ + Clear_Focus(); + return(GadgetClass *)LinkClass::Remove(); +} + + +/*********************************************************************************************** + * GadgetClass::Get_Next -- Returns a pointer to the next gadget in the chain. * + * * + * This returns with the next gadget's pointer. It is identical to the base Get_Next() * + * function, but returns a pointer to a GadgetClass object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the next gadget in the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +GadgetClass * GadgetClass::Get_Next(void) const +{ + return(GadgetClass*)LinkClass::Get_Next(); +} + + +/*********************************************************************************************** + * GadgetClass::Get_Prev -- Fetches a pointer to the previous gadget. * + * * + * This routine will return the previous gadget in the list. It is identical to the base * + * function Get_Prev, but returns a pointer to a GadgetClass object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the previous gadget in the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +GadgetClass * GadgetClass::Get_Prev(void) const +{ + return(GadgetClass*)LinkClass::Get_Prev(); +} + + +/*********************************************************************************************** + * GadgetClass::Delete_List -- Deletes all gadget objects in list. * + * * + * This function will delete all gadgets in the list. It is the counterpart to the * + * Create_One_Of functions. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Any references to these gadget become invalidated by this routine. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Delete_List(void) +{ + GadgetClass * g = this; + + /* + ** Move to head of the list. + */ + while (g->Get_Prev()) { + g = g->Get_Prev(); + } + + /* + ** First delete all the gadgets following the first one. The reason the first one + ** is kept around is that sometimes deleting one gadget will result in related gadgets + ** in the same list also being deleted. The first gadget will always contain the + ** correct gadget pointer. + */ + while (g) { + g->Clear_Focus(); + + GadgetClass * temp = g; + g = g->Get_Next(); + delete temp; + } +} + + +/*********************************************************************************************** + * GadgetClass::Action -- Base action for gadget. * + * * + * This handles the base level action that a gadget performs when a qualifying input event * + * is detected. This sets the redraw flag and returns true (to stop further processing). * + * If no qualifying input event was detected, but this routine was called anyway, then * + * don't perform any action. The call to this routine, in that case, must have been forced * + * for some other reason. * + * * + * INPUT: flag -- The input event bits that qualify for this gadget. A NULL indicates that * + * no qualifying event occured. * + * * + * OUTPUT: bool; Should further gadget list processing be aborted? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int GadgetClass::Action(unsigned flags, KeyNumType &) +{ + /* + ** If any of the event flags are active, then this indicates that something probably + ** has changed the gadget. Flag the gadget to be redrawn. Also, make sure that + ** any sticky flags are cleared up. + */ + if (flags) { + IsToRepaint = true; + Sticky_Process(flags); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * GadgetClass::Draw_Me -- Gadget redraw action (flag control). * + * * + * At this level, there is no actual rendering taking place with the call to Draw_Me, but * + * the IsToRepaint flag must be cleared. Derived objects will call this routine and if it * + * returns true, they will perform their custom rendering. * + * * + * INPUT: forced -- Is this redraw forced by outside circumstances? * + * * + * OUTPUT: bool; Should the gadget imagery be redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/14/1995 JLB : Created. * + *=============================================================================================*/ +int GadgetClass::Draw_Me(int forced) +{ + if (forced || IsToRepaint) { + IsToRepaint = false; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * GadgetClass::Draw_All -- Forces all gadgets in list to be redrawn. * + * * + * Use this function to cause all gadget in the list to be redrawn regardless of the state * + * of the IsToRepaint flag. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + *=============================================================================================*/ +void GadgetClass::Draw_All(bool forced) +{ + GadgetClass *gadget = this; + + while (gadget != NULL) { + gadget->Draw_Me(forced); + gadget = gadget->Get_Next(); + } +} + + +/*************************************************************************** + * GADGETCLASS::PROCESSLIST -- Check list for a mouse click within a gadget* + * * + * INPUT: none. * + * * + * OUTPUT: key pressed. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/03/1995 MML : Created. * + *=========================================================================*/ +KeyNumType GadgetClass::Input(void) +{ + int mousex, mousey; + KeyNumType key; + unsigned flags; + int forced = false; + + /* + ** Record this list so that a forced redraw only occurs the FIRST time the + ** gadget list is passed to this routine. + */ + if (LastList != this) { + LastList = this; + forced = true; + StuckOn = NULL; + Focused = NULL; + } + + /* + ** Fetch any pending keyboard input. + */ + key = Keyboard::Check(); + if (key) { + key = Keyboard::Get(); + } + +#ifdef SCENARIO_EDITOR + + if ( key == KN_K ){ + /* + ** time to create a screen shot using the PCX code (if it works) + */ + GraphicBufferClass temp_page( SeenBuff.Get_Width(), + SeenBuff.Get_Height(), + NULL, + SeenBuff.Get_Width() * SeenBuff.Get_Height()); + char filename[30]; + + SeenBuff.Blit(temp_page); + for (int lp = 0; lp < 99; lp ++) { + if (lp < 10) { + sprintf(filename, "scrsht0%d.pcx", lp); + } else { + sprintf(filename, "scrsht%d.pcx", lp); + } + if (access(filename, F_OK) == -1) + break; + } + + Write_PCX_File(filename, temp_page, (unsigned char *)CurrentPalette); + //Map.Place_Random_Crate(); + } + +#endif //SCENARIO_EDITOR + + /* + ** For mouse button clicks, the mouse position is actually held in the MouseQ... + ** globals rather than their normal Mouse... globals. This is because we need to + ** know the position of the mouse at the exact instant when the click occured + ** rather the the mouse position at the time we get around to this function. + */ + if (((key&0x10FF) == KN_LMOUSE) || ((key&0x10FF) == KN_RMOUSE)) { + mousex = _Kbd->MouseQX; + mousey = _Kbd->MouseQY; + } else { + mousex = Get_Mouse_X(); + mousey = Get_Mouse_Y(); + } + + /* + ** Set the mouse button state flags. These will be passed to the individual + ** buttons so that they can determine what action to perform (if any). + */ + flags = 0; + if (key) { + if (key == KN_LMOUSE) { + flags |= LEFTPRESS; + } + if (key == KN_RMOUSE) { + flags |= RIGHTPRESS; + } + if (key == (KN_LMOUSE | KN_RLSE_BIT)) { + flags |= LEFTRELEASE; + } + if (key == (KN_RMOUSE | KN_RLSE_BIT)) { + flags |= RIGHTRELEASE; + } + } + + /* + ** If the mouse wasn't responsible for this key code, then it must be from + ** the keyboard. Flag this fact. + */ + if (key && !flags) { + flags |= KEYBOARD; + } + + /* + ** Mouse button up or down action is ignored if there is a keyboard event. This + ** allows keyboard events to fall through normally even if the mouse is over a + ** gadget that is flagged for LEFTUP or RIGHTUP. + */ + if (!key) { + + /* + ** Check for the mouse being held down. We can't use the normal input system + ** for this, so we must examine the actual current state of the mouse + ** buttons. As a side note, if we determine that the mouse button isn't being + ** held down, then we automatically know that it must be up -- set the flag + ** accordingly. + */ + if (Keyboard::Down(KN_LMOUSE)) { + flags |= LEFTHELD; + } else { + flags |= LEFTUP; + } + if (Keyboard::Down(KN_RMOUSE)) { + flags |= RIGHTHELD; + } else { + flags |= RIGHTUP; + } + } + + /* + ** If "sticky" processing is active, then only process the stuck gadget. + */ + if (StuckOn) { + StuckOn->Draw_Me(false); + StuckOn->Clicked_On(key, flags, mousex, mousey); + if (StuckOn) { + StuckOn->Draw_Me(false); + } + } else { + + /* + ** If there is a gadget that has the keyboard focus, then route all keyboard + ** events to it. + */ + if (Focused && (flags & KEYBOARD)) { + Focused->Draw_Me(false); + Focused->Clicked_On(key, flags, mousex, mousey); + if (Focused) { + Focused->Draw_Me(false); + } + } else { + + /* + ** Sweep through all the buttons in the chain and pass the current button state + ** and keyboard input data to them. These routines will detect whether they should + ** perform some action and return a flag to this effect. They also have the option + ** of changing the key value so that an appropriate return value is use for this + ** processing routine. + */ + GadgetClass *next_button = this; + while (next_button != NULL) { + + /* + ** Maybe redraw the button if it needs to or is being forced to redraw. + */ + next_button->Draw_Me(forced); + + if (!next_button->IsDisabled) { + + /* + ** Process this button. If the button was recognized and action was + ** performed, then bail from further processing (speed reasons?). + */ + if (next_button->Clicked_On(key, flags, mousex, mousey)) { + + /* + ** Some buttons will require repainting when they perform some action. + ** Do so at this time. + */ + next_button->Draw_Me(false); + break; + } + } + + next_button = next_button->Get_Next(); + } + } + } + return(key); +} + + +/*********************************************************************************************** + * GadgetClass::Extract_Gadget -- Sweeps through the gadget chain to find gadget specified. * + * * + * This examines the gadget list looking for on that has the same ID as specified. If that * + * gadget was found, then a pointer to it is returned. Since only ControlClass gadgets * + * or ones derived from it can have an ID value, we know that the returned pointer is at * + * least of the ControlClass type. * + * * + * INPUT: id -- The ID number to scan for. Zero is not a legal ID number and if passed in, * + * a NULL will always be returned. * + * * + * OUTPUT: Returns with a pointer to the ControlClass gadget that has the matching ID number. * + * If no matching gadget was found, then NULL is returned. * + * * + * WARNINGS: If there happens to be more than one gadget with a matching ID, this routine * + * will return a pointer to the first one only. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +ControlClass * GadgetClass::Extract_Gadget(unsigned id) +{ + GadgetClass * g = this; + + if (id) { + while (g) { + if (g->Get_ID() == id) { + return((ControlClass *)g); + } + g = g->Get_Next(); + } + } + return(0); +} + + +/*********************************************************************************************** + * GadgetClass::Flag_To_Redraw -- Flags this gadget to be redrawn. * + * * + * Use this routine to flag the gadget to be redrawn. A gadget so flagged will have its * + * Draw_Me function called at the next available opportunity. Usually, this is the next * + * time the Input() function is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Flag_To_Redraw(void) +{ + IsToRepaint = true; +} + + +/*********************************************************************************************** + * GadgetClass::Sticky_Process -- Handles the sticky flag processing. * + * * + * This function examines the event flags and handles any "sticky" processing required. * + * Sticky processing is when the button is flagged with the "IsSticky" bit and it will * + * be processed to the exclusion of all other gadgets while the mouse button is held * + * down. * + * * + * INPUT: flags -- The event flags that triggered the call to this routine. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Sticky_Process(unsigned flags) +{ + if (IsSticky && (flags & LEFTPRESS)) { + StuckOn = this; + } + if (StuckOn == this && (flags & LEFTRELEASE)) { + StuckOn = 0; + } +} + + +/*********************************************************************************************** + * GadgetClass::Set_Focus -- Sets focus to this gadget. * + * * + * This will set the focus to this gadget regardless of any current focus setting. If there * + * is another gadget that has focus, it will have its focus cleared before this gadget will * + * get the focus. A focused gadget is one that has all keyboard input routed to it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Set_Focus(void) +{ + if (Focused) { + Focused->Flag_To_Redraw(); + Focused->Clear_Focus(); + } + Flags |= KEYBOARD; + Focused = this; +} + + +/*********************************************************************************************** + * GadgetClass::Clear_Focus -- Clears the focus if this gadget has it. * + * * + * Use this function to clear the focus for the gadget. If the gadget doesn't currently * + * have focus, then this routine will do nothing. For added functionality, overload this * + * virtual function so that gadget specific actions may be take when focus is lost. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Clear_Focus(void) +{ + if (Focused == this) { + Flags &= ~KEYBOARD; + Focused = 0; + } +} + + +/*********************************************************************************************** + * GadgetClass::Has_Focus -- Checks if this object currently has the keyboard focus. * + * * + * If this object has the keyboard focus, then this routine will return true. When the * + * gadget has keyboard focus, all keyboard events get routed to the gadget. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Does this gadget have the keyboard focus? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +bool GadgetClass::Has_Focus(void) +{ + return(this == Focused); +} + +/*********************************************************************************************** + * GadgetClass::Is_List_To_Redraw -- tells if any gadget in the list needs redrawing * + * * + * This function is mostly for supporting HidPage drawing. If it returns true, it means * + * the application needs to re-blit the HidPage forward, after calling the list's Input(). * + * * + * INPUT: none * + * * + * OUTPUT: true = an item needs redrawing, false = no items need redrawing * + * * + * WARNINGS: It is assumed 'this' is the head of the list. * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + *=============================================================================================*/ +int GadgetClass::Is_List_To_Redraw(void) +{ + GadgetClass *gadget = this; + + while (gadget != NULL) { + if (gadget->IsToRepaint) + return (true); + gadget = gadget->Get_Next(); + } + return (false); +} + + diff --git a/GADGET.H b/GADGET.H new file mode 100644 index 0000000..f6f650b --- /dev/null +++ b/GADGET.H @@ -0,0 +1,239 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\gadget.h_v 2.17 16 Oct 1995 16:46:34 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GADGET.H * + * * + * Programmer : Maria del Mar McCready Legg * + * * + * Start Date : January 3, 1995 * + * * + * Last Update : January 3, 1995 [MML] * + * * + * * + * LinkClass [This is the linked list manager class. It keeps a record * + * ³ of the next and previous gadget in the list. It is possible * + * ³ delete a gadget out of the middle of the list with this * + * ³ class.] * + * ³ * + * GadgetClass [The is the basic gadget class. It handles processing of * + * ³ input events and dispatching the appropriate functions. * + * ³ All gadgets must be derived from this class.] * + * ÃÄÄÄÄ¿ * + * ³ ³ * + * ³ ListClass [Ths list class functions like a list box does in Windows. It * + * ³ keeps track of a list of text strings. This list can be * + * ³ scrolled and an item selected. If the list becomes larger than * + * ³ can be completely displayed, it will automatically create a * + * ³ slider (at the right edge) to manage the scrolling.] * + * ³ * + * ControlClass [This class adds the concept of giving an ID number to the * + * ³ gadget. This ID can then be returned from the Input() * + * ³ function as if it were a pseudo-keystroke. Additionally, * + * ³ the ability to inform another button that this button has * + * ³ been actioned is allowed. This ability allows one button * + * ³ to watch what happens to another button. Example: a list * + * ³ box gadget can tell when an attached slider has been * + * ³ touched.] * + * ÚÄÄÄÄÄÄÄÅÄÄÄÄ¿ * + * ³ ³ ³ * + * ³ ³ GaugeClass [This class looks similar to Windows slider, but has * + * ³ ³ ³ a different controlling logic. There is no thumb and * + * ³ ³ ³ it serves as a simple variable control setting. This * + * ³ ³ ³ is analagous to a volume slider.] * + * ³ ³ ³ * + * ³ ³ SliderClass [The slider class is similar to the typical Windows slider. It * + * ³ ³ has a current setting, a thumb, and a controlable scale. This * + * ³ ³ is the object created to handle a scrolling list box.] * + * ³ ³ * + * ³ EditClass * + * ³ * + * ³ * + * ToggleClass [The toggle class is used for buttons that have an image and behave just * + * ³ like the buttons in Windows do. That is, they have a separate visual for * + * ³ when they are pressed and raised. They are officially triggered (return * + * ³ their ID number) when the mouse button is released while over the button. * + * ³ This class doesn't perform any rendering itself. It merely provides the * + * ³ logic so that the derived classes will function correctly.] * + * ÚÄÁÄÄÄÄ¿ * + * ³ ³ * + * ³ TextButtonClass [The text button functions like a normal Windows style button, but * + * ³ the imagery is based on text that is displayed on the button. A * + * ³ typical example would be the "OK" or "Cancel" buttons.] * + * ³ * + * ShapeButtonClass [The shape buttons is similar to the TextButton but instead of text * + * being used to give the button its imagery, an actual shape is used * + * instead. This allows graphic buttons. These are similar to the up/down * + * arrows seen in a Windows slider.] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GADGET_H +#define GADGET_H + +#include "link.h" + +class ControlClass; + +class GadgetClass : public LinkClass +{ + public: + typedef enum FlagEnum { + LEFTPRESS = 0x0001, // Left mouse button press. + LEFTHELD = 0x0002, // Left mouse button is being held down. + LEFTRELEASE = 0x0004, // Left mouse button released. + LEFTUP = 0x0008, // Left mouse button is being held up. + RIGHTPRESS = 0x0010, // Right mouse button press. + RIGHTHELD = 0x0020, // Right mouse button is being held down. + RIGHTRELEASE = 0x0040, // Right mouse button released. + RIGHTUP = 0x0080, // Right mouse button is being held up. + KEYBOARD = 0x0100, // Keyboard input processing (maybe). + } FlagEnum; + + GadgetClass(int x, int y, int w, int h, unsigned flags, int sticky=false); + GadgetClass(void) {}; + virtual ~GadgetClass(void); +// static GadgetClass * Create_One_Of(int x, int y, int w, int h, unsigned flags, int sticky=false); + + /* + ** Gadget list management functions. + */ + virtual KeyNumType Input(void); + virtual void Draw_All(bool forced=true); + virtual void Delete_List(void); + virtual ControlClass * Extract_Gadget(unsigned id); + virtual void Flag_List_To_Redraw(void) {LastList = 0;}; + virtual GadgetClass * Remove(void); + virtual GadgetClass * Get_Next(void) const; + virtual GadgetClass * Get_Prev(void) const; + + /* + ** Manages individual gadget states and actions. + */ + virtual void Disable(void); + virtual void Enable(void); + virtual unsigned Get_ID(void) const {return 0;}; + virtual void Flag_To_Redraw(void); + virtual void Peer_To_Peer(unsigned , KeyNumType & , ControlClass & ) {}; + virtual void Set_Focus(void); + virtual void Clear_Focus(void); + virtual bool Has_Focus(void); + virtual int Is_List_To_Redraw(void); + + /* + ** General render function. + */ + virtual int Draw_Me(int forced=false); + + /* + ** This is the coordinates and dimensions of the gadget region. These are in + ** absolute screen pixel coordinates. + */ + int X; + int Y; + int Width; + int Height; + + protected: + + /* + ** Processes the event flags so that if this gadget needs to "stick" or + ** "unstick", it will be properly flagged. Call this function if you are + ** going to clear the button press flags before calling the base class + ** Action() function. Otherwise, calling this function manually, is + ** unnecessary since the base class Action() function already does so. + */ + virtual void Sticky_Process(unsigned flags); + + /* + ** This is the action functio that will be called whenever the flags and mouse + ** input indicates. This is the main method by which this button performs a useful + ** function. + */ + virtual int Action(unsigned flags, KeyNumType & key); + + /* + ** If there is a sticky button being processed, then this will point to it. A sticky + ** button is one that will ONLY be processed while the mouse button is being + ** held down. + */ + static GadgetClass * StuckOn; + + /* + ** This is a record of the last list passed to the Input() function. If a list + ** different than the last recorded one is detected, then the draw function is + ** called for every gadget in the list. This causes all buttons to be redrawn the + ** fire time Input() is called without forced a manual call to Draw_All(). + */ + static GadgetClass * LastList; + + /* + ** This points to the gadget that has the keyboard focus. All keyboard only + ** events are fed to this gadget to the exclusion of all others. + */ + static GadgetClass * Focused; + + /* + ** This button should call the Draw_Me function because some graphic element needs + ** to be redrawn. This flag is set by default if the Action function is called. + */ + unsigned IsToRepaint:1; + + public: // HACK HACK HACK.. this is here becuase the sidebar buttons are static. + /* + ** A sticky button is one that is processed to the exclusion of all other buttons + ** IF the mouse was pressed down while over this button and the mouse continues + ** to remain pressed. This is the standard behavior for all normal Windows style + ** buttons. + */ + unsigned IsSticky:1; + + protected: + + /* + ** If the button is disabled, then it won't be processed by the input function. It will + ** have its Draw_Me function called as necessary. In order to not display the button + ** at all, the appropriate draw function should perform no action -- just return. Or, + ** just remove the button from the list. + */ + unsigned IsDisabled:1; + + /* + ** These are the action flags that are used to determine when the action function + ** should be called. Example: If this gadget only wants the action button called when + ** the left mouse button is pressed over the its region, then the flag will be set + ** to LEFTPRESS. + */ + unsigned Flags; + + private: + virtual int Clicked_On(KeyNumType & key, unsigned flags, int x, int y); +}; + +inline GadgetClass::FlagEnum operator |(GadgetClass::FlagEnum, GadgetClass::FlagEnum); +inline GadgetClass::FlagEnum operator &(GadgetClass::FlagEnum, GadgetClass::FlagEnum); +inline GadgetClass::FlagEnum operator ~(GadgetClass::FlagEnum); + + +#endif diff --git a/GAMEDLG.CPP b/GAMEDLG.CPP new file mode 100644 index 0000000..4924b3c --- /dev/null +++ b/GAMEDLG.CPP @@ -0,0 +1,422 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\gamedlg.cpv 2.17 16 Oct 1995 16:52:02 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GAMEDLG.CPP * + * * + * Programmer : Maria del Mar McCready Legg, Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : Jan 18, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * OptionsClass::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "gamedlg.h" +#include "sounddlg.h" +#include "visudlg.h" + + +/*********************************************************************************************** + * OptionsClass::Process -- Handles all the options graphic interface. * + * * + * This routine is the main control for the visual representation of the options * + * screen. It handles the visual overlay and the player input. * + * * + * INPUT: none * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: * + * 12/31/1994 MML : Created. * + *=============================================================================================*/ +void GameControlsClass::Process(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 232 * factor; // dialog width + int d_dialog_h = 141 * factor; // dialog height + int d_dialog_x = ((SeenBuff.Get_Width()- d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((SeenBuff.Get_Height() - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + int d_top_margin= 30 * factor; + + int d_txt6_h = 7 * factor; // ht of 6-pt text + int d_margin1 = 5 * factor; // large margin + int d_margin2 = 2 * factor; // small margin + + int d_speed_w = d_dialog_w - (20 * factor); + int d_speed_h = 6 * factor; + int d_speed_x = d_dialog_x + (10 * factor); + int d_speed_y = d_dialog_y + d_top_margin + d_margin1 + d_txt6_h; + + int d_scroll_w = d_dialog_w - (20 * factor); + int d_scroll_h = 6 * factor; + int d_scroll_x = d_dialog_x + (10 * factor); + int d_scroll_y = d_speed_y + d_speed_h + d_txt6_h + (d_margin1 * 2) + d_txt6_h; + + int d_visual_w = d_dialog_w - (40 * factor); + int d_visual_h = 9 * factor; + int d_visual_x = d_dialog_x + (20 * factor); + int d_visual_y = d_scroll_y + d_scroll_h + d_txt6_h + (d_margin1 * 2); + + int d_sound_w = d_dialog_w - (40 * factor); + int d_sound_h = 9 * factor; + int d_sound_x = d_dialog_x + (20 * factor); + int d_sound_y = d_visual_y + d_visual_h + d_margin1; + + int d_ok_w = 20 * factor; + int d_ok_h = 9 * factor; + int d_ok_x = d_dialog_cx - (d_ok_w / 2); + int d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin1; + + /* + ** Button Enumerations + */ + enum { + BUTTON_SPEED = 100, + BUTTON_SCROLLRATE, + BUTTON_VISUAL, + BUTTON_SOUND, + BUTTON_OK, + BUTTON_COUNT, + BUTTON_FIRST = BUTTON_SPEED, + }; + + /* + ** Dialog variables + */ + KeyNumType input; + + int gamespeed = Options.GameSpeed; + int scrollrate = Options.ScrollRate; + int selection; + bool pressed = false; + int curbutton = 0; + TextButtonClass *buttons[BUTTON_COUNT - BUTTON_FIRST]; + TextPrintType style; + + /* + ** Buttons + */ + GadgetClass *commands; // button list + + SliderClass gspeed_btn(BUTTON_SPEED, d_speed_x, d_speed_y, d_speed_w, d_speed_h); + + SliderClass scrate_btn(BUTTON_SCROLLRATE, d_scroll_x, d_scroll_y, d_scroll_w, d_scroll_h); + + TextButtonClass visual_btn(BUTTON_VISUAL, TXT_VISUAL_CONTROLS, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_visual_x, d_visual_y, d_visual_w, d_visual_h); + + TextButtonClass sound_btn(BUTTON_SOUND, TXT_SOUND_CONTROLS, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_sound_x, d_sound_y, d_sound_w, d_sound_h); + + TextButtonClass okbtn(BUTTON_OK, TXT_OPTIONS_MENU, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_ok_x, d_ok_y); + okbtn.X = (SeenBuff.Get_Width()-okbtn.Width)/2; + + /* + ** Various Inits. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Build button list + */ + commands = &okbtn; + gspeed_btn.Add_Tail(*commands); + scrate_btn.Add_Tail(*commands); + visual_btn.Add_Tail(*commands); + sound_btn.Add_Tail(*commands); + + /* + ** Init button states + ** For sliders, the thumb ranges from 0 - (maxval-1), so to convert the + ** thumb value to a real-world value: + ** val = (MAX - slider.Get_Value()) - 1; + ** and, + ** slider.Set_Value(-(val + 1 - MAX)); + */ + gspeed_btn.Set_Maximum(OptionsClass::MAX_SPEED_SETTING); // varies from 0 - 7 + gspeed_btn.Set_Thumb_Size(1); + gspeed_btn.Set_Value((OptionsClass::MAX_SPEED_SETTING-1) - gamespeed); + + scrate_btn.Set_Maximum(OptionsClass::MAX_SCROLL_SETTING); // varies from 0 - 7 + scrate_btn.Set_Thumb_Size(1); + scrate_btn.Set_Value((OptionsClass::MAX_SCROLL_SETTING-1) - scrollrate); + + /* + ** Fill array of button ptrs. + */ + buttons[0] = NULL; + buttons[1] = NULL; + buttons[2] = &visual_btn; + buttons[3] = &sound_btn; + buttons[4] = &okbtn; + + /* + ** Processing loop. + */ + bool process = true; + bool display = true; + bool refresh = true; + while (process) { + + /* + ** Invoke game callback. + */ + if (GameToPlay == GAME_NORMAL) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + } + } + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=TRUE; + } + + /* + ** Refresh display if needed. + */ + if (display) { + Hide_Mouse(); + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption(TXT_GAME_CONTROLS, d_dialog_x, d_dialog_y, d_dialog_w); + Show_Mouse(); + display = false; + refresh = true; + } + + if (refresh) { + Hide_Mouse(); + + /* + ** Label the game speed slider + */ + style = TPF_6PT_GRAD | TPF_NOSHADOW | TPF_USE_GRAD_PAL; + if (curbutton == (BUTTON_SPEED - BUTTON_FIRST)) { + style = (TextPrintType)(style | TPF_BRIGHT_COLOR); + } + Fancy_Text_Print(TXT_SPEED, d_speed_x, d_speed_y - d_txt6_h, CC_GREEN, TBLACK, style); + + Fancy_Text_Print(TXT_SLOWER, d_speed_x, d_speed_y + d_speed_h + 1, CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + Fancy_Text_Print(TXT_FASTER, d_speed_x + d_speed_w, d_speed_y + d_speed_h + 1, CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW|TPF_RIGHT); + + /* + ** Label the scroll rate slider + */ + style = TPF_6PT_GRAD | TPF_NOSHADOW | TPF_USE_GRAD_PAL; + if (curbutton == (BUTTON_SCROLLRATE - BUTTON_FIRST)) { + style = (TextPrintType)(style | TPF_BRIGHT_COLOR); + } + Fancy_Text_Print(TXT_SCROLLRATE, d_scroll_x, d_scroll_y - d_txt6_h, CC_GREEN, TBLACK, style); + + Fancy_Text_Print (TXT_SLOWER, d_scroll_x, d_scroll_y + d_scroll_h + 1, CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + Fancy_Text_Print (TXT_FASTER, d_scroll_x + d_scroll_w, d_scroll_y + d_scroll_h + 1, CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW|TPF_RIGHT); + + commands->Draw_All(); + + Show_Mouse(); + refresh = false; + } + + /* + ** Get user input. + */ + input = commands->Input(); + + /* + ** Process input. + */ + switch (input) { + case (BUTTON_SPEED | KN_BUTTON): + curbutton = (BUTTON_SPEED - BUTTON_FIRST); + refresh = true; + break; + + case (BUTTON_SCROLLRATE | KN_BUTTON): + curbutton = (BUTTON_SCROLLRATE - BUTTON_FIRST); + refresh = true; + break; + + case (BUTTON_VISUAL | KN_BUTTON): + selection = BUTTON_VISUAL; + pressed = true; + break; + + case (BUTTON_SOUND | KN_BUTTON): + selection = BUTTON_SOUND; + pressed = true; + break; + + case (BUTTON_OK | KN_BUTTON): + selection = BUTTON_OK; + pressed = true; + break; + + case (KN_ESC): + process = false; + break; + + case (KN_LEFT): + if (curbutton == (BUTTON_SPEED - BUTTON_FIRST) ) { + gspeed_btn.Bump(1); + } else + if (curbutton == (BUTTON_SCROLLRATE - BUTTON_FIRST) ) { + scrate_btn.Bump(1); + } + break; + + case (KN_RIGHT): + if (curbutton == (BUTTON_SPEED - BUTTON_FIRST) ) { + gspeed_btn.Bump(0); + } else + if (curbutton == (BUTTON_SCROLLRATE - BUTTON_FIRST) ) { + scrate_btn.Bump(0); + } + break; + + case (KN_UP): + if (buttons[curbutton]) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + } + + curbutton--; + if (curbutton < 0) { + curbutton = (BUTTON_COUNT - BUTTON_FIRST - 1); + } + + if (buttons[curbutton]) { + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + refresh = true; + break; + + case (KN_DOWN): + if (buttons[curbutton]) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + } + + curbutton++; + if (curbutton > (BUTTON_COUNT - BUTTON_FIRST - 1) ) { + curbutton = 0; + } + + if (buttons[curbutton]) { + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + refresh = true; + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_FIRST; + pressed = true; + break; + + default: + break; + } + + /* + ** Perform some action. Either to exit the dialog or bring up another. + */ + if (pressed) { + + /* + ** Record the new options slider settings. + ** The GameSpeed data member MUST NOT BE SET HERE!!! It will cause multiplayer + ** games to go out of sync. It's set by virtue of the event being executed. + */ + if (gamespeed != ((OptionsClass::MAX_SPEED_SETTING-1) - gspeed_btn.Get_Value()) ) { + gamespeed = (OptionsClass::MAX_SPEED_SETTING-1) - gspeed_btn.Get_Value(); + OutList.Add(EventClass(EventClass::GAMESPEED, gamespeed)); + } + + if (scrollrate != ((OptionsClass::MAX_SCROLL_SETTING-1) - scrate_btn.Get_Value()) ) { + scrollrate = (OptionsClass::MAX_SCROLL_SETTING-1) - scrate_btn.Get_Value(); + Options.ScrollRate = scrollrate; + } + process = false; + + /* + ** Save the settings in such a way that the GameSpeed is only set during + ** the save process; restore it when we're done, so multiplayer games don't + ** go out of sync. + */ + int old = Options.GameSpeed; // save orig value + Options.GameSpeed = gamespeed; + Options.Save_Settings(); // save new value + Options.GameSpeed = old; // restore old value + + /* + ** Possibly launch into another dialog if so directed. + */ + switch (selection) { + case (BUTTON_VISUAL): + VisualControlsClass().Process(); + process = true; + display = true; + refresh = true; + break; + + case (BUTTON_SOUND): + if (!SoundType) { + CCMessageBox().Process(Text_String(TXT_NO_SOUND_CARD)); + process = true; + display = true; + refresh = true; + } else { + SoundControlsClass().Process(); + } + break; + + case (BUTTON_OK): + break; + } + + pressed = false; + } + } +} + diff --git a/GAMEDLG.H b/GAMEDLG.H new file mode 100644 index 0000000..e166eb0 --- /dev/null +++ b/GAMEDLG.H @@ -0,0 +1,52 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\gamedlg.h_v 2.17 16 Oct 1995 16:45:22 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GAMEDLG.H * + * * + * Programmer : Maria del Mar McCready Legg, Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : Jan 18, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GAMEDLG_H +#define GAMEDLG_H + +#include "gadget.h" + +class GameControlsClass +{ + public: + GameControlsClass(void) {}; + void Process(void); +}; + + +#endif + diff --git a/GAUGE.CPP b/GAUGE.CPP new file mode 100644 index 0000000..d323cd0 --- /dev/null +++ b/GAUGE.CPP @@ -0,0 +1,539 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\gauge.cpv 2.19 16 Oct 1995 16:50:56 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GAUGE.CPP * + * * + * Programmer : Joe L. Bostic, Maria del Mar McCready Legg * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 16, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * GaugeClass::Action -- Handles input events for the gauge. * + * GaugeClass::Draw_Me -- Draws the body of the gauge. * + * GaugeClass::Value_To_Pixel -- Convert gauge value to pixel offset. * + * GaugeClass::Pixel_To_Value -- Convert a pixel offset into a gauge value. * + * GaugeClass::Set_Value -- Set the value of the gauge. * + * GaugeClass::Set_Maximum -- Sets the maximum value for the gauge. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * GAUGECLASS::GAUGECLASS -- class constructor * + * * + * INPUT: id -- button ID * + * * + * x,y -- upper-left corner, in pixels * + * * + * w,h -- width, height, in pixels * + * * + * OUTPUT: none. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/05/1995 MML : Created. * + *=========================================================================*/ +GaugeClass::GaugeClass(unsigned id, int x, int y, int w, int h) + : ControlClass(id, x, y, w, h, LEFTHELD|LEFTPRESS|LEFTRELEASE, true) +{ + Set_Maximum(255); + Set_Value(0); + + HasThumb = true; + IsHorizontal = (w > h); + IsColorized = true; + + ClickDiff = 0; +} + + +/*********************************************************************************************** + * GaugeClass::Set_Maximum -- Sets the maximum value for the gauge. * + * * + * This routine will set the maximum value for the gauge. This is the largest value that * + * the current setting may reach. The ability to change this allows the guage to use and * + * return values that are convenient for the programmer's current needs. * + * * + * INPUT: value -- The value to use as the gauge maximum. * + * * + * OUTPUT: bool; Was the gauge maximum changed? A false indicates that the specified value * + * already matches the current maximum. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Set_Maximum(int value) +{ + if (value != MaxValue) { + MaxValue = value; + Flag_To_Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * GaugeClass::Set_Value -- Set the value of the gauge. * + * * + * This routine will set the current value for the gauge. This value is clipped to the * + * limits of the gauge maximum. * + * * + * INPUT: value -- The value to set at the new current value. * + * * + * OUTPUT: bool; Was the current setting changed? A false indicates that the setting * + * specified is the same as what was already there. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Set_Value(int value) +{ + value = Bound(value, 0, MaxValue); +// value = MIN(value, MaxValue); +// value = MAX(value, 0); + if (value != CurValue) { + CurValue = value; + Flag_To_Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * GaugeClass::Pixel_To_Value -- Convert a pixel offset into a gauge value. * + * * + * Use this routine to conver the specified pixel offset into a gauge value. This is used * + * in translating mouse clicks into a cooresponding setting for the guage. * + * * + * INPUT: pixel -- The pixel offset form the start of the gauge. * + * * + * OUTPUT: Returns with the setting value in guage coordinates. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Pixel_To_Value(int pixel) +{ + int maximum; + + if (IsHorizontal) { + pixel -= X+1; + maximum = Width; + } else { + pixel -= Y+1; + maximum = Height; + } + maximum -= 2; + pixel = Bound(pixel, 0, maximum); +// pixel = MIN(pixel, maximum); +// pixel = MAX(pixel, 0); + return(Fixed_To_Cardinal(MaxValue, Cardinal_To_Fixed(maximum, pixel))); +} + + +/*********************************************************************************************** + * GaugeClass::Value_To_Pixel -- Convert gauge value to pixel offset. * + * * + * Use this routine to convert the specified gauge value into a pixel offset from the * + * star of the gauge. This is used for thumb positioning. * + * * + * INPUT: value -- The value to convert to a pixel offset. * + * * + * OUTPUT: Returns with the pixel offset of the specified value from the start of the * + * guage. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Value_To_Pixel(int value) +{ + int maximum; + int start; + if (IsHorizontal) { + maximum = Width; + start = X; + } else { + maximum = Height; + start = Y; + } + maximum -= 2; + return(start + Fixed_To_Cardinal(maximum, Cardinal_To_Fixed(MaxValue, value))); +} + + +/*********************************************************************************************** + * GaugeClass::Draw_Me -- Draws the body of the gauge. * + * * + * This routine will draw the body of the gauge if necessary. * + * * + * INPUT: forced -- Should the gauge be redrawn regardless of the current redraw flag? * + * * + * OUTPUT: bool; Was the gauge redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Draw_Me(int forced) +{ + if (ControlClass::Draw_Me(forced)) { + + /* + ===================== Hide the mouse ===================== + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + /* + =========== Draw the body & set text color =============== + */ + Draw_Box (X, Y, Width, Height, BOXSTYLE_GREEN_DOWN, true); + + /* + ** Colorize the inside of the gauge if indicated. + */ + if (IsColorized) { + int middle = Value_To_Pixel(CurValue); + int color = CC_BRIGHT_GREEN; + if (IsHorizontal) { + if (middle >= (X + 1)) + LogicPage->Fill_Rect(X+1, Y+1, middle, Y+Height-2, color); + } else { + if (middle >= (Y + 1)) + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, middle, color); + } + } + + if (HasThumb) + Draw_Thumb(); + + /* + =================== Display the mouse =================== + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * GaugeClass::Action -- Handles input events for the gauge. * + * * + * This routine will handle input event processing for the gauge. It will adjust the * + * current setting of the gauge according to the mouse position. * + * * + * INPUT: flags -- The input event that is the reason for this function call. * + * key -- The key code that caused the event. * + * * + * OUTPUT: bool; Was the even recognized, processed, and no further gadget scanning is * + * desird (for this pass). * + * * + * WARNINGS: none * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Action(unsigned flags, KeyNumType &key) +{ + /* + ** If there's no thumb on this gauge, it's a display-only device; ignore + ** any input. + */ + if (!HasThumb) { + key = KN_NONE; + return(true); + } + + /* + ** We might end up clearing the event bits. Make sure that the sticky + ** process is properly updated anyway. + */ + Sticky_Process(flags); + + /* + ** If the thumb is currently being "dragged around", then update the slider + ** position according to the mouse position. In all other cases, ignore the + ** button being held down. + */ + if ((flags & LEFTPRESS) || ((flags & LEFTHELD) && StuckOn == this)) { + + /* + ** Compute the difference between where we clicked, and the edge of + ** the thumb (only if we clicked on the thumb.) + */ + if (flags & LEFTPRESS) { + int curpix = Value_To_Pixel(CurValue); + int clickpix = (IsHorizontal ? Get_Mouse_X() : Get_Mouse_Y()); + + if ( (clickpix > curpix) && (clickpix - curpix) < Thumb_Pixels()) { + ClickDiff = (clickpix - curpix); + } else { + ClickDiff = 0; + } + + int testval = Pixel_To_Value(IsHorizontal ? + Get_Mouse_X() - ClickDiff : Get_Mouse_Y() - ClickDiff); + + /* + ** Correct for round-down errors in Pixel_To_Value() and + ** Value_To_Pixe(); make ClickDiff exactly right so that + ** at this point, Get_Mouse_n() - ClickDiff converts to + ** CurValue. + */ + while (testval < CurValue && ClickDiff > 0) { + ClickDiff--; + testval = Pixel_To_Value(IsHorizontal ? + Get_Mouse_X() - ClickDiff : Get_Mouse_Y() - ClickDiff); + } + } + + /* + ** If no change occurred in the gauge, just call Control's Action routine, + ** but turn off the flags so it won't fill in 'key' with the button ID. + ** Thus, no button ID will be returned by Input. + */ + if (!Set_Value(Pixel_To_Value(IsHorizontal ? + Get_Mouse_X() - ClickDiff : Get_Mouse_Y() - ClickDiff))) { + + flags &= ~(LEFTHELD|LEFTRELEASE|LEFTPRESS); + ControlClass::Action(0,key); + key = KN_NONE; + return(true); + } + + } else { + + /* + ** Ingore the left mouse button being held down if this gauge is not + ** currently in "sticky" mode. This allows processing of the LEFTPRESS + ** by any derived classes such that this guage can be more closely + ** controlled. + */ + flags &= ~LEFTHELD; + } + return(ControlClass::Action(flags, key)); +} + + +/*********************************************************************************************** + * GaugeClass::Draw_Thumb -- Draws the body of the gauge. * + * * + * This routine will draw the body of the gauge if necessary. * + * * + * INPUT: none. * + * * + * OUTPUT: none. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/16/1995 MML : Created. * + *=============================================================================================*/ +void GaugeClass::Draw_Thumb(void) +{ + int x = Value_To_Pixel(CurValue); + +// if ((x + 8) > Value_To_Pixel(MaxValue)) { + if ((x + 4) > Value_To_Pixel(MaxValue)) { + x = Value_To_Pixel(MaxValue) - 2; + } + + if (IsHorizontal) { + Draw_Box(x, Y, 4, Height, BOXSTYLE_GREEN_RAISED, true); + //Draw_Box(x, Y, 8, Height, BOXSTYLE_GREEN_RAISED, true); + } else { + Draw_Box(X, x, Width, 4, BOXSTYLE_GREEN_RAISED, true); + //Draw_Box(X, x, Width, 8, BOXSTYLE_GREEN_RAISED, true); + } +} + + +/*********************************************************************************************** + * TriColorGaugeClass::TriColorGaugeClass -- Constructor for 3 color (red\yellow\green) gauge. * + * * + * This routine will draw the body of the gauge if necessary. * + * * + * INPUT: See below. * + * * + * OUTPUT: none. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/16/1995 MML : Created. * + *=============================================================================================*/ +TriColorGaugeClass::TriColorGaugeClass(unsigned id, int x, int y, int w, int h) + : GaugeClass(id, x, y, w, h) +{ + RedLimit = 0; // maximum value for red + YellowLimit = 0; // maximum value for yellow +} + + +/*********************************************************************************************** + * TriColorGaugeClass::Set_Red_Limit -- Set the value for the red area of gauge. * + * * + * INPUT: int value. * + * * + * OUTPUT: bool true of false. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/16/1995 MML : Created. * + *=============================================================================================*/ +int TriColorGaugeClass::Set_Red_Limit(int value) +{ + if (value >= 0 && value < MaxValue) { + +// if (value > YellowLimit) { +// RedLimit = YellowLimit; +// YellowLimit = value; +// } else { + RedLimit = value; +// } + Flag_To_Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TriColorGaugeClass::Set_Yellow_Limit -- Set the value for the yellow area of gauge. * + * * + * INPUT: int value. * + * * + * OUTPUT: bool true of false. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/16/1995 MML : Created. * + *=============================================================================================*/ +int TriColorGaugeClass::Set_Yellow_Limit(int value) +{ + if (value >= 0 && value < MaxValue) { + +// if (value < RedLimit) { +// YellowLimit = RedLimit; +// RedLimit = value; +// } else { + YellowLimit = value; +// } + Flag_To_Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TriColorGaugeClass::Draw_Me -- Draw the tri color gauge. * + * * + * INPUT: int forced -- draw or not? * + * * + * OUTPUT: bool true of false. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/16/1995 MML : Created. * + *=============================================================================================*/ +int TriColorGaugeClass::Draw_Me(int forced) +{ + if (ControlClass::Draw_Me(forced)) { + + /* + ===================== Hide the mouse ===================== + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + /* + =========== Draw the body & set text color =============== + */ + Draw_Box (X, Y, Width, Height, (IsDisabled ? BOXSTYLE_GREEN_RAISED : BOXSTYLE_GREEN_DOWN), true); + + /* + ** Colorize the inside of the gauge if indicated. + */ + int red = Value_To_Pixel(RedLimit); + int yellow = Value_To_Pixel(YellowLimit); + int middle = Value_To_Pixel(CurValue); + + if (CurValue <= RedLimit) { + if (IsHorizontal) { + LogicPage->Fill_Rect(X+1, Y+1, middle, Y+Height-2, PINK); + } else { + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, middle, PINK); + } + } else if (CurValue > RedLimit && CurValue <= YellowLimit) { + if (IsHorizontal) { + LogicPage->Fill_Rect(X+1, Y+1, red, Y+Height-2, PINK); + LogicPage->Fill_Rect(red, Y+1, middle, Y+Height-2, YELLOW); + } else { + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, red, PINK); + LogicPage->Fill_Rect(X+1, red, X+Width-2, middle, YELLOW); + } + } else if (CurValue > YellowLimit && CurValue <= MaxValue) { + + if (IsHorizontal) { + LogicPage->Fill_Rect(X+1, Y+1, red, Y+Height-2, PINK); + LogicPage->Fill_Rect(red, Y+1, yellow, Y+Height-2, YELLOW); + LogicPage->Fill_Rect(yellow, Y+1, middle, Y+Height-2, GREEN); + } else { + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, red, PINK); + LogicPage->Fill_Rect(X+1, red, X+Width-2, yellow, YELLOW); + LogicPage->Fill_Rect(X+1, yellow, X+Width-2, middle, GREEN); + } + } + + if (HasThumb) + Draw_Thumb(); + + /* + =================== Display the mouse =================== + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + diff --git a/GAUGE.H b/GAUGE.H new file mode 100644 index 0000000..76f191f --- /dev/null +++ b/GAUGE.H @@ -0,0 +1,111 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\gauge.h_v 2.17 16 Oct 1995 16:45:24 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GAUGE.H * + * * + * Programmer : Joe L. Bostic, Maria del Mar McCready Legg * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GAUGE_H +#define GAUGE_H + +class GaugeClass : public ControlClass +{ + public: + + GaugeClass(unsigned id, int x, int y, int w, int h); +// static GaugeClass * Create_One_Of(unsigned id, int x, int y, int w, int h); + + virtual int Draw_Me(int forced=false); + virtual int Set_Maximum(int value); + virtual int Set_Value(int value); + virtual int Get_Value(void) const {return (CurValue);}; + virtual void Use_Thumb(int value) { HasThumb = value ? true : false; }; + + virtual int Thumb_Pixels(void) { return (8);} + + /* + ** If this gauge has a color to the left of the current setting, then this + ** flag will be true. + */ + unsigned IsColorized:1; + + protected: + + /* + ** If a thumb is desired, set to true. + */ + unsigned HasThumb:1; + + /* + ** Is this a horizontal slider? + */ + unsigned IsHorizontal:1; + + int MaxValue; // maximum value (in application units) + int CurValue; // index of 1st displayed string in box + // (in application units) + + /* + ** This value records the difference between where the user clicked + ** and the edge of the thumb, so that the thumb follows the mouse + ** with the proper offset. + */ + int ClickDiff; + + protected: + virtual void Draw_Thumb(void); + virtual int Action(unsigned flags, KeyNumType &key); + virtual int Pixel_To_Value(int pixel); + virtual int Value_To_Pixel(int value); +}; + + + +class TriColorGaugeClass : public GaugeClass +{ + public: + TriColorGaugeClass(unsigned id, int x, int y, int w, int h); +// static TriColorGaugeClass * Create_One_Of(unsigned id, int x, int y, int w, int h); + virtual int Draw_Me(int forced); + virtual int Set_Red_Limit(int value); + virtual int Set_Yellow_Limit(int value); + + protected: + int RedLimit; // maximum value for red + int YellowLimit; // maximum value for yellow +}; + + + + +#endif diff --git a/GDI.BAT b/GDI.BAT new file mode 100644 index 0000000..de54db5 --- /dev/null +++ b/GDI.BAT @@ -0,0 +1,5 @@ +@echo off +pushd +cd ..\run +conquer -CD..\cd\aud1;..\cd;..\cd\install %1 %2 %3 %4 %5 +popd diff --git a/GLOBALS.CPP b/GLOBALS.CPP new file mode 100644 index 0000000..ebff031 --- /dev/null +++ b/GLOBALS.CPP @@ -0,0 +1,1035 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\globals.cpv 2.17 16 Oct 1995 16:52:22 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GLOBALS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : September 10, 1993 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef JAPANESE +bool ForceEnglish = false; +#endif + +bool Debug_Quiet = false; +bool Debug_Cheat = false; +bool Debug_Remap = false; +bool Debug_Icon = false; +bool Debug_Flag = false; +bool Debug_Lose = false; +bool Debug_Win = false; +bool Debug_Map = false; // true = map editor mode +bool Debug_Passable = false; // true = show passable/impassable terrain +bool Debug_Unshroud = false; // true = hide the shroud +bool Debug_Threat = false; +bool Debug_Find_Path = false; +bool Debug_Check_Map = false; // true = validate the map each frame +bool Debug_Playtest = false; +int In_Debugger = 0; +bool Debug_Heap_Dump = false; // true = print the Heap Dump +bool Debug_Smart_Print = false; // true = print everything that calls Smart_Printf +bool Debug_Trap_Check_Heap = false; // true = check the Heap +bool Debug_Instant_Build = false; + +TFixedIHeapClass Units; +TFixedIHeapClass Factories; +TFixedIHeapClass Terrains; +TFixedIHeapClass Templates; +TFixedIHeapClass Smudges; +TFixedIHeapClass Overlays; +TFixedIHeapClass Infantry; +TFixedIHeapClass Bullets; +TFixedIHeapClass Buildings; +TFixedIHeapClass Anims; +TFixedIHeapClass Aircraft; +TFixedIHeapClass Triggers; +TFixedIHeapClass TeamTypes; +TFixedIHeapClass Teams; +TFixedIHeapClass Houses; + + +#ifdef PATCH +/*************************************************************************** +** Compatibility with version 1.07 flag. +*/ +bool IsV107 = false; +char OverridePath[128]="."; +#endif + + +/*************************************************************************** +** This is a list of all selected objects (for this map). The support functions +** are used to control access to this list. Do not modify it directly. +*/ +DynamicVectorClass CurrentObject; + + +/*************************************************************************** +** This holds the custom version text that is fetched from the version +** text file. This version is displayed on the options dialog. +*/ +char VersionText[16]; + + +/*************************************************************************** +** This is the VQ animation controller structure. It is filled in by reading +** the PLAYER.INI and overridden through program control. +*/ +VQAConfig AnimControl; + +int PreserveVQAScreen; // Used for screen mode transition control. +bool BreakoutAllowed = true; // "true" if aborting of movies is allowed. +bool Brokeout; // Was the movie broken out of? +bool SlowPalette = true; // Slow palette flag set? + + +/*************************************************************************** +** These are the movie names to use for mission briefing, winning, and losing +** sequences. They are read from the INI file. +*/ +char IntroMovie[_MAX_FNAME+_MAX_EXT]; +char BriefMovie[_MAX_FNAME+_MAX_EXT]; +char WinMovie[_MAX_FNAME+_MAX_EXT]; +char LoseMovie[_MAX_FNAME+_MAX_EXT]; +char ActionMovie[_MAX_FNAME+_MAX_EXT]; +char BriefingText[512]; +ThemeType TransitTheme = THEME_NONE; + + +/*************************************************************************** +** This records the view hotspots for the player. These are the cell numbers +** of the upper left corner for the view position. +*/ +CELL Views[4]; + + +/*************************************************************************** +** This is the pending speech sample to play. This sample will be played +** at the first opportunity. +*/ +VoxType SpeakQueue = VOX_NONE; + + +/*************************************************************************** +** This records if the score (music) file is present. If not, then much of +** the streaming score system can be disabled. +*/ +bool ScoresPresent; + + +/*************************************************************************** +** This flag will control whether there is a response from game units. +** By carefully controlling this global, multiple responses are supressed +** when a large group of infantry is given the movement order. +*/ +bool AllowVoice = true; + + +/*************************************************************************** +** This counts the number of crates on the map. When this value reaches zero, +** then a timer is started that will control crate creation. +*/ +int CrateCount; +TCountDownTimerClass CrateTimer; +bool CrateMaker = false; + + +/*************************************************************************** +** This is the current frame number. This number is guaranteed to count +** upward at the rate of one per game logic process. The target rate is 15 +** per second. This value is saved and restored with the saved game. +*/ +long Frame = 0; + + +/*************************************************************************** +** These globals are constantly monitored to determine if the player +** has won or lost. They get set according to the trigger events associated +** with the scenario. +*/ +bool PlayerWins; +bool PlayerLoses; +bool PlayerRestarts; + +/* +** This flag is set if the player neither wins nor loses; it's mostly for +** multiplayer mode. +*/ +bool PlayerAborts; + + +/*************************************************************************** +** This is the pointer for the speech staging buffer. This buffer is used +** to hold the currently speaking voice data. Since only one speech sample +** is played at a time, this buffer is only as big as the largest speech +** sample that can be played. +*/ +void * SpeechBuffer; + + +/*************************************************************************** +** This is a running accumulation of the number of ticks that were unused. +** This accumulates into a useful value that contributes to a +** histogram of game performance. +*/ +long SpareTicks; + + +/*************************************************************************** +** This is a special scenario count down value. End of game condition will +** not be checked until this value reaches zero. +*/ +int EndCountDown; + + +/*************************************************************************** +** When the player sabotages a building (scenario #6 GDI only) then when +** the next scenario starts, that building will already be destroyed. +*/ +StructType SabotagedType; + + +/*************************************************************************** +** If the Nod temple was destroyed by the ion cannon, then this flag will +** be set to true. +*/ +bool TempleIoned = false; + + +/*************************************************************************** +** This is the monochrome debug page array. The various monochrome data +** screens are located here. +*/ +MonoClass MonoArray[MonoClass::MAX_MONO_PAGES]; +int MonoPage; // The current page. + + +/*************************************************************************** +** This is true if the game is the currently in focus windows app +** +*/ +bool GameInFocus; + +/*************************************************************************** +** This holds the theater specific mixfiles. +*/ +MixFileClass *TheaterData = NULL; +MixFileClass *TheaterIcons = NULL; +MixFileClass *LowTheaterData; +MixFileClass *MoviesMix = 0; +MixFileClass *GeneralMix = 0; +MixFileClass *ScoreMix = 0; + + +/*************************************************************************** +** This is the options control class. The options control such things as +** game speed, visual controls, and other user settings. +*/ +GameOptionsClass Options; + + +/*************************************************************************** +** Logic processing is controlled by this element. It handles both graphic +** and AI logic. +*/ +LogicClass Logic; + + +/*************************************************************************** +** This handles the background music. +*/ +ThemeClass Theme; + + +/*************************************************************************** +** This is the main control class for the map. +*/ +#ifdef SCENARIO_EDITOR +MapEditClass Map; +#else +MouseClass Map; +#endif + +/************************************************************************** +** The running game score is handled by this class (and member functions). +*/ +ScoreClass Score; + + +/*************************************************************************** +** The running credit display is controlled by this class (and member +** functions. +*/ +CreditClass CreditDisplay; + + +/*************************************************************************** +** These are the bits that are set when the appropriate tutor message +** has been displayed. Once the message has been displayed, it will not be +** displayed again. +*/ +long TutorFlags[2]; + + +/************************************************************************** +** This class records the special command override options that C&C +** supports. +*/ +SpecialClass Special; + + +/*************************************************************************** +** This is the scenario data for the currently loaded scenario. +** These variables should all be set together. +*/ +HousesType Whom; // Initial command line house choice. +unsigned Scenario; // Scenario # +ScenarioPlayerType ScenPlayer; // GDI, NOD, 2-Player, Multi-Player +ScenarioDirType ScenDir; // East/West +ScenarioVarType ScenVar; // variation A/B/C +char ScenarioName[_MAX_FNAME+_MAX_EXT]; // name of scenario +int CarryOverMoney; // Carry over money from last scenario. +int CarryOverPercent; // Carry over money percentage control. +int CarryOverCap; // Maxmimum carry over money allowed. +bool ScenarioInit; +bool SpecialFlag = false; + + +/*************************************************************************** +** This value tells the sidebar what items it's allowed to add. The +** lower the value, the simpler the sidebar will be. +*/ +unsigned BuildLevel = 3; // Buildable level (1 = simplest) + + +/*************************************************************************** +** This value is computed every time a new scenario is loaded; it's a +** CRC of the INI and binary map files. +*/ +unsigned long ScenarioCRC; + + +/*************************************************************************** +** The various tutor and dialog messages are located in the data block +** referenced by this pointer. +*/ +char const * SystemStrings; + + +/*************************************************************************** +** The game plays as long as this var is true. +*/ +bool GameActive; + + +/*************************************************************************** +** This is a scratch variable that is used to when a reference is needed to +** a long, but the value wasn't supplied to a function. This is used +** specifically for the default reference value. As such, it is not stable. +*/ +long LParam; + + +#ifdef SCENARIO_EDITOR +/*************************************************************************** +** The currently-selected cell for the Scenario Editor +*/ +CELL CurrentCell = 0; +#endif + + +/*************************************************************************** +** Most of the text in the game will use the six point font. These are the +** pointers to the fonts. If it is NULL, then the font hasn't been loaded +** yet. +*/ +void const *Green12FontPtr; // Green font for pressed in tabs +void const *Green12GradFontPtr; // Graduated green font for tabs +void const *MapFontPtr; // Standard very small font. +void const *Font3Ptr; // Standard very small font. +void const *Font6Ptr; // Standard small font. +void const *Font8Ptr; // 8 point proportional. +void const *FontLEDPtr; // LED fixed point font. +void const *VCRFontPtr; // VCR font pointer. +void const *ScoreFontPtr; // font for score & map selection screens +void const *GradFont6Ptr; // gradient 6 point font pointer. + + +/*************************************************************************** +** This is the house that the human player is currently playing. +*/ +HouseClass * PlayerPtr; + + +/*************************************************************************** +** Special palettes for MCGA mode goes here. These palette buffers are used +** for pictures that do not use the game palette or are used for fading to +** black. +*/ +unsigned char *GamePalette; +unsigned char *BlackPalette; +unsigned char *WhitePalette; +unsigned char *OriginalPalette; +unsigned char *Palette; + + +/*************************************************************************** +** These are the event queues. One is for holding events until they are ready to be +** sent to the remote computer for processing. The other list is for incoming events +** that need to be executed when the correct frame has been reached. +*/ +QueueClass OutList; +QueueClass DoList; + + +/*************************************************************************** +** These are arrays/lists of trigger pointers for each cell & the houses. +*/ +DynamicVectorClass CellTriggers; +DynamicVectorClass HouseTriggers[HOUSE_COUNT]; + + +/*************************************************************************** +** This is an array of waypoints; each waypoint corresponds to a letter of +** the alphabet, and points to a cell number. -1 means unassigned. +** The CellClass has a bit that tells if that cell has a waypoint attached to +** it; the only way to find which waypoint it is, is to scan this array. This +** shouldn't be needed often; usually, you know the waypoint & you want the CELL. +*/ +CELL Waypoint[WAYPT_COUNT]; + + +/*************************************************************************** +** This is the list of BuildingTypes that define the AI's base. +*/ +BaseClass Base; + + +/*************************************************************************** +** This value tells what type of multiplayer game we're playing. +*/ +GameType GameToPlay = GAME_NORMAL; + + +/*************************************************************************** +** This is the current communications protocol +*/ +CommProtocolType CommProtocol; + + +/*************************************************************************** +** These values are used for recording & playing back a game. +*/ +CCFileClass RecordFile ("RECORD.BIN"); +int RecordGame = 0; // 1 = record a game +int SuperRecord = 0; // 1 = reopen record file with every write +int PlaybackGame= 0; // 1 = play back a game +int AllowAttract = 0; // 1 = allow attract mode + + +/*************************************************************************** +** This is the null modem manager class. Declaring this class doesn't +** perform any allocations; +** the class itself is ?? bytes. +*/ +bool ModemService = true; // When false disable servicing modem. +NullModemClass NullModem ( + 16, // number of send entries + 64, // number of receive entries +// sizeof (EventClass) * MAX_EVENTS, // maxlen of entry buffer + (200 / sizeof(EventClass) ) * sizeof(EventClass) + sizeof( CommHeaderType ), + 0x1234); // Magic number must have each digit unique + // and different from the queue magic number + +DynamicVectorClass PhoneBook; +int CurPhoneIdx; // current phonebook index, for dialing + +DynamicVectorClass InitStrings; + +SerialSettingsType SerialDefaults; // serial port default settings + +ModemGameType ModemGameToPlay; // type of modem play Dialer, answerer, null + +char *DialMethodCheck[ DIAL_METHODS ] = { + "T", + "P" +}; + +char *CallWaitStrings[ CALL_WAIT_STRINGS_NUM ] = { + "*70,", + "70#,", + "1170,", + "CUSTOM - " +}; + + +/*************************************************************************** +** Index into scenario description list box +*/ +int ScenarioIdx; + + +/*************************************************************************** +** This array of flags tells if the given colors have been used, or are +*/ +int ColorUsed[MAX_MPLAYER_COLORS]; + + +/*************************************************************************** +** This string stores the player's name. +*/ +char MPlayerName[MPLAYER_NAME_MAX]; + + +/*************************************************************************** +** This is the array of remap colors. Each player in a network game is +** assigned one of these colors. The 'G' is for graphics drawing; the 'T' +** is for text printing (indicates a remap table for the font to use). +*/ +int MPlayerGColors[MAX_MPLAYER_COLORS] = { + 5, // Yellow + 127, // Red + 135, // BlueGreen + 26, // Orange + 4, // Green + 202 // Blue-Grey +}; + +int MPlayerTColors[MAX_MPLAYER_COLORS] = { + CC_GDI_COLOR, // Yellow + CC_NOD_COLOR, // Red + CC_BLUE_GREEN, // BlueGreen + CC_ORANGE, // Orange //26 + CC_GREEN, // Green + CC_BLUE_GREY, // Blue +}; + + +/*************************************************************************** +** This is a list of all the names of the multiplayer scenarios that use +** bases (production), and those that don't. There is a list for +** descriptions, and another for actual filenames. +*/ +char MPlayerDescriptions[100][40]; +DynamicVectorClass MPlayerScenarios; +DynamicVectorClass MPlayerFilenum; + + +/*************************************************************************** +** This value determines the max allowable # of players. +*/ +int MPlayerMax = 4; + + +/*************************************************************************** +** Multiplayer game options +*/ +int MPlayerPrefColor; // preferred color index for this player +int MPlayerColorIdx; // actual color index of this player +HousesType MPlayerHouse; // House of this player (GDI/NOD) +unsigned char MPlayerLocalID; // ID of this player +int MPlayerCount; // # of human players in this game +int MPlayerBases; // 1 = bases are on for this scenario +int MPlayerCredits; // # credits everyone gets +int MPlayerTiberium; // 1 = tiberium enabled for this scenario +int MPlayerGoodies; // 1 = goodies enabled for this scenario +int MPlayerGhosts; // 1 = houses with no players will still play +int MPlayerSolo = 0; // 1 = allows a single-player net game +int MPlayerUnitCount = 10; // # units for non-base multiplayer scenarios + + +/*--------------------------------------------------------------------------- +Min & Max unit count values; index0 = bases OFF, index1 = bases ON +---------------------------------------------------------------------------*/ +int MPlayerCountMin[2] = {1,0}; +int MPlayerCountMax[2] = {50,12}; + + +/*--------------------------------------------------------------------------- +MPlayerMaxAhead is the number of frames ahead of this one to execute a given +packet. It's set by the RESPONSE_TIME event. +---------------------------------------------------------------------------*/ +unsigned long MPlayerMaxAhead = 3; + + +/*--------------------------------------------------------------------------- +'FrameSendRate' is the # frames between data packets +'FrameRateDelay' is the time ticks to wait between frames, for smoothing. +---------------------------------------------------------------------------*/ +unsigned long FrameSendRate; + + +/*************************************************************************** +** Multiplayer ID's, stored in order of event execution. +** Format: +** bits 0-3: the "preferred" house of the player (GDI/NOD) +** bits 4-7: the player's Color Index +** These values are used as the IPX connection ID's. +*/ +unsigned char MPlayerID [MAX_PLAYERS]; + + +/*************************************************************************** +** This array stores the actual HousesType for all players (MULT1, etc). +*/ +HousesType MPlayerHouses [MAX_PLAYERS]; + + +/*************************************************************************** +** This array stores the names of all players in a multiplayer game. +*/ +char MPlayerNames [MAX_PLAYERS][MPLAYER_NAME_MAX]; + + +/*************************************************************************** +** This is a list of the messages received from / sent to other players, +** the address to send to (IPX only), and the last message received or +** sent (for the computer's messages). +*/ +MessageListClass Messages; +IPXAddressClass MessageAddress; +char LastMessage[MAX_MESSAGE_LENGTH]; + + +/*************************************************************************** +** If this flag is set, computer AI will blitz the humans all at once; +** otherwise, the computer units trickle gradually out. +*/ +int MPlayerBlitz = 0; + + +/*************************************************************************** +** If this flag is set, we can move around the map, but we can't do anything. +** It means we've been defeated, but we're still allowed to watch the action. +*/ +int MPlayerObiWan = 0; + + +/*************************************************************************** +** These variables keep track of the multiplayer game scores. +*/ +MPlayerScoreType MPlayerScore[MAX_MULTI_NAMES]; +int MPlayerGamesPlayed; // # games played this run +int MPlayerNumScores; // # active entries in MPlayerScore +int MPlayerWinner; // index of winner of last game +int MPlayerCurGame; // index of current game being played + + +// +// This array stores the processing time required by all multiplayer systems. +// The values are stored in the same order as the 'MPlayerID' array. +// +int TheirProcessTime[MAX_PLAYERS - 1]; +int DesiredFrameRate; + + +/*************************************************************************** +** These values are used purely for the Mono debug display. They show the +** names of the Global Channel packet types, and the event types. +*/ +char *GlobalPacketNames[] = { + "Game?", + "Game!", + "Player?", + "Player!", + "Join?", + "Join!", + "Reject", + "GameOptions", + "Sign Off", + "GO!", + "Message", + "Ping" +}; + + +// yeah, there's 100 empty names here, because the SerialCommandType starts at 100. +char *SerialPacketNames[] = { + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "CONNECT", + "GAME_OPTIONS", + "SIGN_OFF", + "GO", + "MESSAGE", + "TIMING", + "SCORE_SCREEN", + "LAST_COMMAND", +}; + + +/*************************************************************************** +** These variables are just to help find sync bugs. +*/ +long TrapFrame = 0x7fffffff; // frame to start trapping object values at +RTTIType TrapObjType = RTTI_NONE; // type of object to trap +TrapObjectType TrapObject = {NULL}; // ptr to object being trapped +COORDINATE TrapCoord = 0; // COORD of object to trap +void *TrapThis = NULL; // 'this' ptr of object to trap +CellClass *TrapCell = NULL; // for trapping a cell +int TrapCheckHeap = 0; // start checking the Heap + + +/*************************************************************************** +** This is the network IPX manager class. It handles multiple remote +** connections. Declaring this class doesn't perform any allocations; +** the class itself is 140 bytes. +*/ +IPXManagerClass Ipx ( + sizeof (GlobalPacketType), // size of Global Channel packets + ((546 - sizeof(CommHeaderType)) / sizeof(EventClass) ) * sizeof(EventClass), + 10, // # entries in Global Queue + 8, // # entries in Private Queues + VIRGIN_SOCKET, // Socket ID # + IPXGlobalConnClass::COMMAND_AND_CONQUER); // Product ID # + + +//#if(TIMING_FIX) +// +// These values store the min & max frame #'s for when MaxAhead >>increases<<. +// If MaxAhead increases, and the other systems free-run to the new MaxAhead +// value, they may miss an event generated after the MaxAhead event was sent, +// but before it executed, since it will have been scheduled with the older, +// shorter MaxAhead value. This will cause a Packet_Received_Too_Late error. +// The frames from the point where the new MaxAhead takes effect, up to that +// frame Plus the new MaxAhead, represent a "period of vulnerability"; any +// events received that are scheduled to execute during this period should +// be re-scheduled for after that period. +// +int NewMaxAheadFrame1; +int NewMaxAheadFrame2; +//#endif + +/*************************************************************************** +** This is the user-specified IPX address of a desired game owner machine. +** Use this to cross a bridge. Only the 1st 4 numbers in the address are +** used; the rest are set to ff's, for broadcasting. 'IsBridge' is set +** if this address should be used. +*/ +int IsBridge = 0; +IPXAddressClass BridgeNet; + + +/*************************************************************************** +** This flag is true if the user has requested that this game be "secret" +** (The game will not appear to other systems just starting up.) +*/ +bool NetStealth = false; + + +/*************************************************************************** +** If this flag is true, the user won't receive messages from any player +** other than those in his own game. It defaults to protected mode. +*/ +bool NetProtect = true; + + +/*************************************************************************** +** This flag indicates whether the game is "open" or not to other network players. +*/ +bool NetOpen = false; + + +/*************************************************************************** +** This string stores the game's network name. +** GameName does not include the "'s Game"; comparing GameName to +** PlayerName can determine if this player is the originator of the game. +*/ +char MPlayerGameName[MPLAYER_NAME_MAX]; + + +/*************************************************************************** +** These variables are for servicing the Global Channel. +*/ +GlobalPacketType GPacket; // Global Channel packet +int GPacketlen; // length of incoming packet +IPXAddressClass GAddress; // address of sender +unsigned short GProductID; // sender's Product ID + + +/*************************************************************************** +** This is the "meta-packet"; it's a bunch of events lumped together. +** The packet's size is IPX's max size (546), rounded down to accommodate +** the max number of events possible. +*/ +char *MetaPacket = 0; +int MetaSize = ((546 - sizeof(CommHeaderType)) / sizeof(EventClass) ) * sizeof(EventClass); + + +/*************************************************************************** +** This is the random-number seed; it's synchronized between systems for +** multiplayer games. +*/ +int Seed = 0; +long *RandSeedPtr; + + +/*************************************************************************** +** If this value is non-zero, use it as the random # seed instead; this should +** help reproduce some bugs. +*/ +int CustomSeed = 0; + +int WindowList[][8] = { +/* xbyte, ypixel, bytewid, pixelht, cursor color, bkgd color, cursor x, cursor y */ + + /* do not change the first 2 entries!! they are necc. to the system */ + {0,0,40,200,WHITE,BLACK,0,0}, /* screen window */ + {1,75,38,100,WHITE,BLACK,0,0}, /* DOS Error window */ + + // Tactical map. + {0, 0, 40, 200, WHITE,LTGREY,0,0}, + + // Initial menu window. + {12, 199-42, 16, 42, LTGREY, DKGREY, 0, 0}, + + // Sidebar clipping window. + {0,0,0,0,0,0,0,0}, + + // Scenario editor window. + {5, 30, 30, 140, 0, 0, 0, 0}, + + // Custom window. + {0, 0, 0, 0, 0, 0, 0, 0}, + +}; + + +/* X,Y,Item Width,Items High,Selected,Norm Color,Sel Color,zero */ +int MenuList[][8]={ + {1, 3, 12, 3, 0, WHITE, PINK, 0}, +}; + +GraphicBufferClass VisiblePage; +GraphicBufferClass HiddenPage; + +GraphicViewPortClass SeenBuff(&VisiblePage, 0,0,640,480); +GraphicBufferClass ModeXBuff; +GraphicViewPortClass HidPage(&HiddenPage, 0,0,640,480); +GraphicBufferClass SysMemPage(DEFAULT_SCREEN_WIDTH, 200, (void*)NULL); +int SoundOn; +CountDownTimerClass FrameTimer(BT_SYSTEM, 0L); +CountDownTimerClass DebugTimer(BT_SYSTEM, 0L); +CountDownTimerClass CountDownTimer(BT_SYSTEM, 0L); + +NewConfigType NewConfig; + +/*************************************************************************** +** This timer measures how long (in ticks) it takes to process the game's +** logic, with no packet processing or artificial delays. +*/ +TimerClass ProcessTimer; +int ProcessTicks; // accumulated ticks +int ProcessFrames; // # frames used to measure 'ProcessTicks' + + +/*************************************************************************** +** This flag is for popping up dialogs that call the main loop. +*/ +SpecialDialogType SpecialDialog = SDLG_NONE; + + +/* +** This flags if used to tell can enter cell that we are in a find path +** check and thus should not uncloak units via Can_Enter_Cell. +*/ +//bool IsFindPath = false; + + +/*************************************************************************** +** Globals for the network Dialogs. +*/ + +/* +** List of all games out there, & the address of the game's owner +*/ +DynamicVectorClass Games; + +/* +** List of names & addresses of all the players in the game I'm joining. +** This is the really critical list, since it's used to form connections with +** all other players in my game. It's updated when I get a response to my +** outgoing query, or when I get a query from another system in my game asking +** who I am. This double-insurance means that if any system knows about me, +** I know about them too. The only catch is that if the game is started very, +** very soon after a player joins, not everyone may know about him; to prevent +** this, a timer restriction is put on the New Game dialog's GO button. +*/ +DynamicVectorClass Players; + +char *DebugFname; // for stoopid debugging purposes +int DebugLine; // for stoopid debugging purposes +#ifdef DEMO +int RequiredCD = -2; +#else +int RequiredCD = -1; +#endif +int MouseInstalled; + +/* +** Certain options must be enabled by both a command-line option, and an +** an entry in an INI file. If this flag is 'true', those options have been +** enabled by the INI file. +*/ +int AreThingiesEnabled = false; + + +/* +** Pointer to windows timer object +** +** +*/ + +WinTimerClass *WindowsTimer=NULL; + + +/* +** Command line arguments +** +** +*/ +char * Argv[20]; //Pointers to command line arguments +int Argc; //Command line argument count + + +WWKeyboardClass Kbd; +int ScreenWidth=640; +int ScreenHeight=400; +WWMouseClass *WWMouse = NULL; +HANDLE hInstance; +int AllDone; +BOOL InMovie = FALSE; //Are we currently playing a VQ movie? +bool MMXAvailable = false; //Does this CPU support MMX extensions? +GetCDClass CDList; +bool GameStatisticsPacketSent; +bool ConnectionLost; + +TheaterType LastTheater = THEATER_NONE; + diff --git a/GOPTIONS.CPP b/GOPTIONS.CPP new file mode 100644 index 0000000..bf1e0ae --- /dev/null +++ b/GOPTIONS.CPP @@ -0,0 +1,587 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\goptions.cpv 2.17 16 Oct 1995 16:50:26 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : July 27, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * OptionsClass::Process -- Handles all the options graphic interface. * + * Draw_Caption -- Draws a caption on a dialog box. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#include "goptions.h" +#include "loaddlg.h" +#include "sounddlg.h" +#include "visudlg.h" +#include "gamedlg.h" +#include "textbtn.h" +#include "confdlg.h" +#include "descdlg.h" + +void GameOptionsClass::Adjust_Variables_For_Resolution(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + OptionWidth = (216+8) * factor; + OptionHeight = 100 * factor; + OptionX = ((SeenBuff.Get_Width() - OptionWidth) / 2); + OptionY = ((SeenBuff.Get_Height() - OptionHeight) / 2); + ButtonWidth = 130 * factor; + OButtonHeight = 9 * factor; + CaptionYPos = 5 * factor; + ButtonY = 21 * factor; + Border1Len = 72 * factor; + Border2Len = 16 * factor; + ButtonResumeY = (OptionHeight - (15 * factor)); +} +/*********************************************************************************************** + * OptionsClass::Process -- Handles all the options graphic interface. * + * * + * This routine is the main control for the visual representation of the options * + * screen. It handles the visual overlay and the player input. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: 12/31/1994 MML : Created. * + * 06/23/1995 JLB : Handles restating the mission objective. * + * 07/27/1995 JLB : Adjusts menu for multiplay mode. * + *=============================================================================================*/ +void GameOptionsClass::Process(void) +{ + static struct { + int ID; // Button ID to use. + int Text; // Text number to use for this button. + bool Multiplay; // Allowed in multiplayer version? + } _constants[] = { + {BUTTON_LOAD, TXT_LOAD_MISSION, false}, + {BUTTON_SAVE, TXT_SAVE_MISSION, false}, + {BUTTON_DELETE, TXT_DELETE_MISSION, true}, + {BUTTON_GAME, TXT_GAME_CONTROLS, true}, + {BUTTON_QUIT, TXT_QUIT_MISSION, true}, + {BUTTON_RESUME, TXT_RESUME_MISSION, true}, + {BUTTON_RESTATE, TXT_RESTATE_MISSION, false}, + }; + + /* + ** Variables. + */ + TextButtonClass * buttons = 0; + int selection; + bool pressed; + int curbutton = 6; + int y; + TextButtonClass *buttonsel[sizeof(_constants)/sizeof(_constants[0])]; + + Set_Logic_Page(SeenBuff); + + /* + ** Build the button list for all of the buttons for this dialog. + */ + int maxwidth = 0; + int resfactor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + for (int index = 0; index < sizeof(_constants)/sizeof(_constants[0]); index++ ) { + int text = _constants[index].Text; + buttonsel[index] = NULL; + + if (GameToPlay != GAME_NORMAL && !_constants[index].Multiplay) { + buttonsel[index] = 0; + continue; + } + + if (GameToPlay != GAME_NORMAL && text == TXT_DELETE_MISSION) { + text = TXT_RESIGN; + } + + if (index < 5) { + y = (SeenBuff.Get_Height() - OptionHeight)/2 + ButtonY + ((OButtonHeight+2) * index); + } else { + y = OptionY + ButtonResumeY; + } + + TextButtonClass * g = new TextButtonClass(_constants[index].ID, + text, TPF_6PT_GRAD|TPF_NOSHADOW, 0, y); + + if (g->Width > maxwidth) { + maxwidth = g->Width; + } + if (!buttons) { + buttons = g; + } else { + g->Add_Tail(*buttons); + } + + buttonsel[index] = g; + } + + buttonsel[curbutton-1]->Turn_On(); + + /* + ** Force all button lengths to match the maximum length of the widest button. + */ + GadgetClass * g = buttons; + while (g) { + g->Width = MAX(maxwidth, 90 * resfactor); + g->X = OptionX+(OptionWidth-g->Width)/2; + g = g->Get_Next(); + } +#ifdef FRENCH + buttonsel[BUTTON_RESUME-1]->Width = 104 *resfactor; +#else + buttonsel[BUTTON_RESUME-1]->Width = 90 *resfactor; +#endif + buttonsel[BUTTON_RESUME-1]->X = OptionX+(5 * resfactor); + + if (GameToPlay == GAME_NORMAL) { + buttonsel[BUTTON_RESTATE-1]->Width = 90 * resfactor; + buttonsel[BUTTON_RESTATE-1]->X = OptionX+OptionWidth-(buttonsel[BUTTON_RESTATE-1]->Width+(5 * resfactor)); + } + + /* + ** This causes left mouse button clicking within the confines of the dialog to + ** be ignored if it wasn't recognized by any other button or slider. + */ + (new GadgetClass(OptionX, OptionY, OptionWidth, OptionHeight, GadgetClass::LEFTPRESS))->Add_Tail(*buttons); + + /* + ** This cause a right click anywhere or a left click outside the dialog region + ** to be equivalent to clicking on the return to game button. + */ + (new ControlClass(BUTTON_RESUME, 0, 0, SeenBuff.Get_Width(), SeenBuff.Get_Height(), GadgetClass::LEFTPRESS|GadgetClass::RIGHTPRESS))->Add_Tail(*buttons); + + Keyboard::Clear(); + + Fancy_Text_Print(TXT_NONE, 0, 0, CC_GREEN, TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + /* + ** Main Processing Loop. + */ + bool display = true; + bool process = true; + pressed = false; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=TRUE; + } + + /* + ** Invoke game callback. + */ + if (GameToPlay == GAME_NORMAL) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + } + } + + /* + ** Refresh display if needed. + */ + if (display) { + + /* + ** Redraw the map. + */ + HiddenPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + + /* + ** Reset up the window. Window x-coords are in bytes not pixels. + */ + Set_Window(WINDOW_EDITOR, OptionX, OptionY, OptionWidth, OptionHeight); + Hide_Mouse(); + + /* + ** Draw the background. + */ + Window_Box (WINDOW_EDITOR, BOXSTYLE_GREEN_BORDER); // has border, raised up + + /* + ** Draw the arrows border if requested. + */ + Draw_Caption(TXT_OPTIONS, OptionX, OptionY, OptionWidth); + + /* + ** Display the version number at the bottom of the dialog box. + */ +#ifdef DEMO + Version_Number(); + Fancy_Text_Print("DEMO%s", + ((WindowList[WINDOW_EDITOR][WINDOWX]+WindowList[WINDOW_EDITOR][WINDOWWIDTH])<<3)-3*resfactor, + WindowList[WINDOW_EDITOR][WINDOWY]+WindowList[WINDOW_EDITOR][WINDOWHEIGHT]-((GameToPlay == GAME_NORMAL) ? (32*resfactor) : (24*resfactor)), + DKGREY, TBLACK, + TPF_6POINT|TPF_NOSHADOW|TPF_RIGHT, + ScenarioName, + VersionText); +#else + Fancy_Text_Print("%s\rV.%d%s", + ((WindowList[WINDOW_EDITOR][WINDOWX]+WindowList[WINDOW_EDITOR][WINDOWWIDTH])<<3)-3*resfactor, + WindowList[WINDOW_EDITOR][WINDOWY]+WindowList[WINDOW_EDITOR][WINDOWHEIGHT]-((GameToPlay == GAME_NORMAL) ? (32*resfactor) : (24*resfactor)), + DKGREY, TBLACK, + TPF_6POINT|TPF_NOSHADOW|TPF_RIGHT, + ScenarioName, + Version_Number(), + VersionText); +#endif + + buttons->Draw_All(); + TabClass::Hilite_Tab(0); + Show_Mouse(); + display = false; + } + + /* + ** Get user input. + */ + KeyNumType input = buttons->Input(); + + /* + ** Process Input. + */ + switch (input) { + case (BUTTON_RESTATE | KN_BUTTON): + selection = BUTTON_RESTATE; + pressed = true; + break; + + case (BUTTON_LOAD | KN_BUTTON): + selection = BUTTON_LOAD; + pressed = true; + break; + + case (BUTTON_SAVE | KN_BUTTON): + selection = BUTTON_SAVE; + pressed = true; + break; + + case (BUTTON_DELETE | KN_BUTTON): + selection = BUTTON_DELETE; + pressed = true; + break; + + case (BUTTON_QUIT | KN_BUTTON): + selection = BUTTON_QUIT; + pressed = true; + break; + + case (BUTTON_GAME | KN_BUTTON): + selection = BUTTON_GAME; + pressed = true; + break; + + case (KN_ESC): + case (BUTTON_RESUME | KN_BUTTON): + selection = BUTTON_RESUME; + pressed = true; + break; + + case (KN_UP): + buttonsel[curbutton-1]->Turn_Off(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + curbutton--; + if (GameToPlay == GAME_NORMAL) { + if (curbutton < BUTTON_LOAD) { + curbutton = (BUTTON_COUNT - 1); + } + } else { + if (curbutton < BUTTON_DELETE) { + curbutton = BUTTON_RESUME; +// curbutton = (BUTTON_COUNT-1); + } + } + buttonsel[curbutton-1]->Turn_On(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + break; + + case (KN_DOWN): + buttonsel[curbutton-1]->Turn_Off(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + curbutton++; + if (GameToPlay == GAME_NORMAL) { + if (curbutton >= BUTTON_COUNT) { + curbutton = BUTTON_LOAD; + } + } else { + if (curbutton > BUTTON_RESUME) { + curbutton = BUTTON_DELETE; + } + } + buttonsel[curbutton-1]->Turn_On(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + break; + + case (KN_RETURN): + buttonsel[curbutton-1]->IsPressed = true; + buttonsel[curbutton-1]->Draw_Me(true); + selection = curbutton; + pressed = true; + break; + + default: + break; + } + + if (pressed) { + buttonsel[curbutton-1]->Turn_Off(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + curbutton = selection; + buttonsel[curbutton-1]->Turn_On(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + + switch (selection) { + case BUTTON_RESTATE: + display = true; +#ifdef JAPANESE + if (!Restate_Mission(ScenarioName, TXT_VIDEO, TXT_TAB_BUTTON_CONTROLS)) { +#else + if (!Restate_Mission(ScenarioName, TXT_VIDEO, TXT_OPTIONS)) { +#endif + BreakoutAllowed = true; + char buffer[25]; + sprintf(buffer, "%s.VQA", BriefMovie); + if (CCFileClass(buffer).Is_Available()) { + Play_Movie(BriefMovie); + } else { + Play_Movie(ActionMovie); + } + //BreakoutAllowed = false; + memset(BlackPalette, 0x01, 768); + Set_Palette(BlackPalette); + memset(BlackPalette, 0x00, 768); + Set_Palette(BlackPalette); + Map.Flag_To_Redraw(true); + Theme.Queue_Song(THEME_PICK_ANOTHER); + process = false; + } + break; + + case (BUTTON_LOAD): + display = true; + if (LoadOptionsClass(LoadOptionsClass::LOAD).Process()) { + process = false; + } + break; + + case (BUTTON_SAVE): + display = true; + LoadOptionsClass(LoadOptionsClass::SAVE).Process(); + break; + + case (BUTTON_DELETE): + display = true; + if (GameToPlay != GAME_NORMAL) { + if (Surrender_Dialog()) { + OutList.Add(EventClass(EventClass::DESTRUCT)); + } + process = false; + } else { + LoadOptionsClass(LoadOptionsClass::WWDELETE).Process(); + } + break; + + case (BUTTON_QUIT): + if (GameToPlay == GAME_NORMAL) { +#ifdef JAPANESE + switch (CCMessageBox().Process(TXT_CONFIRM_EXIT, TXT_YES, TXT_NO, TXT_RESTART)) { +#else + switch (CCMessageBox().Process(TXT_CONFIRM_EXIT, TXT_ABORT, TXT_CANCEL, TXT_RESTART)) { +#endif + case 2: + display = true; + break; + + case 0: + process = false; + Queue_Exit(); + break; + + case 1: + PlayerRestarts = true; + process = false; + break; + } + } else { + if (ConfirmationClass().Process(TXT_CONFIRM_EXIT)) { + process = false; + Queue_Exit(); + } else { + display = true; + } + } + break; + + case (BUTTON_GAME): + display = true; + GameControlsClass().Process(); + break; + + case (BUTTON_RESUME): + //Save_Settings(); + process = false; + display = true; + break; + } + + pressed = false; + buttonsel[curbutton-1]->IsPressed = false; + //buttonsel[curbutton-1]->Turn_Off(); + buttonsel[curbutton-1]->Turn_On(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + } + } + + /* + ** Clean up and re-enter the game. + */ + buttons->Delete_List(); + + /* + ** Redraw the map. + */ + Keyboard::Clear(); + Call_Back(); + HiddenPage.Clear(); + Call_Back(); + Map.Flag_To_Redraw(true); + Map.Render(); +} + + +/*********************************************************************************************** + * Draw_Caption -- Draws a caption on a dialog box. * + * * + * This routine draws the caption text and any fancy filigree that the dialog may require. * + * * + * INPUT: text -- The text of the caption. This is the text number. * + * * + * x,y -- The dialog box X and Y pixel coordinate of the upper left corner. * + * * + * w -- The width of the dialog box (in pixels). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/23/1995 JLB : Created. * + *=============================================================================================*/ +void Draw_Caption(int text, int x, int y, int w) +{ + OptionControlType option = OPTION_NONE; + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + + /* + ** Determine the filigree to use depending on the text of the caption. + */ + switch (text) { + case TXT_GAME_CONTROLS: + case TXT_OPTIONS: + option = OPTION_CONTROLS; + break; + + case TXT_LOAD_MISSION: + case TXT_SAVE_MISSION: + case TXT_DELETE_MISSION: + option = OPTION_DELETE; + break; + + case TXT_NONE: + case TXT_MODEM_SERIAL: + case TXT_SELECT_MPLAYER_GAME: + case TXT_SELECT_SERIAL_GAME: + option = OPTION_DIALOG; + break; + + case TXT_HOST_SERIAL_GAME: + case TXT_JOIN_SERIAL_GAME: + option = OPTION_SERIAL; + break; + + case TXT_SETTINGS: + case TXT_PHONE_LIST: + case TXT_PHONE_LISTING: + option = OPTION_PHONE; + break; + + case TXT_JOIN_NETWORK_GAME: + option = OPTION_JOIN_NETWORK; + break; + + case TXT_NETGAME_SETUP: + option = OPTION_NETWORK; + break; + + case TXT_VISUAL_CONTROLS: + option = OPTION_VISUAL; + break; + + case TXT_SOUND_CONTROLS: + option = OPTION_SOUND; + break; + + default: + option = OPTION_DIALOG; + break; + } + + /* + ** Draw the filigree at the corners of the dialog. + */ + if (option != OPTION_NONE) { + CC_Draw_Shape(MixFileClass::Retrieve("OPTIONS.SHP"), (int)option, x+12, y+11, WINDOW_MAIN, SHAPE_CENTER); + CC_Draw_Shape(MixFileClass::Retrieve("OPTIONS.SHP"), (int)option+1, x+w-14, y+11, WINDOW_MAIN, SHAPE_CENTER); + } + + /* + ** Draw the caption. + */ + if (text != TXT_NONE) { + Fancy_Text_Print(text, w/2 + x, 5*factor + y, CC_GREEN, TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + int length = String_Pixel_Width(Text_String(text)); + LogicPage->Draw_Line((x+(w/2))-(length/2), y+FontHeight+FontYSpacing + 5*factor, (x+(w/2))+(length/2), y+FontHeight+FontYSpacing + 5*factor, CC_GREEN); + } +} diff --git a/GOPTIONS.H b/GOPTIONS.H new file mode 100644 index 0000000..9de8a0b --- /dev/null +++ b/GOPTIONS.H @@ -0,0 +1,100 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\goptions.h_v 2.19 16 Oct 1995 16:46:26 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : June 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GOPTIONS_H +#define GOPTIONS_H + +#include "options.h" +#include "gadget.h" + + +class GameOptionsClass : public OptionsClass { + enum GameOptionsButtonEnum { + BUTTON_LOAD=1, + BUTTON_SAVE, + BUTTON_DELETE, + BUTTON_GAME, + BUTTON_QUIT, + BUTTON_RESUME, + BUTTON_RESTATE, + + BUTTON_COUNT, + }; + + enum GameOptionsEnum { + #if(0) + + OPTION_WIDTH=(216+8), + OPTION_HEIGHT=100, + OPTION_X=((320 - OPTION_WIDTH) / 2), + OPTION_Y=((200 - OPTION_HEIGHT) / 2), +#ifdef FRENCH + BUTTON_WIDTH=142, +#else + BUTTON_WIDTH=130, +#endif +// OBUTTON_HEIGHT=13, + NUMBER_OF_BUTTONS=6, + CAPTION_Y_POS=5, + BUTTON_Y=21, + BORDER1_LEN=72, + BORDER2_LEN=16, + BUTTON_RESUME_Y=(OPTION_HEIGHT-15) + + #endif + }; + + public: + GameOptionsClass(void): OptionsClass () { }; + void Adjust_Variables_For_Resolution(void); + void Process(void); + + private: + int OptionWidth; + int OptionHeight; + int OptionX; + int OptionY; + int ButtonWidth; + int OButtonHeight; + int CaptionYPos; + int ButtonY; + int Border1Len; + int Border2Len; + int ButtonResumeY; +}; + +#endif diff --git a/GSCREEN.CPP b/GSCREEN.CPP new file mode 100644 index 0000000..3c5c5ae --- /dev/null +++ b/GSCREEN.CPP @@ -0,0 +1,529 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\gscreen.cpv 2.17 16 Oct 1995 16:51:34 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GSCREEN.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : January 19, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * GScreenClass::GScreenClass -- Default constructor for GScreenClass. * + * GScreenClass::One_Time -- Handles one time class setups. * + * GScreenClass::Init -- Init's the entire display hierarchy by calling all Init routines. * + * GScreenClass::Init_Clear -- Sets the map to a known state. * + * GScreenClass::Init_Theater -- Performs theater-specific initializations. * + * GScreenClass::Init_IO -- Initializes the Button list ('Buttons'). * + * GScreenClass::Flag_To_Redraw -- Flags the display to be redrawn. * + * GScreenClass::Blit_Display -- Redraw the display from the hidpage to the seenpage. * + * GScreenClass::Render -- General drawing dispatcher an display update function. * + * GScreenClass::Input -- Fetches input and processes gadgets. * + * GScreenClass::Add_A_Button -- Add a gadget to the game input system. * + * GScreenClass::Remove_A_Button -- Removes a gadget from the game input system. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#include + +GadgetClass * GScreenClass::Buttons = 0; + +GraphicBufferClass * GScreenClass::ShadowPage = 0; + + +/*********************************************************************************************** + * GScreenClass::GScreenClass -- Default constructor for GScreenClass. * + * * + * This constructor merely sets the display system, so that it will redraw the first time * + * the render function is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +GScreenClass::GScreenClass(void) +{ + IsToUpdate = true; + IsToRedraw = true; +} + + +/*********************************************************************************************** + * GScreenClass::One_Time -- Handles one time class setups. * + * * + * This routine (and all those that overload it) must perform truly one-time initialization. * + * Such init's would normally be done in the constructor, but other aspects of the game may * + * not have been initialized at the time the constructors are called (such as the file system, * + * the display, or other WWLIB subsystems), so many initializations should be deferred to the * + * One_Time init's. * + * * + * Any variables set in this routine should be declared as static, so they won't be modified * + * by the load/save process. Non-static variables will be over-written by a loaded game. * + * * + * This function allocates the shadow buffer that is used for quick screen updates. If * + * there were any data files to load, they would be loaded at this time as well. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine only ONCE at the beginning of the game. * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::One_Time(void) +{ + /* + ** Allocate the screen shadow page. This page is used to reduce access to the + ** actual screen memory. It contains a duplicate of what the SEENPAGE is. + */ + Buttons = 0; + ShadowPage = new GraphicBufferClass(320,200); + if (ShadowPage) { + ShadowPage->Clear(); + HiddenPage.Clear(); + } +} + + +/*********************************************************************************************** + * GScreenClass::Init -- Init's the entire display hierarchy by calling all Init routines. * + * * + * This routine shouldn't be overloaded. It's the main map initialization routine, and will * + * perform a complete map initialization, from mixfiles to clearing the buffers. Calling this * + * routine results in calling every initialization routine in the entire map hierarchy. * + * * + * INPUT: * + * theater theater to initialize to * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + *=============================================================================================*/ +void GScreenClass::Init(TheaterType theater) +{ + Init_Clear(); + Init_IO(); + Init_Theater(theater); +} + + +/*********************************************************************************************** + * GScreenClass::Init_Clear -- Sets the map to a known state. * + * * + * This routine (and those that overload it) clears any buffers and variables to a known * + * state. It assumes that all buffers are allocated & valid. The map should be displayable * + * after calling this function, and should draw basically an empty display. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + *=============================================================================================*/ +void GScreenClass::Init_Clear(void) +{ + /* + ** Clear the ShadowPage & HidPage to force a complete shadow blit. + */ + if (ShadowPage) { + ShadowPage->Clear(); + HiddenPage.Clear(); + } + + IsToRedraw = true; +} + + +/*********************************************************************************************** + * GScreenClass::Init_Theater -- Performs theater-specific initializations. * + * * + * This routine (and those that overload it) performs any theater-specific initializations * + * needed. This will include setting the palette, setting up remap tables, etc. This routine * + * only needs to be called when the theater has changed. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + *=============================================================================================*/ +void GScreenClass::Init_Theater(TheaterType ) +{ +} + + +/*********************************************************************************************** + * GScreenClass::Init_IO -- Initializes the Button list ('Buttons'). * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + *=============================================================================================*/ +void GScreenClass::Init_IO(void) +{ + /* + ** Reset the button list. This means that any other elements of the map that need + ** buttons must attach them after this routine is called! + */ + Buttons = 0; + +} + + +/*********************************************************************************************** + * GScreenClass::Flag_To_Redraw -- Flags the display to be redrawn. * + * * + * This function is used to flag the display system whether any rendering is needed. The * + * parameter tells the system either to redraw EVERYTHING, or just that something somewhere * + * has changed and the individual Draw_It functions must be called. When a sub system * + * determines that it needs to render something local to itself, it would call this routine * + * with a false parameter. If the entire screen gets trashed or needs to be rebuilt, then * + * this routine will be called with a true parameter. * + * * + * INPUT: complete -- bool; Should the ENTIRE screen be redrawn? * + * * + * OUTPUT: none * + * * + * WARNINGS: This doesn't actually draw the screen, it merely sets flags so that when the * + * Render() function is called, the appropriate drawing steps will be performed. * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::Flag_To_Redraw(bool complete) +{ + IsToUpdate = true; + if (complete) { + IsToRedraw = true; + } +} + + +/*********************************************************************************************** + * GScreenClass::Input -- Fetches input and processes gadgets. * + * * + * This routine will fetch the keyboard/mouse input and dispatch this through the gadget * + * system. * + * * + * INPUT: key -- Reference to the key code (for future examination). * + * * + * x,y -- Reference to mouse coordinates (for future examination). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::Input(KeyNumType & key, int & x, int & y) +{ + key = Keyboard::Check(); + + x = Keyboard::Mouse_X(); + y = Keyboard::Mouse_Y(); + + if (Buttons) { + + /* + ** If any buttons need redrawing, they will do so in the Input routine, and + ** they should draw themselves to the HidPage. So, flag ourselves for a Blit + ** to show the newly drawn buttons. + */ + if (Buttons->Is_List_To_Redraw()) { + Flag_To_Redraw(false); + } + + GraphicViewPortClass * oldpage= Set_Logic_Page(HidPage); + + key = Buttons->Input(); + + Set_Logic_Page(oldpage); + + } else { + if (key) { + key = Keyboard::Get(); + } + } + AI(key, x, y); + +} + + +/*********************************************************************************************** + * GScreenClass::Add_A_Button -- Add a gadget to the game input system. * + * * + * This will add a gadget to the game input system. The gadget will be processed in * + * subsiquent calls to the GScreenClass::Input() function. * + * * + * INPUT: gadget -- Reference to the gadget that will be added to the input system. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::Add_A_Button(GadgetClass & gadget) +{ + /*------------------------------------------------------------------------ + If this gadget is already in the list, remove it before adding it in: + - If 1st gadget in list, use Remove_A_Button to remove it, to reset the + value of 'Buttons' appropriately + - Otherwise, just call the Remove function for that gadget to remove it + from any list it may be in + ------------------------------------------------------------------------*/ + if (Buttons == &gadget) { + Remove_A_Button(gadget); + } else { + gadget.Remove(); + } + + /*------------------------------------------------------------------------ + Now add the gadget to our list: + - If there are not buttons, start the list with this one + - Otherwise, add it to the tail of the existing list + ------------------------------------------------------------------------*/ + if (Buttons) { + gadget.Add_Tail(*Buttons); + } else { + Buttons = &gadget; + } +} + + +/*********************************************************************************************** + * GScreenClass::Remove_A_Button -- Removes a gadget from the game input system. * + * * + * INPUT: gadget -- Reference to the gadget that will be removed from the input system. * + * * + * OUTPUT: none * + * * + * WARNINGS: 'gadget' MUST be already a part of 'Buttons', or the new value of 'Buttons' * + * will be invalid! * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::Remove_A_Button(GadgetClass & gadget) +{ + Buttons = gadget.Remove(); +} + + +/*********************************************************************************************** + * GScreenClass::Render -- General drawing dispatcher an display update function. * + * * + * This routine should be called in the main game loop (once every game frame). It will * + * call the Draw_It() function if necessary. All rendering is performed to the LogicPage * + * which is set to the HIDPAGE. After rendering has been performed, the HIDPAGE is * + * copied to the visible page. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This actually updates the graphic display. As a result it can take quite a * + * while to perform. * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::Render(void) +{ + //if (Buttons && Buttons->Is_List_To_Redraw()) { + // IsToRedraw = true; + //} + + + if (IsToUpdate || IsToRedraw) { + + //WWMouse->Erase_Mouse(&HidPage, TRUE); + GraphicViewPortClass * oldpage= Set_Logic_Page(HidPage); + + //if (IsToRedraw) { + // Hide_Mouse(); + // SeenBuff.To_Buffer(0, 0, 320, 200, ShadowPage); + // Show_Mouse(); + //} + Draw_It(IsToRedraw); + + if (Buttons) Buttons->Draw_All(false); + +#ifdef SCENARIO_EDITOR + /* + ** Draw the Editor's buttons + */ + if (Debug_Map) { + if (Buttons) { + Buttons->Draw_All(); + } + } +#endif + /* + ** Draw the multiplayer message system to the Hidpage at this point. + ** This way, they'll Blit along with the rest of the map. + */ + if (Messages.Num_Messages() > 0) { + Messages.Set_Width(Lepton_To_Cell(Map.TacLeptonWidth) * ICON_PIXEL_W); + } + Messages.Draw(); + + Blit_Display(); + IsToUpdate = false; + IsToRedraw = false; + + Set_Logic_Page(oldpage); + } +} + + + +#ifdef CHEAT_KEYS + +#define MAX_SCREENS_SAVED 30*15 // Enough for 30 seconds @ 15 fps + +GraphicBufferClass *ScreenList[MAX_SCREENS_SAVED]; +int CurrentScreen = 0; +bool ScreenRecording = false; + +void Add_Current_Screen(void) +{ + if (ScreenRecording){ + ScreenList[CurrentScreen] = new GraphicBufferClass; + ScreenList[CurrentScreen]->Init ( SeenBuff.Get_Width(), SeenBuff.Get_Height(), NULL, 0, (GBC_Enum) 0); + SeenBuff.Blit (*ScreenList[CurrentScreen]); + + CurrentScreen++; + + if (CurrentScreen == MAX_SCREENS_SAVED){ + + char filename[20]; + for (int i = 0 ; i < MAX_SCREENS_SAVED ; i++){ + sprintf (filename, "SCRN%04d.PCX", i); + Write_PCX_File (filename,*ScreenList[i], (unsigned char *)CurrentPalette); + delete ScreenList[i]; + } + + CurrentScreen = 0; + ScreenRecording = 0; + + } + } +} + +#endif //CHEAT_KEYS + + +extern bool CanVblankSync; + +/*********************************************************************************************** + * GScreenClass::Blit_Display -- Redraw the display from the hidpage to the seenpage. * + * * + * This routine is used to copy the correct display from the HIDPAGE * + * to the SEENPAGE. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/14/1994 JLB : Created. * + * 05/01/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void GScreenClass::Blit_Display(void) +{ + if (SeenBuff.Get_Width()!=320){ +#if (0) + if (HidPage.Get_IsDirectDraw() && (Options.GameSpeed >1 || Options.ScrollRate==6 && CanVblankSync) ){ + WWMouse->Draw_Mouse(&HidPage); + SeenBuff.Get_Graphic_Buffer()->Get_DD_Surface()->Flip(NULL , DDFLIP_WAIT); + SeenBuff.Blit (HidPage , 0 , 0 , 0 , 0 , SeenBuff.Get_Width() , SeenBuff.Get_Height() , (BOOL) FALSE ); +#ifdef CHEAT_KEYS + Add_Current_Screen(); +#endif + //HidPage.Blit ( SeenBuff , 0 , 0 , 0 , 0 , HidPage.Get_Width() , HidPage.Get_Height() , (BOOL) FALSE ); + WWMouse->Erase_Mouse(&HidPage, FALSE); + }else{ +#else //(0) + WWMouse->Draw_Mouse(&HidPage); + HidPage.Blit ( SeenBuff , 0 , 0 , 0 , 0 , HidPage.Get_Width() , HidPage.Get_Height() , (BOOL) FALSE ); +#ifdef CHEAT_KEYS + Add_Current_Screen(); +#endif + WWMouse->Erase_Mouse(&HidPage, FALSE); +#endif //(0) +#if (0) + } +#endif //(0) + + } else { + ModeX_Blit (&HiddenPage); + } + +} + + + + + diff --git a/GSCREEN.H b/GSCREEN.H new file mode 100644 index 0000000..5dd4cb2 --- /dev/null +++ b/GSCREEN.H @@ -0,0 +1,141 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\gscreen.h_v 2.17 16 Oct 1995 16:45:20 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GSCREEN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : December 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GSCREEN_H +#define GSCREEN_H + +#include "function.h" +#include "cell.h" + +class GScreenClass : public VectorClass +{ + public: + + GScreenClass(void); + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time initializations + virtual void Init(TheaterType = THEATER_NONE); // Inits everything + virtual void Init_Clear(void); // Clears all to known state + virtual void Init_IO(void); // Inits button list + virtual void Init_Theater(TheaterType theater); // Theater-specific inits + + /* + ** Player I/O is routed through here. It is called every game tick. + */ + virtual void Input(KeyNumType & key, int & x, int & y); + virtual void AI(KeyNumType &, int, int) {}; + virtual void Add_A_Button(GadgetClass & gadget); + virtual void Remove_A_Button(GadgetClass & gadget); + + /* + ** Called when map needs complete updating. + */ + virtual void Flag_To_Redraw(bool complete=false); + + /* + ** Render maintenance routine (call every game tick). Probably no need + ** to override this in derived classes. + */ + virtual void Render(void); + + /* + ** Is called when actual drawing is required. This is the function to + ** override in derived classes. + */ + virtual void Draw_It(bool =false) {}; + + /* + ** This moves the hidpage up to the seenpage. + */ + static void Blit_Display(void); + + /* + ** Changes the mouse shape as indicated. + */ + virtual void Set_Default_Mouse(MouseType mouse, bool wwsmall) = 0; + virtual bool Override_Mouse_Shape(MouseType mouse, bool wwsmall) = 0; + virtual void Revert_Mouse_Shape(void) = 0; + virtual void Mouse_Small(bool wwsmall) = 0; + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Misc routines. + */ + virtual void * Shadow_Address(void) {return(ShadowPage);}; + + /* + ** This points to the buttons that are used for input. All of the derived classes will + ** attached their specific buttons to this list. + */ + static GadgetClass * Buttons; + + private: + + /* + ** If the entire map is required to redraw, then this flag is true. This flag + ** is set by the Flag_To_Redraw function. Typically, this occurs when the screen + ** has been trashed or is first created. + */ + unsigned IsToRedraw:1; + + /* + ** If only a sub-system of the map must be redrawn, then this flag will be set. + ** An example of something that would set this flag would be an animating icon + ** in the sidebar. In such a case, complete redrawing of the entire display is not + ** necessary, but the Draw_It function should still be called so that the appropriate + ** class can perform it's rendering. + */ + unsigned IsToUpdate:1; + + /* + ** Pointer to an exact copy of the visible graphic page. This copy is used to speed + ** display rendering by using an only-update-changed-pixels algorithm. + */ + public: + static GraphicBufferClass * ShadowPage; + private: +}; + +#endif diff --git a/HDATA.CPP b/HDATA.CPP new file mode 100644 index 0000000..0057d3a --- /dev/null +++ b/HDATA.CPP @@ -0,0 +1,318 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\hdata.cpv 2.17 16 Oct 1995 16:48:18 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 22, 1994 * + * * + * Last Update : January 23, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * HouseTypeClass::From_Name -- Fetch house pointer from its name. * + * HouseTypeClass::As_Reference -- Fetches a reference to the house specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** These are the colors used to identify the various owners. +*/ +const int COLOR_GOOD = 180; // GOLD +const int COLOR_BRIGHT_GOOD = 176; // GOLD +const int COLOR_BAD = 123; //RED; +const int COLOR_BRIGHT_BAD = 127; //RED; +const int COLOR_NEUTRAL = 205; //WHITE; +const int COLOR_BRIGHT_NEUTRAL = 202; //WHITE; + + +static HouseTypeClass const HouseGood( + HOUSE_GOOD, + "GoodGuy", // NAME: House name. + TXT_GDI, // FULLNAME: Translated house name. + "GDI", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + COLOR_GOOD, // COLOR: Dark Radar map color. + COLOR_BRIGHT_GOOD, // COLOR: Bright Radar map color. + REMAP_YELLOW, // Remap color ID number. + RemapYellow, // Default remap table. + 'G' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseBad( + HOUSE_BAD, + "BadGuy", // NAME: House name. + TXT_NOD, // FULLNAME: Translated house name. + "NOD", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + COLOR_BAD, // COLOR: Dark Radar map color. + COLOR_BRIGHT_BAD, // COLOR: Bright Radar map color. + REMAP_BLUE, // Remap color ID number. + RemapBlue, // Default remap table. + 'B' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseCivilian( + HOUSE_NEUTRAL, + "Neutral", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "CIV", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + COLOR_NEUTRAL, // COLOR: Dark Radar map color. + COLOR_BRIGHT_NEUTRAL, // COLOR: Bright Radar map color. + REMAP_YELLOW, // Remap color ID number. + RemapNone, // Default remap table. + 'C' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseJP( + HOUSE_JP, + "Special", // NAME: House name. + TXT_JP, // FULLNAME: Translated house name. + "JP", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + COLOR_NEUTRAL, // COLOR: Dark Radar map color. + COLOR_BRIGHT_NEUTRAL, // COLOR: Bright Radar map color. + REMAP_YELLOW, // Remap color ID number. + RemapNone, // Default remap table. + 'J' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti1( + HOUSE_MULTI1, + "Multi1", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP1", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + COLOR_NEUTRAL, // COLOR: Radar map color. + COLOR_BRIGHT_NEUTRAL, // COLOR: Bright Radar map color. + REMAP_AQUA, // Remap color ID number. + RemapBlueGreen, // Default remap table. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti2( + HOUSE_MULTI2, + "Multi2", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP2", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + COLOR_NEUTRAL, // COLOR: Radar map color. + COLOR_BRIGHT_NEUTRAL, // COLOR: Bright Radar map color. + REMAP_ORANGE, // Remap color ID number. + RemapOrange, // Default remap table. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti3( + HOUSE_MULTI3, + "Multi3", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP3", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + COLOR_NEUTRAL, // COLOR: Radar map color. + COLOR_BRIGHT_NEUTRAL, // COLOR: Bright Radar map color. + REMAP_GREEN, // Remap color ID number. + RemapGreen, // Default remap table. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti4( + HOUSE_MULTI4, + "Multi4", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP4", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + COLOR_NEUTRAL, // COLOR: Radar map color. + COLOR_BRIGHT_NEUTRAL, // COLOR: Bright Radar map color. + REMAP_BLUE, // Remap color ID number. + RemapBlue, // Default remap table. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti5( + HOUSE_MULTI5, + "Multi5", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP5", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + COLOR_NEUTRAL, // COLOR: Radar map color. + COLOR_BRIGHT_NEUTRAL, // COLOR: Bright Radar map color. + REMAP_YELLOW, // Remap color ID number. + RemapYellow, // Default remap table. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti6( + HOUSE_MULTI6, + "Multi6", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP6", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + COLOR_NEUTRAL, // COLOR: Radar map color. + COLOR_BRIGHT_NEUTRAL, // COLOR: Bright Radar map color. + REMAP_RED, // Remap color ID number. + RemapRed, // Default remap table. + 'M' // VOICE: Voice prefix character. +); + +HouseTypeClass const * const HouseTypeClass::Pointers[HOUSE_COUNT] = { + &HouseGood, + &HouseBad, + &HouseCivilian, + &HouseJP, + &HouseMulti1, + &HouseMulti2, + &HouseMulti3, + &HouseMulti4, + &HouseMulti5, + &HouseMulti6, +}; + + +/*********************************************************************************************** + * HouseTypeClass::HouseTypeClass -- Constructor for house type objects. * + * * + * This is the constructor for house type objects. This object holds the constant data * + * for the house type. * + * * + * INPUT: house -- The ID number for this house type. * + * ini -- The INI name of this house. * + * fullname -- The text number representing the complete name of the house. * + * ext -- The filename extension used when loading data files. * + * lemon -- The percentage for objects of this ownership to be lemon. * + * remapc -- The remap color number to use. * + * color -- The radar color to use for this "house". * + * prefix -- A unique prefix letter used when building custom filenames. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/21/1994 JLB : Created. * + *=============================================================================================*/ +HouseTypeClass::HouseTypeClass(HousesType house, + char const * ini, + int fullname, + char const * ext, + int lemon, + int color, + int bright_color, + PlayerColorType remapcolor, + unsigned char const * remap, + char prefix) +{ + RemapTable = remap; + RemapColor = remapcolor; + House = house; + IniName = ini; + FullName = fullname; + strncpy(Suffix, ext, 3); + Suffix[3] = '\0'; + Lemon = lemon; + Color = color; + BrightColor = bright_color; + Prefix = prefix; +} + + +/*********************************************************************************************** + * HouseTypeClass::From_Name -- Fetch house pointer from its name. * + * * + * This routine will convert the ASCII house name specified into a * + * real house number. Typically, this is used when processing a * + * scenario INI file. * + * * + * INPUT: name -- ASCII name of house to process. * + * * + * OUTPUT: Returns with actual house number represented by the ASCII * + * name specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 05/21/1994 JLB : Converted to member function. * + *=============================================================================================*/ +HousesType HouseTypeClass::From_Name(char const *name) +{ + if (name) { + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + if (stricmp(Pointers[house]->IniName, name) == 0) { + return(house); + } + } + } + return(HOUSE_NONE); +} + + +/*********************************************************************************************** + * HouseTypeClass::One_Time -- One-time initialization * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/21/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void HouseTypeClass::One_Time(void) +{ + /* + ** Change the radar color for special units; otherwise, they'll be the same + ** color as the player! + */ + if (Special.IsJurassic && AreThingiesEnabled) { + ((unsigned char &)HouseJP.Color) = (unsigned char)COLOR_BAD; + ((unsigned char &)HouseJP.BrightColor) = (unsigned char)COLOR_BRIGHT_BAD; + } +} + + +/*********************************************************************************************** + * HouseTypeClass::As_Reference -- Fetches a reference to the house specified. * + * * + * Use this routine to fetch a reference to the house number specified. * + * * + * INPUT: house -- The house number (HousesType) to look up. * + * * + * OUTPUT: Returns with a reference to the HouseTypeClass object that matches the house * + * number specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +HouseTypeClass const & HouseTypeClass::As_Reference(HousesType house) +{ + return(*Pointers[house]); +} diff --git a/HEADER.MAC b/HEADER.MAC new file mode 100644 index 0000000..ea443db --- /dev/null +++ b/HEADER.MAC @@ -0,0 +1,20 @@ +%home%tof + +%home%tof/* $Header:$ */ +/*********************************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *********************************************************************************************** + * * + * Project Name : %qProject Name$%col96* + * * + * File Name : %-r%-e%col96* + * * + * Programmer : %eProgrammer$%col96* + * * + * Start Date : %date%col96* + * * + * Last Update : %date%col96* + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ diff --git a/HEAP.CPP b/HEAP.CPP new file mode 100644 index 0000000..db1555d --- /dev/null +++ b/HEAP.CPP @@ -0,0 +1,553 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\heap.cpv 2.18 16 Oct 1995 16:49:56 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HEAP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/18/95 * + * * + * Last Update : May 22, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FixedIHeapClass::Free -- Frees an object in the heap. * + * FixedHeapClass::FixedHeapClass -- Normal constructor for heap management class. * + * FixedHeapClass::~FixedHeapClass -- Destructor for the heap manager class. * + * FixedHeapClass::Set_Heap -- Assigns a memory block for this heap manager. * + * FixedHeapClass::Allocate -- Allocate a sub-block from the heap. * + * FixedHeapClass::Free -- Frees a sub-block in the heap. * + * FixedHeapClass::ID -- Converts a pointer to a sub-block index number. * + * FixedHeapClass::Clear -- Clears (and frees) the heap manager memory. * + * TFixedIHeapClass::Save -- Saves all active objects * + * TFixedIHeapClass::Load -- Loads all active objects * + * TFixedIHeapClass::Code_Pointers -- codes pointers for every object, to prepare for save * + * TFixedIHeapClass::Decode_Pointers -- Decodes all object pointers, for after loading * + * FixedHeapClass::Free_All -- Frees all objects in the fixed heap. * + * FixedIHeapClass::Free_All -- Frees all objects out of the indexed heap. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include "heap.h" +#include +#include +#include +#include +#include + + +/*********************************************************************************************** + * FixedHeapClass::FixedHeapClass -- Normal constructor for heap management class. * + * * + * This is the normal constructor used for the heap manager class. This initializes * + * the class but doesn't yet assign actual heap memory to this manager. That is handled * + * by the Set_Heap() function. * + * * + * INPUT: size -- The size of the individual sub-blocks in this heap. This value is * + * typically the size of some class or structure. * + * * + * OUTPUT: none * + * * + * WARNINGS: The heap must first be assigned a block of memory to manage before it can * + * be used. * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +FixedHeapClass::FixedHeapClass(int size) +{ + Size = size; + Buffer = 0; + IsAllocated = false; + TotalCount = 0; + ActiveCount = 0; +} + + +/*********************************************************************************************** + * FixedHeapClass::~FixedHeapClass -- Destructor for the heap manager class. * + * * + * This is the default constructor for the heap manager class. It handles freeing the * + * memory assigned to this heap. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +FixedHeapClass::~FixedHeapClass(void) +{ + FixedHeapClass::Clear(); +} + + +/*********************************************************************************************** + * FixedHeapClass::Set_Heap -- Assigns a memory block for this heap manager. * + * * + * This routine is used to assign a memory heap to this object. A memory heap so assigned * + * will start with all sub-blocks unallocated. After this routine is called, normal * + * allocation and freeing may occur. This routine will allocate necessary memory if the * + * buffer parameter is NULL. * + * * + * INPUT: count -- The number of objects that this heap should manage. * + * * + * buffer -- Pointer to pre-allocated buffer that this manager will use. If this * + * parameter is NULL, then memory will be automatically allocated. * + * * + * OUTPUT: bool; Was the heap successfully initialized? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +int FixedHeapClass::Set_Heap(int count, void * buffer) +{ + /* + ** Clear out the old heap data. + */ + Clear(); + + /* + ** If there is no size to the objects in the heap, then this block memory + ** handler can NEVER function. Return with a failure condition. + */ + if (!Size) return(false); + + /* + ** If there is no count specified, then this indicates that the heap should + ** be disabled. + */ + if (!count) return(true); + + /* + ** Initialize the free boolean vector and the buffer for the actual + ** allocation objects. + */ + if (FreeFlag.Resize(count)) { + if (!buffer) { + buffer = new char[count * Size]; + if (!buffer) { + FreeFlag.Clear(); + return(false); + } + IsAllocated = true; + } + Buffer = buffer; + TotalCount = count; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FixedHeapClass::Allocate -- Allocate a sub-block from the heap. * + * * + * Finds the first available sub-block in the heap and returns a pointer to it. The sub- * + * block is marked as allocated by this routine. If there are no more sub-blocks * + * available, then this routine will return NULL. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the newly allocated sub-block. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +void * FixedHeapClass::Allocate(void) +{ + if (ActiveCount < TotalCount) { + int index = FreeFlag.First_False(); + + if (index != -1) { + ActiveCount++; + FreeFlag[index] = true; + return((*this)[index]); + } + } + return(0); +} + + +/*********************************************************************************************** + * FixedHeapClass::Free -- Frees a sub-block in the heap. * + * * + * Use this routine to free a previously allocated sub-block in the heap. * + * * + * INPUT: pointer -- A pointer to the sub-block to free. This is the same pointer that * + * was returned from the Allocate() function. * + * * + * OUTPUT: bool; Was the deallocation successful? Failure could indicate a pointer that * + * doesn't refer to this heap or a null pointer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +int FixedHeapClass::Free(void * pointer) +{ + if (pointer && ActiveCount) { + int index = ID(pointer); + + if ((unsigned)index < TotalCount) { + if (FreeFlag[index]) { + ActiveCount--; + FreeFlag[index] = false; + return(true); + } + } + } + return(false); +} + + +/*********************************************************************************************** + * FixedHeapClass::ID -- Converts a pointer to a sub-block index number. * + * * + * Use this routine to convert a pointer (returned by Allocate) into the sub-block * + * index number. This index number can be used as a form of identifier for the block. * + * * + * INPUT: pointer -- A pointer to the sub-block to conver into an ID number. * + * * + * OUTPUT: Returns with the index (ID) number for the sub-block specified. This number will * + * range between 0 and the sub-block max -1. If -1 is returned, then the pointer * + * was invalid. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +int FixedHeapClass::ID(void const * pointer) +{ + if (pointer && Size) { + return((int)(((char *)pointer - (char *)Buffer) / Size)); + } + return(-1); +} + + +/*********************************************************************************************** + * FixedHeapClass::Clear -- Clears (and frees) the heap manager memory. * + * * + * This routine is used to bring the heap manager back into a non-functioning state. All * + * memory allocated by this manager is freeed. Any previous pointers to allocated blocks * + * from this heap are now invalid. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +void FixedHeapClass::Clear(void) +{ + /* + ** Free the old buffer (if present). + */ + if (Buffer && IsAllocated) { + delete[] Buffer; + } + Buffer = 0; + IsAllocated = false; + ActiveCount = 0; + TotalCount = 0; + FreeFlag.Clear(); +} + + +/*********************************************************************************************** + * FixedHeapClass::Free_All -- Frees all objects in the fixed heap. * + * * + * This routine will free all previously allocated objects out of the heap. Use this * + * routine to ensure that the heap is empty. * + * * + * INPUT: none * + * * + * OUTPUT: Was the heap successfully cleared of all objects? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +int FixedHeapClass::Free_All(void) +{ + ActiveCount = 0; + FreeFlag.Reset(); + return(true); +} + + +///////////////////////////////////////////////////////////////////// + + +/*********************************************************************************************** + * FixedIHeapClass::Free_All -- Frees all objects out of the indexed heap. * + * * + * Use this routine to free all previously allocated objects in the heap. This routine will * + * also clear out the allocated object vector as well. * + * * + * INPUT: none * + * * + * OUTPUT: Was the heap successfully cleared of objects? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +int FixedIHeapClass::Free_All(void) +{ + ActivePointers.Delete_All(); + return(FixedHeapClass::Free_All()); +} + + +void FixedIHeapClass::Clear(void) +{ + FixedHeapClass::Clear(); + ActivePointers.Clear(); +} + + +int FixedIHeapClass::Set_Heap(int count, void * buffer) +{ + Clear(); + if (FixedHeapClass::Set_Heap(count, buffer)) { + ActivePointers.Resize(count); + return(true); + } + return(false); +} + + +void * FixedIHeapClass::Allocate(void) +{ + void * ptr = FixedHeapClass::Allocate(); + if (ptr) { + ActivePointers.Add(ptr); + memset (ptr, 0, Size); + } + return(ptr); +} + + +/*********************************************************************************************** + * FixedIHeapClass::Free -- Frees an object in the heap. * + * * + * This routine is used to free an object in the heap. Freeing is accomplished by marking * + * the object's memory as free to be reallocated. The object is also removed from the * + * allocated object pointer vector. * + * * + * INPUT: pointer -- Pointer to the object that is to be removed from the heap. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +int FixedIHeapClass::Free(void * pointer) +{ + if (FixedHeapClass::Free(pointer)) { + ActivePointers.Delete(pointer); + } + return(false); +} + + +/*********************************************************************************************** + * TFixedIHeapClass::Save -- Saves all active objects * + * * + * INPUT: file file to write to * + * * + * OUTPUT: true = OK, false = error * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BRR : Created. * + *=============================================================================================*/ +template +int TFixedIHeapClass::Save(FileClass &file) +{ + int i; // loop counter + int idx; // object index + + /* + ** Save the number of instances of this class + */ + if (file.Write(&ActiveCount, sizeof(ActiveCount)) != sizeof(ActiveCount)) { + return(false); + } + + /* + ** Save each instance of this class + */ + for (i = 0; i < ActiveCount; i++) { + /* + ** Save the array index of the object, so it can be loaded back into the + ** same array location (so TARGET translations will work) + */ + idx = ID(Ptr(i)); + if (file.Write(&idx, sizeof(idx)) != sizeof(idx)) { + return(false); + } + + /* + ** Save the object itself + */ + if (!Ptr(i)->Save(file)) { + return(false); + } + } + + return(true); +} + + +/*********************************************************************************************** + * TFixedIHeapClass::Load -- Loads all active objects * + * * + * INPUT: file file to read from * + * * + * OUTPUT: true = OK, false = error * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BRR : Created. * + *=============================================================================================*/ +template +int TFixedIHeapClass::Load(FileClass &file) +{ + int i; // loop counter + int idx; // object index + T *ptr; // object pointer + int a_count; + + /* + ** Read the number of instances of this class + */ + if (file.Read(&a_count, sizeof(a_count)) != sizeof(a_count)) { + return(false); + } + + /* + ** Error if more objects than we can hold + */ + if (a_count > TotalCount) { + return(false); + } + + /* + ** Read each class instance + */ + for (i = 0; i < a_count; i++) { + /* + ** Read the object's array index + */ + if (file.Read (&idx, sizeof(idx)) != sizeof(idx)) { + return(false); + } + + /* + ** Get a pointer to the object, activate that object + */ + ptr = (T *)(*this)[idx]; + FreeFlag[idx] = true; + ActiveCount++; + ActivePointers.Add(ptr); + + /* + ** Load the object + */ + if (!ptr->Load(file)) { + return(false); + } + } + + return(true); +} + + +/*********************************************************************************************** + * TFixedIHeapClass::Code_Pointers -- codes pointers for every object, to prepare for save * + * * + * INPUT: file file to read from * + * * + * OUTPUT: true = OK, false = error * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BRR : Created. * + *=============================================================================================*/ +template +void TFixedIHeapClass::Code_Pointers(void) +{ + int i; + + for (i = 0; i < ActiveCount; i++) { + Ptr(i)->Code_Pointers(); + } +} + + +/*********************************************************************************************** + * TFixedIHeapClass::Decode_Pointers -- Decodes all object pointers, for after loading * + * * + * INPUT: file file to read from * + * * + * OUTPUT: true = OK, false = error * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BRR : Created. * + *=============================================================================================*/ +template +void TFixedIHeapClass::Decode_Pointers(void) +{ + int i; + + for (i = 0; i < ActiveCount; i++) { + Ptr(i)->Decode_Pointers(); + } +} diff --git a/HEAP.H b/HEAP.H new file mode 100644 index 0000000..19ef6a0 --- /dev/null +++ b/HEAP.H @@ -0,0 +1,191 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\heap.h_v 2.15 16 Oct 1995 16:47:08 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HEAP.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/18/95 * + * * + * Last Update : February 18, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef HEAP_H +#define HEAP_H + +#include "vector.h" + +/************************************************************************** +** This is a block memory managment handler. It is used when memory is to +** be treated as a series of blocks of fixed size. This is similar to an +** array of integral types, but unlike such an array, the memory blocks +** are annonymous. This facilitates the use of this class when overloading +** the new and delete operators for a normal class object. +*/ +class FixedHeapClass +{ + public: + FixedHeapClass(int size); + virtual ~FixedHeapClass(void); + + int ID(void const * pointer); + int Count(void) {return ActiveCount;}; + int Length(void) {return TotalCount;}; + int Avail(void) {return TotalCount-ActiveCount;}; + + virtual int Set_Heap(int count, void * buffer=0); + virtual void * Allocate(void); + virtual void Clear(void); + virtual int Free(void * pointer); + virtual int Free_All(void); + + protected: + void * operator[](int index) {return ((char *)Buffer) + (index * Size);}; + + /* + ** If the memory block buffer was allocated by this class, then this flag + ** will be true. The block must be deallocated by this class if true. + */ + unsigned IsAllocated:1; + + /* + ** This is the size of each sub-block within the buffer. + */ + int Size; + + /* + ** This records the absolute number of sub-blocks in the buffer. + */ + int TotalCount; + + /* + ** This is the total blocks allocated out of the heap. This number + ** will never exceed Count. + */ + int ActiveCount; + + /* + ** Pointer to the heap's memory buffer. + */ + void * Buffer; + + /* + ** This is a boolean vector array of allocation flag bits. + */ + BooleanVectorClass FreeFlag; + + private: + // The assignment operator is not supported. + FixedHeapClass & operator = (FixedHeapClass const &); + + // The copy constructor is not supported. + FixedHeapClass(FixedHeapClass const &); +}; + + +/************************************************************************** +** This template serves only as an interface to the heap manager class. By +** using this template, the object pointers are automatically converted +** to the correct type without any code overhead. +*/ +template +class TFixedHeapClass : public FixedHeapClass +{ + public: + TFixedHeapClass(void) : FixedHeapClass(sizeof(T)) {}; + virtual ~TFixedHeapClass(void) {}; + + int ID(T const * pointer) {return FixedHeapClass::ID(pointer);}; + + virtual T * Alloc(void) {return (T*)FixedHeapClass::Allocate();}; + virtual int Free(T * pointer) {FixedHeapClass::Free(pointer);}; + + protected: + T & operator[](int index) {return *(((char *)Buffer) + (index * Size));}; +}; + + +/************************************************************************** +** This is a derivative of the fixed heap class. This class adds the +** ability to quickly iterate through the active (allocated) objects. Since the +** active array is a sequence of pointers, the overhead of this class +** is 4 bytes per potential allocated object (be warned). +*/ +class FixedIHeapClass : public FixedHeapClass +{ + public: + FixedIHeapClass(int size) : FixedHeapClass(size) {}; + virtual ~FixedIHeapClass(void) {}; + + virtual int Set_Heap(int count, void * buffer=0); + virtual void * Allocate(void); + virtual void Clear(void); + virtual int Free(void * pointer); + virtual int Free_All(void); + + virtual void * Active_Ptr(int index) {return ActivePointers[index];}; + + /* + ** This is an array of pointers to allocated objects. Using this array + ** to control iteration through the objects ensures a minimum of processing. + ** It also allows access to this array so that custom sorting can be + ** performed. + */ + DynamicVectorClass ActivePointers; +}; + + +/************************************************************************** +** This template serves only as an interface to the iteratable heap manager +** class. By using this template, the object pointers are automatically converted +** to the correct type without any code overhead. +*/ +template +class TFixedIHeapClass : public FixedIHeapClass +{ + public: + TFixedIHeapClass(void) : FixedIHeapClass(sizeof(T)) {}; + virtual ~TFixedIHeapClass(void) {}; + + int ID(T const * pointer) {return FixedIHeapClass::ID(pointer);}; + virtual T * Alloc(void) {return (T*)FixedIHeapClass::Allocate();}; + virtual int Free(T * pointer) {return FixedIHeapClass::Free(pointer);}; + virtual int Free(void * pointer) {return FixedIHeapClass::Free(pointer);}; + virtual int Save(FileClass & ); + virtual int Load(FileClass & ); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + virtual T * Ptr(int index) {return (T*)FixedIHeapClass::ActivePointers[index];}; + virtual T * Raw_Ptr(int index) {return (T*)((*this)[index]);}; +}; + + +#endif + + diff --git a/HELP.CPP b/HELP.CPP new file mode 100644 index 0000000..b724ef5 --- /dev/null +++ b/HELP.CPP @@ -0,0 +1,405 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\help.cpv 2.18 16 Oct 1995 16:51:02 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HELP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/18/94 * + * * + * Last Update : July 16, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * HelpClass::Draw_Help -- Display the help message (if necessary). * + * HelpClass::HelpClass -- Default constructor for the help processor. * + * HelpClass::Help_AI -- Handles the help text logic. * + * HelpClass::Help_Text -- Assigns text as the current help text. * + * HelpClass::Init_Clear -- Sets help system to a known state. * + * HelpClass::Overlap_List -- Returns with offset list for cells under help text. * + * HelpClass::Scroll_Map -- Makes sure scrolling doesn't leave text shards. * + * HelpClass::Set_Cost -- Initiates the second line of help text showing item cost. * + * HelpClass::Set_Tactical_Position -- Sets the tactial map position. * + * HelpClass::Set_Tactical_Position -- Sets the tactical map position. * + * HelpClass::Set_Text -- Determines the overlap list and draw coordinates. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** This is the holding buffer for the text overlap list. This buffer must be in the near +** data segment. It will be filled in by the Set_Text() function. +*/ +short const HelpClass::OverlapList[30] = { + REFRESH_EOL +}; + +char const * HelpClass::HelpText; + + +CountDownTimerClass HelpClass::CountDownTimer; + + +/*********************************************************************************************** + * HelpClass::HelpClass -- Default constructor for the help processor. * + * * + * The help processor is initialized by this routine. It merely sets up the help engine * + * to the default state. The default state will not display any help text. Call the * + * Help_Text() function to enable help processing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + *=============================================================================================*/ +HelpClass::HelpClass(void) +{ + X = 0; + Y = 0; + Width = 0; + Text = TXT_NONE; + Color = LTGREY; + CountDownTimer.Set(0); + IsRight = false; + Cost = 0; +} + + +/*********************************************************************************************** + * HelpClass::Init_Clear -- Sets help system to a known state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void HelpClass::Init_Clear(void) +{ + TabClass::Init_Clear(); + + Set_Text(TXT_NONE); +} + + +/*********************************************************************************************** + * HelpClass::Overlap_List -- Returns with offset list for cells under help text. * + * * + * Use this routine to fetch an offset list for the cells under the text displayed. If * + * there is no text displayed, then the list will consist of just the terminator code. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the offset list for the help text overlap. The offset * + * list is based on the tactical map upper left corner cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + *=============================================================================================*/ +short const * HelpClass::Overlap_List(void) const +{ + if (Text == TXT_NONE || CountDownTimer.Time()) { + ((short &)(OverlapList[0])) = REFRESH_EOL; + } + return(OverlapList); +} + + +/*********************************************************************************************** + * HelpClass::Help_AI -- Handles the help text logic. * + * * + * This routine handles tracking the mouse position to see if the mouse remains stationary * + * for the required amount of time. If the time requirement has been met, then it flags * + * the help system to display the help text the next time the Draw_Help() function is * + * called. * + * * + * INPUT: key -- Keyboard input code. * + * * + * x,y -- Mouse coordinates. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine must be called once and only once per game frame (15 times per * + * second). * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinates as passed in. * + *=============================================================================================*/ +void HelpClass::AI(KeyNumType &key, int x, int y) +{ + /* + ** If there is any keyboard input, then the help text goes away. + */ +// if (key) { +// Help_Text(TXT_NONE); +// } + + if (!CountDownTimer.Time() && !IsRight && (x != X || y != Y)) { + Help_Text(TXT_NONE); + } + + /* + ** Process the countdown timer only if it hasn't already expired and there is + ** a real help text message to display. + */ + if (CountDownTimer.Time() && !HelpText && Text != TXT_NONE) { + + /* + ** If the mouse has moved, then reset the timer since a moving mouse is not + ** supposed to bring up the help text. + */ + if (!IsRight && (X != x || Y != y)) { + X = x; + Y = y; + CountDownTimer.Start(); + CountDownTimer.Set(HELP_DELAY); + Set_Text(TXT_NONE); + } else { + + /* + ** If the delay has expired, then the text must be drawn. Build the help text + ** overlay list at this time. Better to do it now, when we KNOW it is needed, then + ** to do it earlier when it might not be needed. + */ + Set_Text(Text); + } + } + + TabClass::AI(key, x, y); +} + + +/*********************************************************************************************** + * HelpClass::Help_Text -- Assigns text as the current help text. * + * * + * Use this routine to change the help text that will pop up if the cursor isn't moved * + * for the help delay duration. Call this routine as often as desired. * + * * + * INPUT: text -- The text number for the help text to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + *=============================================================================================*/ +void HelpClass::Help_Text(int text, int x, int y, int color, bool quick, int cost) +{ + if (text != Text) { + + /* + ** If there is an existing text message, then flag the map to redraw the underlying + ** icons so that the text message is erased. + */ + if (Text != TXT_NONE) { + Refresh_Cells(Coord_Cell(TacticalCoord), &OverlapList[0]); + } + + /* + ** Record the position of the mouse. This recorded position will be used to determine + ** if the mouse has moved. A moving mouse prevents the help text from popping up. + */ + X = x; + if (x == -1) X = Get_Mouse_X(); + Y = y; + if (y == -1) Y = Get_Mouse_Y(); + IsRight = (y != -1) || (x != -1); + + if (quick) { + CountDownTimer.Set(1); + } else { + CountDownTimer.Set(HELP_DELAY); + } + + Color = color; + Text = text; + Cost = cost; + } +} + + +/*********************************************************************************************** + * HelpClass::Draw_Help -- Display the help message (if necessary). * + * * + * This function will print the help text if it thinks it should. The timer and text * + * message can control whether this occurs. If there is no help text or the countdown timer * + * has not expired, then no text will be printed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + *=============================================================================================*/ +void HelpClass::Draw_It(bool forced) +{ + TabClass::Draw_It(forced); + + if (Text != TXT_NONE && (forced || !CountDownTimer.Time())) { + + if (LogicPage->Lock()){ + + // Fancy_Text_Print(Text, DrawX, DrawY, Color, BLACK, TPF_6POINT|TPF_NOSHADOW); + Fancy_Text_Print(Text, DrawX, DrawY, Color, BLACK, TPF_MAP|TPF_NOSHADOW); + LogicPage->Draw_Rect(DrawX-1, DrawY-1, DrawX+Width+1, DrawY+FontHeight, Color); + + if (Cost) { + char buffer[15]; + sprintf(buffer, "$%d", Cost); + int width = String_Pixel_Width(buffer); + + // Fancy_Text_Print(buffer, DrawX, DrawY+FontHeight, Color, BLACK, TPF_6POINT|TPF_NOSHADOW); + Fancy_Text_Print(buffer, DrawX, DrawY+FontHeight, Color, BLACK, TPF_MAP|TPF_NOSHADOW); + LogicPage->Draw_Rect(DrawX-1, DrawY+FontHeight, DrawX+width+1, DrawY+FontHeight+FontHeight-1, Color); + LogicPage->Draw_Line(DrawX, DrawY+FontHeight, DrawX+MIN(width+1, Width) - 1, DrawY+FontHeight, BLACK); + } + + LogicPage->Unlock(); + } + } + // if (!In_Debugger) HidPage.Unlock(); +} + + +/*********************************************************************************************** + * HelpClass::Set_Text -- Determines the overlap list and draw coordinates. * + * * + * This routine is used to build the overlap list -- used for icon refreshing. It also * + * determines if the text can fit on the screen and makes adjustments so that it will. * + * * + * INPUT: text -- The text number to set the help system to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + * 12/11/1994 JLB : Won't draw past tactical map edges. * + *=============================================================================================*/ +void HelpClass::Set_Text(int text) +{ + if (text != TXT_NONE) { + Text = text; +// Fancy_Text_Print(TXT_NONE, 0, 0, 0, 0, TPF_6POINT|TPF_NOSHADOW); + Fancy_Text_Print(TXT_NONE, 0, 0, 0, 0, TPF_MAP|TPF_NOSHADOW); + Width = String_Pixel_Width(Text_String(Text)); + if (IsRight) { + DrawX = X - Width; + DrawY = Y; + } else { + int right = TacPixelX + Lepton_To_Pixel(TacLeptonWidth) - 3; + int bottom = TacPixelY + Lepton_To_Pixel(TacLeptonHeight) - 1; + + DrawX = X+X_OFFSET; + DrawY = Y+Y_OFFSET; + if (DrawX + Width > right) { + DrawX -= (DrawX+Width) - right; + } + if (DrawY + FontHeight > bottom) { + DrawY -= (DrawY+FontHeight) - bottom; + } + if (DrawX < TacPixelX+1) DrawX = TacPixelX+1; + if (DrawY < TacPixelY+1) DrawY = TacPixelY+1; + } + int lines = (Cost) ? 2 : 1; + memcpy((void*)OverlapList, Text_Overlap_List(Text_String(Text), DrawX-1, DrawY, lines), sizeof(OverlapList)); + } +} + + +/*********************************************************************************************** + * HelpClass::Scroll_Map -- Makes sure scrolling doesn't leave text shards. * + * * + * This routine intercepts the map scrolling request and then makes sure that if, in fact, * + * the map is going to scroll, then reset and erase the help text so that it doesn't * + * mess up the display. * + * * + * INPUT: facing -- The direction to scroll (unused by this routine). * + * * + * really -- If the scroll is actually going to occur, rather than just be examined * + * for legality, then this parameter will be true. If this parameter is * + * true, then the help text is reset. * + * * + * OUTPUT: Returns if it can, or did, scroll in the requested direction. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +bool HelpClass::Scroll_Map(DirType facing, int & distance, bool really) +{ + if (really) { + Help_Text(TXT_NONE); + } + return(TabClass::Scroll_Map(facing, distance, really)); +} + + +/*********************************************************************************************** + * HelpClass::Set_Cost -- Initiates the second line of help text showing item cost. * + * * + * Use this routine after the Help_Text() function to activate the second line. The second * + * line displays a cost. Typically, this is used by the sidebar to display the cost of the * + * specified item. * + * * + * INPUT: cost -- The cost to associate with this help text. If this value is zero, then * + * no second line is displayed, so don't pass in zero. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/09/1995 JLB : Created. * + *=============================================================================================*/ +void HelpClass::Set_Cost(int cost) +{ + Cost = cost; +} + + +void HelpClass::Set_Tactical_Position(COORDINATE coord) +{ + if (TacticalCoord != coord) { + Help_Text(TXT_NONE); + } + TabClass::Set_Tactical_Position(coord); +} diff --git a/HELP.H b/HELP.H new file mode 100644 index 0000000..020b5a9 --- /dev/null +++ b/HELP.H @@ -0,0 +1,144 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\help.h_v 2.17 16 Oct 1995 16:46:28 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HELP.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/18/94 * + * * + * Last Update : November 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef HELP_H +#define HELP_H + +#include "tab.h" + +class HelpClass: public TabClass +{ + public: + HelpClass(void); + + /* + ** Initialization + */ + virtual void Init_Clear(void); // Clears all to known state + + virtual void Draw_It(bool complete=false); + virtual void AI(KeyNumType &input, int x, int y); + virtual bool Scroll_Map(DirType facing, int &distance, bool really); + virtual void Set_Tactical_Position(COORDINATE coord); + + void Help_Text(int text, int x=-1, int y=-1, int color=LTGREY, bool quick=false, int cost = 0); + void Set_Cost(int cost); + short const * Overlap_List(void) const; + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + private: + + static char const *HelpText; + int HelpX; + int HelpY; + int HelpWidth; + + + void Set_Text(int text); + + /* + ** If the help text is right justified (as with the help text that pops up over the + ** sidebar icons), then this flag is set to true. + */ + unsigned IsRight:1; + + /* + ** If the optional second line of text that displays cost is desired, then this + ** value will be non-zero. Typically, this is true when the help text is associated + ** with one of the sidebar construction icons. + */ + int Cost; + + /* + ** This is the recorded position of the cursor at the time the help text + ** pops up. The help text is rendered as an offset from this pixel position. + */ + int X; + int Y; + + /* + ** This is the draw X and Y coordinate. This position is relative to the X and + ** Y coordinates but adjusted for screen edges as necessary. + */ + int DrawX; + int DrawY; + + /* + ** The width of the help text (in pixels) is stored here. This is a convenience + ** since calculating the width takes a bit of time. + */ + int Width; + + /* + ** The text number of the help text to display is held here. If no text is to be + ** displayed, then this value will be TXT_NONE. + */ + int Text; + + /* + ** This is the background color to use for the help text. It can change according + ** to the message displayed. + */ + int Color; + + /* + ** This countdown timer controls when the help text will pop up. If the mouse + ** remains stationary while this countdown timer expires, then the help text + ** will pop up. + */ + static CountDownTimerClass CountDownTimer; + + /* + ** This is a calculated cell offset list (from the Map.TacticalCell) that indicates + ** which cells are under the help text and thus which cells need to be redrawn if + ** the help text is to be erased. + */ + static short const OverlapList[30]; + + enum HelpClassEnum { + HELP_DELAY=TIMER_SECOND*1, // The countdown timer delay before help text pops up. + Y_OFFSET=0, // The Y pixel offset from cursor for help text print. + X_OFFSET=10, // The X pixel offset from cursor for help text print. + }; +}; + +#endif diff --git a/HOTLIST.TXT b/HOTLIST.TXT new file mode 100644 index 0000000..b15730e --- /dev/null +++ b/HOTLIST.TXT @@ -0,0 +1,114 @@ +Burn version with new setup/install and scenario files. + +) I've noticed that the Tiberium "tiles" are skragging in the +multiplayer mode. Skragging= a "tile" of random colored pixels. +It happened everytime I would scroll to the bottom and there was +still tiberium on the field, but also, just as I would scroll +around. + +) Vehicles still cross over each other when they are moving. + +) I think you should be able to click a tile partially full of +infantry if the selected unit is an infantry. (e.g. if you have 3 +infantry in a square, you should be able to select 2 infantry and +be able to click in the square where the 3 are.) If you select +more than 2 infantry, the remainder should just move as close as +possible... + +) When multiple units are selected and sent to a location, 1 or 2 +never go on the first try. I suggest that those who can't find +there destination try to get as close as possible to it. + +) The game currently pauses anytime any player gets a new score. +This turns into repeated "lockup scares" when the player is playing three other people. +If we can't solve this problem, perhaps we should give each player +a sound track and never load a new one. + +) The game bogs down when multiple people are scrolling around. + +) Right now, there is no message when a player has been wiped off the +map. Should this be an EVA thing or on screen text? I think both. + +) There is also no "You have won!" message. This should be EVA and +text as well. + +"RUNGAME.EXE" to be renamed to so that it is clear what the player should +type to run the game. Possibly rename to "utility.exe". + +Install new multiplayer EVA speech. Disable enemy destroyed EVA speech +when in multiplayer mode. + +Install new VQ movies. + +Merge with Barry to incorporate map/score screen changes. + +Install these samples: +REPAIR2.WAV use when vehicle goes on repair ppad. +RADAR2.WAV use when radar kicks in. + +Track down Watcom debug problem. Call Tech Rep + +There is a bug with radar in that it doesn't update enough +for fast moving units. + +Fix so that harvester returns to Tiberium field after unloading. + +Add pause option to game (for network play)? + +Prevent placement of buildings off the edge of the map. Especially +critical for the Weapons factory. + +Purple tiberium color on radar map. + +Fix bug with why bases aren't being built by the computer. + +Increase delay for "end of game check" from 20 seconds to 30 seconds. + +Teams arriving by Chinook are not following waypoints. +They will go into Guard upon exiting the helicopter. Fix this. + +Find a solution for: +Chinooks only fly to the Reinforcement cell. +This presents the problem of not being able to bring in troops for the +player, or not having GDI drop troops by helicopter. + +Allow creation of Civilian Teams. + +Check up on the problems with the "ambush" team order. What is it really +supposed to do? + +Fix rocks passibility problems in the desert set. + +Attack cycle should carry a weak machine gun weapon. + +Enemy tanks should drive over infantry if they are very close rather than +merely attacking them with firepower. + +Fix problem with harvester driving over the backside of the refinery -- it looks bad. + +Install two or four small transition scores to reside on the hard drive. + +Control the transition score from the INI file. + +Allow control of the sidebar by way of a trigger event. This will allow certain +scenarios to force the sidebar to be disabled until an event occurs. + +Factories should produce what they originally would produced when constructed, NOT +restricted to what the current owner can produce. This will allow capturing of +a factory so that, otherwise unavailable, units could be produced. + +Show percentage full graphic for the harvester. + +Stealth tank cloaking and decloaking problems. + +Graphic icon glitches on the radar map. + + + + + + + + + + diff --git a/HOUSE.CPP b/HOUSE.CPP new file mode 100644 index 0000000..20583fc --- /dev/null +++ b/HOUSE.CPP @@ -0,0 +1,4349 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\house.cpv 2.13 02 Aug 1995 17:03:50 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HOUSE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 21, 1994 * + * * + * Last Update : August 12, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * HouseClass::AI -- Process house logic. * + * HouseClass::Abandon_Production -- Abandons production of item type specified. * + * HouseClass::Add_Nuke_Piece -- Add a nuclear piece to the collection. * + * HouseClass::Adjust_Capacity -- Adjusts the house Tiberium storage capacity. * + * HouseClass::Adjust_Threat -- Adjust threat for the region specified. * + * HouseClass::As_Pointer -- Converts a house number into a house object pointer. * + * HouseClass::Attacked -- Lets player know if base is under attack. * + * HouseClass::Available_Money -- Fetches the total credit worth of the house. * + * HouseClass::Begin_Production -- Starts production of the specified object type. * + * HouseClass::Blowup_All -- blows up everything * + * HouseClass::Can_Build -- Determines if the aircraft type can be built. * + * HouseClass::Can_Build -- Determines if the building type can be built. * + * HouseClass::Can_Build -- Determines if the infantry unit can be built by this house. * + * HouseClass::Can_Build -- Determines if the unit can be built by this house. * + * HouseClass::Can_Build -- General purpose build legality checker. * + * HouseClass::Clobber_All -- removes house & all its objects * + * HouseClass::Debug_Dump -- Dumps the house status data to the mono screen. * + * HouseClass::Detach -- Removes specified object from house tracking systems. * + * HouseClass::Does_Enemy_Building_Exist -- Checks for enemy building of specified type. * + * HouseClass::Flag_Attach -- Attach flag to specified cell (or thereabouts). * + * HouseClass::Flag_Attach -- Attaches the house flag the specified unit. * + * HouseClass::Flag_Remove -- Removes the flag from the specified target. * + * HouseClass::Flag_To_Die -- Flags the house to blow up soon. * + * HouseClass::Flag_To_Lose -- Flags the house to die soon. * + * HouseClass::Flag_To_Win -- Flags the house to win soon. * + * HouseClass::Harvested -- Adds Tiberium to the harvest storage. * + * HouseClass::Has_Nuke_Device -- Deteremines if the house has a nuclear device. * + * HouseClass::HouseClass -- Constructor for a house object. * + * HouseClass::Init -- init's in preparation for new scenario * + * HouseClass::Init_Air_Strike -- Add (or reset) the air strike sidebar button. * + * HouseClass::Init_Data -- Initializes the multiplayer color data. * + * HouseClass::Init_Ion_Cannon -- Initialize the ion cannon countdown. * + * HouseClass::Init_Nuke_Bomb -- Adds (if necessary) the atom bomb to the sidebar. * + * HouseClass::Is_Ally -- Checks to see if the object is an ally. * + * HouseClass::Is_Ally -- Determines if the specified house is an ally. * + * HouseClass::Is_Ally -- Determines if the specified house is an ally. * + * HouseClass::MPlayer_Defeated -- multiplayer; house is defeated * + * HouseClass::Make_Air_Strike_Available -- Make the airstrike available. * + * HouseClass::Make_Ally -- Make the specified house an ally. * + * HouseClass::Make_Enemy -- Make an enemy of the house specified. * + * HouseClass::Manual_Place -- Inform display system of building placement mode. * + * HouseClass::One_Time -- Handles one time initialization of the house array. * + * HouseClass::Place_Object -- Places the object (building) at location specified. * + * HouseClass::Place_Special_Blast -- Place a special blast effect at location specified. * + * HouseClass::Power_Fraction -- Fetches the current power output rating. * + * HouseClass::Read_INI -- Reads house specific data from INI. * + * HouseClass::Refund_Money -- Refunds money to back to the house. * + * HouseClass::Remap_Table -- Fetches the remap table for this house object. * + * HouseClass::Remove_Air_Strike -- Removes the air strike button from the sidebar. * + * HouseClass::Remove_Ion_Cannon -- Disables the ion cannon. * + * HouseClass::Remove_Nuke_Bomb -- Removes the nuclear bomb from the sidebar. * + * HouseClass::Sell_Wall -- Tries to sell the wall at the specified location. * + * HouseClass::Silo_Redraw_Check -- Flags silos to be redrawn if necessary. * + * HouseClass::Special_Weapon_AI -- Fires special weapon. * + * HouseClass::Spend_Money -- Removes money from the house. * + * HouseClass::Suggest_New_Object -- Determine what would the next buildable object be. * + * HouseClass::Suggested_New_Team -- Determine what team should be created. * + * HouseClass::Suspend_Production -- Temporarily puts production on hold. * + * HouseClass::Validate -- validates house pointer * + * HouseClass::Write_INI -- Writes house specific data into INI file. * + * HouseClass::delete -- Deallocator function for a house object. * + * HouseClass::new -- Allocator for a house class. * + * HouseClass::operator HousesType -- Conversion to HousesType operator. * + * HouseClass::~HouseClass -- Default destructor for a house object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * HouseClass::Validate -- validates house pointer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int HouseClass::Validate(void) const +{ + int num; + + num = Houses.ID(this); + if (num < 0 || num >= HOUSE_MAX) { + Validate_Error("HOUSE"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * HouseClass::operator HousesType -- Conversion to HousesType operator. * + * * + * This operator will automatically convert from a houses class object into the HousesType * + * enumerated value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the object's HousesType value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +HouseClass::operator HousesType(void) const +{ + Validate(); + return(Class->House); +} + + +/*********************************************************************************************** + * HouseClass::As_Pointer -- Converts a house number into a house object pointer. * + * * + * Use this routine to convert a house number into the house pointer that it represents. * + * A simple index into the Houses template array is not sufficient, since the array order * + * is arbitrary. An actual scan through the house object is required in order to find the * + * house object desired. * + * * + * INPUT: house -- The house type number to look up. * + * * + * OUTPUT: Returns with a pointer to the house object that the house number represents. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +HouseClass * HouseClass::As_Pointer(HousesType house) +{ + for (int index = 0; index < Houses.Count(); index++) { + if (Houses.Ptr(index)->Class->House == house) { + return(Houses.Ptr(index)); + } + } + return(0); +} + + +/*********************************************************************************************** + * HouseClass::One_Time -- Handles one time initialization of the house array. * + * * + * This basically calls the constructor for each of the houses in the game. All other * + * data specific to the house is initialized when the scenario is loaded. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this ONCE at the beginning of the game. * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +void HouseClass::One_Time(void) +{ +// for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) { +// new(index) HouseClass; +// } +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * HouseClass::Debug_Dump -- Dumps the house status data to the mono screen. * + * * + * This utility function will output the current status of the house class to the mono * + * screen. Through this information bugs may be fixed or detected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Debug_Dump(MonoClass *) const +{ + Validate(); +} +#endif + + +/*********************************************************************************************** + * HouseClass::new -- Allocator for a house class. * + * * + * This is the allocator for a house class. Since there can be only * + * one of each type of house, this is allocator has restricted * + * functionality. Any attempt to allocate a house structure for a * + * house that already exists, just returns a pointer to the previously * + * allocated house. * + * * + * INPUT: house -- The house to allocate a class object for. * + * * + * OUTPUT: Returns with a pointer to the allocated class object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1994 JLB : Created. * + *=============================================================================================*/ +void * HouseClass::operator new(size_t) +{ + void * ptr = Houses.Allocate(); + if (ptr) { + ((HouseClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * HouseClass::delete -- Deallocator function for a house object. * + * * + * This function marks the house object as "deallocated". Such a * + * house object is available for reallocation later. * + * * + * INPUT: ptr -- Pointer to the house object to deallocate. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1994 JLB : Created. * + *=============================================================================================*/ +void HouseClass::operator delete(void *ptr) +{ + if (ptr) { + ((HouseClass *)ptr)->IsActive = false; + } + Houses.Free((HouseClass *)ptr); +} + + +/*********************************************************************************************** + * HouseClass::HouseClass -- Constructor for a house object. * + * * + * This function is the constructor and it marks the house object * + * as being allocated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1994 JLB : Created. * + *=============================================================================================*/ +HouseClass::HouseClass(HousesType house) : + Class(&HouseTypeClass::As_Reference(house)), + IonCannon(ION_CANNON_GONE_TIME, VOX_ION_READY, VOX_ION_CHARGING, VOX_ION_CHARGING, VOX_NO_POWER), + AirStrike(AIR_CANNON_GONE_TIME, VOX_AIRSTRIKE_READY, VOX_NONE, VOX_NOT_READY, VOX_NOT_READY), + NukeStrike(NUKE_GONE_TIME, VOX_NUKE_AVAILABLE, VOX_NONE, VOX_NOT_READY, VOX_NO_POWER) +{ + + for (HousesType i = HOUSE_FIRST; i < HOUSE_COUNT; i++) { + UnitsKilled[i] = 0; + BuildingsKilled[i] = 0; + } + WhoLastHurtMe = house; // init this to myself + + IsVisionary = false; + IsFreeHarvester = false; + Blockage = 0; + UnitsLost = 0; + BuildingsLost = 0; + + NewActiveBScan = 0; + ActiveBScan = 0; + NewActiveUScan = 0; + ActiveUScan = 0; + NewActiveIScan = 0; + ActiveIScan = 0; + NewActiveAScan = 0; + ActiveAScan = 0; + + strcpy((char *)Name, "Computer"); // Default computer name. + JustBuilt = STRUCT_NONE; + AlertTime = 0; + IsAlerted = false; + IsAirstrikePending = false; + AircraftFactory = -1; + AircraftFactories = 0; + ActLike = Class->House; + Allies = 0; + AScan = 0; + NukeDest = 0; + BlitzTime.Clear(); + BScan = 0; + BuildingFactories = 0; + BuildingFactory = -1; + Capacity = 0; + Credits = 0; + CreditsSpent = 0; + CurBuildings = 0; + CurUnits = 0; + DamageTime = DAMAGE_DELAY; + Drain = 0; + Edge = SOURCE_NORTH; + FlagHome = 0; + FlagLocation = TARGET_NONE; + HarvestedCredits = 0; + HouseTriggers[house].Clear(); + IGaveUp = false; + InfantryFactories = 0; + InfantryFactory = -1; + InitialCredits = 0; + InitialCredits = 0; + IScan = 0; + IsRecalcNeeded = true; + IsCivEvacuated = false; + IsDefeated = false; + IsDiscovered = false; + IsHuman = false; + IsMaxedOut = false; + IsStarted = false; + IsToDie = false; + IsToLose = false; + IsToWin = false; + Make_Ally(house); + MaxBuilding = 0; + MaxUnit = 0; + NewAScan = 0; + NewBScan = 0; + NewIScan = 0; + NewUScan = 0; + NukePieces = 0x07; + Power = 0; + RemapTable = Class->RemapTable; + RemapColor = Class->RemapColor; + Resigned = false; + SpeakAttackDelay = 1; + SpeakMaxedDelay = 1; + SpeakMoneyDelay = 1; + SpeakPowerDelay = 1; + SpecialFactories = 0; + SpecialFactory = -1; + TeamTime = TEAM_DELAY; + Tiberium = 0; + TriggerTime = 0; + UnitFactories = 0; + UnitFactory = -1; + UScan = 0; + memset((void *)&Regions[0], 0x00, sizeof(Regions)); + + AircraftTotals = new UnitTrackerClass( (int) AIRCRAFT_COUNT); + InfantryTotals = new UnitTrackerClass( (int) INFANTRY_COUNT); + UnitTotals = new UnitTrackerClass ( (int) UNIT_COUNT); + BuildingTotals = new UnitTrackerClass ( (int) STRUCT_COUNT); + + DestroyedAircraft = new UnitTrackerClass ( (int) AIRCRAFT_COUNT); + DestroyedInfantry = new UnitTrackerClass( (int) INFANTRY_COUNT); + DestroyedUnits = new UnitTrackerClass ( (int) UNIT_COUNT); + DestroyedBuildings = new UnitTrackerClass ( (int) STRUCT_COUNT); + + CapturedBuildings = new UnitTrackerClass ( (int) STRUCT_COUNT); + TotalCrates = new UnitTrackerClass ( TOTAL_CRATE_TYPES ); //15 crate types +} + + +HouseClass::~HouseClass (void) +{ + delete AircraftTotals; + delete InfantryTotals; + delete UnitTotals; + delete BuildingTotals; + + delete DestroyedAircraft; + delete DestroyedInfantry; + delete DestroyedUnits; + delete DestroyedBuildings; + + delete CapturedBuildings; + delete TotalCrates; +} + +/*********************************************************************************************** + * HouseClass::Can_Build -- General purpose build legality checker. * + * * + * This routine is called when it needs to be determined if the specified object type can * + * be built by this house. Production and sidebar maintenance use this routine heavily. * + * * + * INPUT: type -- Pointer to the type of object that legality is to be checked for. * + * * + * house -- This is the house to check for legality against. Note that this might * + * not be 'this' house since the check could be from a captured factory. * + * Captured factories build what the original owner of them could build. * + * * + * OUTPUT: Can the specified object be built? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + * 08/12/1995 JLB : Updated for GDI building sandbag walls in #9. * + *=============================================================================================*/ +bool HouseClass::Can_Build(TechnoTypeClass const * type, HousesType house) const +{ + Validate(); + if (!type || !type->IsBuildable || !((1L << house) & type->Ownable)) return(false); + + /* + ** The computer can always build everthing. + */ + if (!IsHuman) return(true); + + /* + ** Perform some equivalency fixups for the building existance flags. + */ + long flags = ActiveBScan; + int pre = type->Pre; + if (flags & STRUCTF_ADVANCED_POWER) flags |= STRUCTF_POWER; + if (flags & STRUCTF_HAND) flags |= STRUCTF_BARRACKS; + if (flags & STRUCTF_OBELISK) flags |= STRUCTF_ATOWER; + if (flags & STRUCTF_TEMPLE) flags |= STRUCTF_EYE; + if (flags & STRUCTF_AIRSTRIP) flags |= STRUCTF_WEAP; + if (flags & STRUCTF_SAM) flags |= STRUCTF_HELIPAD; + + /* + ** Multiplayer game uses a different legality check for building. + */ + if (GameToPlay != GAME_NORMAL || (Special.IsJurassic && AreThingiesEnabled)) { + return((pre & flags) == pre && type->Level <= BuildLevel); + } + +#ifdef NEWMENU + int level = BuildLevel; +#else + int level = Scenario; +#endif + + /* + ** Special check to make the mission objective buildings the prerequisite + ** for the stealth tank in mission #11 only. + */ + if (house == HOUSE_BAD && + type->What_Am_I() == RTTI_UNITTYPE && + ((UnitTypeClass const *)type)->Type == UNIT_STANK && + level == 11) { + + pre = STRUCTF_MISSION; + level = type->Scenario; + } + + /* + ** Special case check to ensure that GDI doesn't get the bazooka guy + ** until mission #8. + */ + if (house == HOUSE_GOOD && + type->What_Am_I() == RTTI_INFANTRYTYPE && + ((InfantryTypeClass const *)type)->Type == INFANTRY_E3 && + level < 7) { + + return(false); + } + + /* + ** Special check to allow GDI to build the MSAM by mission #9 + ** and no sooner. + */ + if (house == HOUSE_GOOD && + type->What_Am_I() == RTTI_UNITTYPE && + ((UnitTypeClass const *)type)->Type == UNIT_MLRS && + level < 9) { + + return(false); + } + + /* + ** Special case to disable the APC from the Nod player. + */ + if (house == HOUSE_BAD && + type->What_Am_I() == RTTI_UNITTYPE && + ((UnitTypeClass const *)type)->Type == UNIT_APC) { + + return(false); + } + + /* + ** Ensure that the Temple of Nod cannot be built by GDI even + ** if GDI has captured the Nod construction yard. + */ + if (type->What_Am_I() == RTTI_BUILDINGTYPE && + (((BuildingTypeClass const *)type)->Type == STRUCT_TEMPLE || ((BuildingTypeClass const *)type)->Type == STRUCT_OBELISK) && + Class->House == HOUSE_GOOD) { + + return(false); + } + + /* + ** Ensure that the rocket launcher tank cannot be built by Nod. + */ + if (type->What_Am_I() == RTTI_UNITTYPE && + ((UnitTypeClass const *)type)->Type == UNIT_MLRS && + Class->House == HOUSE_BAD) { + + return(false); + } + + /* + ** Ensure that the ion cannon cannot be built if + ** Nod has captured the GDI construction yard. + */ + if (type->What_Am_I() == RTTI_BUILDINGTYPE && + (((BuildingTypeClass const *)type)->Type == STRUCT_EYE) && + Class->House == HOUSE_BAD) { + + return(false); + } + + /* + ** Nod can build the advanced power plant at scenario #12. + */ + if (house == HOUSE_BAD && + level >= 12 && + type->What_Am_I() == RTTI_BUILDINGTYPE && + ((BuildingTypeClass const *)type)->Type == STRUCT_ADVANCED_POWER) { + + level = type->Scenario; + } + + /* + ** Nod cannot build a helipad in the normal game. + */ + if (house == HOUSE_BAD && + type->What_Am_I() == RTTI_BUILDINGTYPE && + ((BuildingTypeClass const *)type)->Type == STRUCT_HELIPAD) { + + return(false); + } + + /* + ** GDI can build the sandbag wall only from scenario #9 onwards. + */ + if (house == HOUSE_GOOD && + level < 8 && + type->What_Am_I() == RTTI_BUILDINGTYPE && + ((BuildingTypeClass const *)type)->Type == STRUCT_SANDBAG_WALL) { + + return(false); + } + + /* + ** GDI has a special second training mission. Adjust the scenario level so that + ** scenario two will still feel like scenario #1. + */ + if (house == HOUSE_GOOD && level == 2) { + level = 1; + } + + if (Debug_Cheat) level = 98; + return((pre & flags) == pre && type->Scenario <= level); +} + + +/*********************************************************************************************** + * HouseClass::Can_Build -- Determines if the building type can be built. * + * * + * This routine is used by the construction preparation code to building a list of building * + * types that can be built. It determines if a building can be built by checking if the * + * prerequisite buildings have been built (and still exist) as well as checking to see if * + * the house can build the specified structure. * + * * + * INPUT: s -- The structure type number that is being checked. * + * * + * house -- The house number to use when determining if the object can be built. * + * This is necessary because the current owner of the factory doesn't * + * control what the factory can produce. Rather, the original builder of * + * the factory controls this. * + * * + * OUTPUT: bool; Can this structure type be built at this time? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/08/1994 JLB : Created. * + * 05/31/1995 JLB : Handles specified ownership override. * + *=============================================================================================*/ +bool HouseClass::Can_Build(StructType s, HousesType house) const +{ + Validate(); + return(Can_Build(&BuildingTypeClass::As_Reference(s), house)); +} + + +/*********************************************************************************************** + * HouseClass::Can_Build -- Determines if the infantry unit can be built by this house. * + * * + * Use this routine to determine if the infantry type specified can be built by this * + * house. It determines this by checking the ownership allowed bits in the infantry * + * type class. * + * * + * INPUT: infantry -- The infantry type to check against this house. * + * * + * house -- The house number to use when determining if the object can be built. * + * This is necessary because the current owner of the factory doesn't * + * control what the factory can produce. Rather, the original builder of * + * the factory controls this. * + * * + * OUTPUT: bool; Can the infantry be produced by this house? * + * * + * WARNINGS: It does not check to see if there is a functional barracks available, but * + * merely checks to see if it is legal for this house to build that infantry * + * type. * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + * 05/31/1995 JLB : Handles specified ownership override. * + *=============================================================================================*/ +bool HouseClass::Can_Build(InfantryType infantry, HousesType house) const +{ + Validate(); + return(Can_Build(&InfantryTypeClass::As_Reference(infantry), house)); +} + + +/*********************************************************************************************** + * HouseClass::Can_Build -- Determines if the unit can be built by this house. * + * * + * This routine is used to determine if the unit type specified can in fact be built by * + * this house. It checks the ownable bits in the unit's type to determine this. * + * * + * INPUT: unit -- The unit type to check against this house. * + * * + * house -- The house number to use when determining if the object can be built. * + * This is necessary because the current owner of the factory doesn't * + * control what the factory can produce. Rather, the original builder of * + * the factory controls this. * + * * + * OUTPUT: bool; Can the unit be built by this house? * + * * + * WARNINGS: This doesn't check to see if there is a functional factory that can build * + * this unit, but merely if the unit can be built according to ownership rules. * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + * 05/31/1995 JLB : Handles specified ownership override. * + *=============================================================================================*/ +bool HouseClass::Can_Build(UnitType unit, HousesType house) const +{ + Validate(); + return(Can_Build(&UnitTypeClass::As_Reference(unit), house)); +} + + +/*********************************************************************************************** + * HouseClass::Can_Build -- Determines if the aircraft type can be built. * + * * + * Use this routine to determine if the specified aircraft type can be built. This routine * + * is used by the sidebar and factory to determine what can be built. * + * * + * INPUT: aircraft -- The aircraft type to check for build legality. * + * * + * house -- The house that is performing the check. This is typically the house * + * of the original building of the factory rather than the current * + * owner. * + * * + * OUTPUT: Can this aircraft type be built by the house specified? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Can_Build(AircraftType aircraft, HousesType house) const +{ + Validate(); + return(Can_Build(&AircraftTypeClass::As_Reference(aircraft), house)); +} + + +/*************************************************************************** + * HouseClass::Init -- init's in preparation for new scenario * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + * 12/17/1994 JLB : Resets tracker bits. * + *=========================================================================*/ +void HouseClass::Init(void) +{ + Houses.Free_All(); + + for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) { + HouseTriggers[index].Clear(); + } +} + + +/*********************************************************************************************** + * HouseClass::AI -- Process house logic. * + * * + * This handles the AI for the house object. It should be called once per house per game * + * tick. It processes all house global tasks such as low power damage accumulation and * + * house specific trigger events. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + * 07/17/1995 JLB : Limits EVA speaking unless the player can do something. * + *=============================================================================================*/ +void HouseClass::AI(void) +{ + Validate(); + + /* + ** Reset the scan accumulation bits for the next logic pass. + */ + IScan = NewIScan; + BScan = NewBScan; + UScan = NewUScan; + AScan = NewAScan; + ActiveIScan = NewActiveIScan; + ActiveBScan = NewActiveBScan; + ActiveUScan = NewActiveUScan; + ActiveAScan = NewActiveAScan; + NewIScan = 0; + NewBScan = 0; + NewUScan = 0; + NewAScan = 0; + NewActiveIScan = 0; + NewActiveBScan = 0; + NewActiveUScan = 0; + NewActiveAScan = 0; + + /* + ** Check to see if the house wins. + */ + if (GameToPlay == GAME_NORMAL && IsToWin && BorrowedTime.Expired() && Blockage <= 0) { + IsToWin = false; + if (this == PlayerPtr) { + PlayerWins = true; + } else { + PlayerLoses = true; + } + } + + /* + ** Check to see if the house loses. + */ + if (GameToPlay == GAME_NORMAL && IsToLose && BorrowedTime.Expired()) { + IsToLose = false; + if (this == PlayerPtr) { + PlayerLoses = true; + } else { + PlayerWins = true; + } + } + + /* + ** Check to see if all objects of this house should be blown up. + */ + if (IsToDie && BorrowedTime.Expired()) { + IsToDie = false; + Blowup_All(); + } + + /* + ** Double check power values to correct illegal conditions. It is possible to + ** get a power output of negative (one usually) as a result of damage sustained + ** and the fixed point fractional math involved with power adjustements. If the + ** power rating drops below zero, then make it zero. + */ + if (GameToPlay == GAME_NORMAL) { + Power = MAX(Power, 0); + Drain = MAX(Drain, 0); + } + + /* + ** If the base has been alerted to the enemy and should be attacking, then + ** see if the attack timer has expired. If it has, then create the attack + ** teams. + */ + if (IsAlerted && AlertTime.Expired()) { + + /* + ** Adjusted to reduce maximum number of teams created. + */ + int maxteams = Random_Pick(2, (int)(((BuildLevel-1)/3)+1)); + for (int index = 0; index < maxteams; index++) { + TeamTypeClass const * ttype = Suggested_New_Team(true); + if (ttype) { + ScenarioInit++; + ttype->Create_One_Of(); + ScenarioInit--; + } + } + if (Special.IsDifficult) { + AlertTime = (TICKS_PER_MINUTE * Random_Pick(4, 10)); + } else { + if (Special.IsEasy) { + AlertTime = (TICKS_PER_MINUTE * Random_Pick(16, 40)); + } else { + AlertTime = (TICKS_PER_MINUTE * Random_Pick(5, 20)); + } + } + } + + /* + ** Create teams for this house if necessary. + ** (Use the same timer for some extra capture-the-flag logic.) + */ + if (TeamTime.Expired()) { + TeamTypeClass const * ttype = Suggested_New_Team(false); + if (ttype) { + ttype->Create_One_Of(); + } + + /* + ** Also use this timer to detect if someone is sitting on my flag cell. + */ + if (Special.IsCaptureTheFlag && GameToPlay != GAME_NORMAL) { + TechnoClass *techno; + int damage; + int count; + int moving; + + /* + ** If this house's flag waypoint is a valid cell, see if there's + ** someone sitting on it. If so, make the scatter. If they refuse, + ** blow them up. + */ + if (FlagHome) { + techno = Map[FlagHome].Cell_Techno(); + if (techno) { + moving = false; + techno->Scatter(0,true); + + /* + ** If the techno doesn't have a valid NavCom, he's not moving, + ** so blow him up. + */ + if (techno->What_Am_I() == RTTI_INFANTRY || + techno->What_Am_I() == RTTI_UNIT) { + if (Target_Legal(((FootClass *)techno)->NavCom)) { + moving = true; + } + } + + /* + ** If the techno wasn't an infantry or unit (ie he's a building), + ** or he refuses to move, blow him up + */ + if (!moving) { + count = 0; + while (!(techno->IsInLimbo) && count++ < 5) { + damage = 0x7fff; + Explosion_Damage(techno->Center_Coord(), damage, NULL, WARHEAD_HE); + } + } + } + } + } + + /* + ** Randomly create a Visceroid or other disastrous multiplayer object. + ** Create the object, and use Scan_Place_Object to place the object near + ** the center of the map. + */ + if (GameToPlay != GAME_NORMAL && Class->House==HOUSE_JP) { + int rlimit; + + if (Special.IsJurassic && AreThingiesEnabled) { + rlimit = 450; + } else { + rlimit = 1000; + } + + if (IRandom(0, rlimit) == 0) { + UnitClass *obj = NULL; + CELL cell; + + if (Special.IsJurassic && AreThingiesEnabled) { + obj = new UnitClass(Random_Pick(UNIT_TRIC, UNIT_STEG), HOUSE_JP); + } else { + if (BuildLevel >= 7) { + if (!(UScan & UNITF_VICE)) { + obj = new UnitClass(UNIT_VICE, HOUSE_JP); + } + } + } + + if (obj) { + cell = XY_Cell (Map.MapCellX + Random_Pick(0, Map.MapCellWidth - 1), + Map.MapCellY + Random_Pick(0, Map.MapCellHeight - 1)); + if (!Scan_Place_Object(obj, cell)) { + delete obj; + } + } + } + } + + TeamTime.Set(TEAM_DELAY); + } + + /* + ** If there is insufficient power, then all buildings that are above + ** half strength take a little bit of damage. + */ + if (DamageTime.Expired()) { + +/* +** No free harvesters for computer or player. - 8/16/95 +*/ +#ifdef OBSOLETE + /* + ** Replace the last harvester if there is a refinery present. + */ + if (GameToPlay == GAME_NORMAL && + Frame > 5 && + (!IsHuman && BuildLevel <= 6) && + (ActiveBScan & STRUCTF_REFINERY) != 0 && + (UScan & UNITF_HARVESTER) == 0 && + !IsFreeHarvester) { + + IsFreeHarvester = true; + FreeHarvester = TICKS_PER_MINUTE * 2; + } +#endif + + /* + ** If a free harvester is to be created and the time is right, then create + ** the harvester and clear the free harvester pending flag. + */ + if (IsFreeHarvester && FreeHarvester.Expired()) { + IsFreeHarvester = false; + Create_Special_Reinforcement(this, (TechnoTypeClass *)&UnitTypeClass::As_Reference(UNIT_HARVESTER), NULL); + } + + /* + ** When the power is below required, then the buildings will take damage over + ** time. + */ + if (Power_Fraction() < 0x100) { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass & b = *Buildings.Ptr(index); + + if (b.House == this && b.Health_Ratio() > 0x080) { + int damage = 1; + b.Take_Damage(damage, 0, WARHEAD_AP, 0); + } + } + } + DamageTime.Set(DAMAGE_DELAY); + } + + /* + ** If there are no more buildings to sell, then automatically cancel the + ** sell mode. + */ + if (PlayerPtr == this && !BScan && Map.IsSellMode) { + Map.Sell_Mode_Control(0); + } + + /* + ** Various base conditions may be announced to the player. + */ + if (PlayerPtr == this) { + + if (SpeakMaxedDelay.Expired() && IsMaxedOut) { + IsMaxedOut = false; + if ((Capacity - Tiberium) < 300 && Capacity > 500 && (BScan & (STRUCTF_REFINERY | STRUCTF_CONST))) { + Speak(VOX_NEED_MO_CAPACITY); + SpeakMaxedDelay.Set(Options.Normalize_Delay(SPEAK_DELAY)); + } + } + if (SpeakPowerDelay.Expired() && Power_Fraction() < 0x0100) { + if (BScan & STRUCTF_CONST) { + Speak(VOX_LOW_POWER); + SpeakPowerDelay.Set(Options.Normalize_Delay(SPEAK_DELAY)); + } + } + } + + /* + ** If there is a flag associated with this house, then mark it to be + ** redrawn. + */ + if (Target_Legal(FlagLocation)) { + UnitClass * unit = As_Unit(FlagLocation); + if (unit) { + unit->Mark(MARK_CHANGE); + } else { + CELL cell = As_Cell(FlagLocation); + Map[cell].Redraw_Objects(); + } + } + + bool is_time = false; + + /* + ** Triggers are only checked every so often. If the trigger timer has expired, + ** then set the trigger processing flag. + */ + if (TriggerTime.Expired()) { + is_time = true; + TriggerTime = TICKS_PER_MINUTE/10; + } + + /* + ** Check to see if the ion cannon should be removed from the sidebar + ** because of outside circumstances. The advanced communications facility + ** being destroyed is a good example of this. + */ + if (IonCannon.Is_Present()) { + if (!(ActiveBScan & STRUCTF_EYE) && !IonCannon.Is_One_Time()) { + + /* + ** Remove the ion cannon when there is no advanced communication facility. + ** Note that this will not remove the one time created ion cannon. + */ + if (IonCannon.Remove()) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + IsRecalcNeeded = true; + } + } else { + + /* + ** Turn the ion cannon suspension on or off depending on the available + ** power. Note that one time ion cannon will not be affected by this. + */ + IonCannon.Suspend(Power_Fraction() < 0x0100); + + /* + ** Process the ion cannon AI and if something changed that would affect + ** the sidebar, then flag the sidebar to be redrawn. + */ + if (IonCannon.AI(this == PlayerPtr)) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + } + } + + /* + ** The computer may decide to fire the ion cannon if it is ready. + */ + if (IonCannon.Is_Ready() && !IsHuman) { + Special_Weapon_AI(SPC_ION_CANNON); + } + + } else { + + /* + ** If there is no ion cannon present, but there is an advanced communcation + ** center available, then make the ion cannon available as well. + */ + if ((ActiveBScan & STRUCTF_EYE) && + (ActLike == HOUSE_GOOD || GameToPlay != GAME_NORMAL) && + (IsHuman || GameToPlay != GAME_NORMAL)) { + + IonCannon.Enable(false, this == PlayerPtr, Power_Fraction() < 0x0100); + + /* + ** Flag the sidebar to be redrawn if necessary. + */ + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_ION_CANNON); + Map.Column[1].Flag_To_Redraw(); + } + } + } + + /* + ** Check to see if the nuke strike should be removed from the sidebar + ** because of outside circumstances. The Temple of Nod + ** being destroyed is a good example of this. + */ + if (NukeStrike.Is_Present()) { + if (!(ActiveBScan & STRUCTF_TEMPLE) && (!NukeStrike.Is_One_Time() || GameToPlay == GAME_NORMAL)) { + + /* + ** Remove the nuke strike when there is no Temple of Nod. + ** Note that this will not remove the one time created nuke strike. + */ + if (NukeStrike.Remove(true)) { + IsRecalcNeeded = true; + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + } + } else { + + /* + ** Turn the nuke strike suspension on or off depending on the available + ** power. Note that one time nuke strike will not be affected by this. + */ + NukeStrike.Suspend(Power_Fraction() < 0x0100); + + /* + ** Process the nuke strike AI and if something changed that would affect + ** the sidebar, then flag the sidebar to be redrawn. + */ + if (NukeStrike.AI(this == PlayerPtr)) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + } + } + + /* + ** The computer may decide to fire the nuclear missile if it is ready. + */ + if (NukeStrike.Is_Ready() && !IsHuman) { + Special_Weapon_AI(SPC_NUCLEAR_BOMB); + } + + } else { + + /* + ** If there is no nuke strike present, but there is a Temple of Nod + ** available, then make the nuke strike strike available. + */ + if ((ActiveBScan & STRUCTF_TEMPLE) && Has_Nuke_Device() && IsHuman) { + NukeStrike.Enable((GameToPlay == GAME_NORMAL), this == PlayerPtr); + + /* + ** Flag the sidebar to be redrawn if necessary. + */ + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB); + Map.Column[1].Flag_To_Redraw(); + } + } + } + + /* + ** Process the airstrike AI and if something changed that would affect + ** the sidebar, then flag the sidebar to be redrawn. + */ + if (AirStrike.Is_Present()) { + if (AirStrike.AI(this == PlayerPtr)) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + } + + /* + ** The computer may decide to call in the airstrike if it is ready. + */ + if (AirStrike.Is_Ready() && !IsHuman) { + Special_Weapon_AI(SPC_AIR_STRIKE); + } + } + + /* + ** Add the airstrike option if it is pending. + */ + if (IsAirstrikePending) { + IsAirstrikePending = false; + if (AirStrike.Enable(false, this == PlayerPtr)) { + AirStrike.Forced_Charge(this == PlayerPtr); + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_AIR_STRIKE); + Map.Column[1].Flag_To_Redraw(); + } + } + } + +#ifdef NEVER + /* + ** The following logic deals with the nuclear warhead state machine. It + ** handles all the different stages of the temple firing and the rocket + ** travelling up and down. The rocket explosion is handled by the anim + ** which is attached to the bullet. + */ + if (!IsHuman && NukePresent) { + Special_Weapon_AI(SPC_NUCLEAR_BOMB); + + } +#endif + + /* + ** Special win/lose check for multiplayer games; by-passes the + ** trigger system. We must wait for non-zero frame, because init + ** may not properly set IScan etc for each house; you have to go + ** through each object's AI before it will be properly set. + */ + if (GameToPlay != GAME_NORMAL && !IsDefeated && + !ActiveBScan && !ActiveAScan && !UScan && !ActiveIScan && Frame > 0) { + MPlayer_Defeated(); + } + + for (int index = 0; index < HouseTriggers[Class->House].Count(); index++) { + TriggerClass * t = HouseTriggers[Class->House][index]; + + /* + ** Check for just built the building trigger event. + */ + if (JustBuilt != STRUCT_NONE) { + if (t->Spring(EVENT_BUILD, Class->House, JustBuilt)) { + JustBuilt = STRUCT_NONE; + continue; + } + } + + /* + ** Check for civilian evacuation trigger event. + */ + if (IsCivEvacuated && t->Spring(EVENT_EVAC_CIVILIAN, Class->House)) { + continue; + } + + /* + ** Number of buildings destroyed checker. + */ + if (t->Spring(EVENT_NBUILDINGS_DESTROYED, Class->House, BuildingsLost)) { + continue; + } + + /* + ** Number of units destroyed checker. + */ + if (t->Spring(EVENT_NUNITS_DESTROYED, Class->House, UnitsLost)) { + continue; + } + + /* + ** House has been revealed trigger event. + */ + if (IsDiscovered && t->Spring(EVENT_HOUSE_DISCOVERED, Class->House)) { + IsDiscovered = false; + continue; + } + + /* + ** The "all destroyed" triggers are only processed after a certain + ** amount of safety time has expired. + */ + if (!EndCountDown) { + + /* + ** All buildings destroyed checker. + */ + if (!ActiveBScan) { + if (t->Spring(EVENT_BUILDINGS_DESTROYED, Class->House)) { + continue; + } + } + + /* + ** All units destroyed checker. + */ + if (!((ActiveUScan & ~(UNITF_GUNBOAT)) | IScan | (ActiveAScan & ~(AIRCRAFTF_TRANSPORT|AIRCRAFTF_CARGO|AIRCRAFTF_A10)))) { + if (t->Spring(EVENT_UNITS_DESTROYED, Class->House)) { + continue; + } + } + + /* + ** All buildings AND units destroyed checker. + */ + if (!(ActiveBScan | (ActiveUScan & ~(UNITF_GUNBOAT)) | IScan | (ActiveAScan & ~(AIRCRAFTF_TRANSPORT|AIRCRAFTF_CARGO|AIRCRAFTF_A10)))) { + if (t->Spring(EVENT_ALL_DESTROYED, Class->House)) { + continue; + } + } + } + + /* + ** Credit check. + */ + if (t->Spring(EVENT_CREDITS, Class->House, Credits)) { + continue; + } + + /* + ** Timeout check. + */ + if (is_time && t->Spring(EVENT_TIME, Class->House)) { + continue; + } + + /* + ** All factories destroyed check. + */ + if (!(BScan & (STRUCTF_AIRSTRIP|STRUCTF_HAND|STRUCTF_WEAP|STRUCTF_BARRACKS)) && t->Spring(EVENT_NOFACTORIES, Class->House)) { + continue; + } + } + + /* + ** If a radar facility is not present, but the radar is active, then turn the radar off. + ** The radar also is turned off when the power gets below 100% capacity. + */ + if (PlayerPtr == this) { + if (Map.Is_Radar_Active()) { + if (BScan & (STRUCTF_RADAR|STRUCTF_EYE)) { + if (Power_Fraction() < 0x0100) { + Map.Radar_Activate(0); + } + } else { + Map.Radar_Activate(0); + } + } else { + if (BScan & (STRUCTF_RADAR|STRUCTF_EYE)) { + if (Power_Fraction() >= 0x0100) { + Map.Radar_Activate(1); + } + } else { + if (Map.Is_Radar_Existing()) { + Map.Radar_Activate(4); + } + } + } + } + + /* + ** If the production possibilities need to be recalculated, then do so now. This must + ** occur after the scan bits have been properly updated. + */ + if (PlayerPtr == this && IsRecalcNeeded) { + IsRecalcNeeded = false; + Map.Recalc(); + +#ifdef NEVER + /* + ** Remove the ion cannon if necessary. + */ + if (IonCannon.Is_Present() && !(BScan & STRUCTF_EYE)) { + IonCannon.Remove(); + } + + /* + ** Remove the nuclear bomb if necessary. + */ + if (NukeStrike.Is_Present() && !(BScan & STRUCTF_TEMPLE)) { + NukeStrike.Remove(); + } +#endif + + /* + ** This placement might affect any prerequisite requirements for construction + ** lists. Update the buildable options accordingly. + */ + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + if (building && building->Owner() == Class->House) { + + building->Update_Specials(); + if (PlayerPtr == building->House) { + building->Update_Buildables(); + } + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Attacked -- Lets player know if base is under attack. * + * * + * Call this function whenever a building is attacked (with malice). This function will * + * then announce to the player that his base is under attack. It checks to make sure that * + * this is referring to the player's house rather than the enemy's. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Attacked(void) +{ + Validate(); + if (SpeakAttackDelay.Expired() && PlayerPtr->Class->House == Class->House) { + Speak(VOX_BASE_UNDER_ATTACK); + SpeakAttackDelay.Set(Options.Normalize_Delay(SPEAK_DELAY)); + + /* + ** If there is a trigger event associated with being attacked, process it + ** now. + */ + for (int index = 0; index < HouseTriggers[Class->House].Count(); index++) { + HouseTriggers[Class->House][index]->Spring(EVENT_ATTACKED, Class->House); + } + } +} + + +/*********************************************************************************************** + * HouseClass::Harvested -- Adds Tiberium to the harvest storage. * + * * + * Use this routine whenever Tiberium is harvested. The Tiberium is stored equally between * + * all storage capable buildings for the house. Harvested Tiberium adds to the credit * + * value of the house, but only up to the maximum storage capacity that the house can * + * currently maintain. * + * * + * INPUT: tiberium -- The number of Tiberium credits to add to the House's total. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Harvested(unsigned tiberium) +{ + Validate(); + long oldtib = Tiberium; + + Tiberium += tiberium; + if (Tiberium > Capacity) { + Tiberium = Capacity; + IsMaxedOut = true; + } + HarvestedCredits += tiberium; + Silo_Redraw_Check(oldtib, Capacity); +} + + +/*********************************************************************************************** + * HouseClass::Available_Money -- Fetches the total credit worth of the house. * + * * + * Use this routine to determine the total credit value of the house. This is the sum of * + * the harvested Tiberium in storage and the initial unspent cash reserves. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the total credit value of the house. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + *=============================================================================================*/ +long HouseClass::Available_Money(void) const +{ + Validate(); + return(Tiberium + Credits); +} + + +/*********************************************************************************************** + * HouseClass::Spend_Money -- Removes money from the house. * + * * + * Use this routine to extract money from the house. Typically, this is a result of * + * production spending. The money is extracted from available cash reserves first. When * + * cash reserves are exhausted, then Tiberium is consumed. * + * * + * INPUT: money -- The amount of money to spend. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + * 06/20/1995 JLB : Spends Tiberium before spending cash. * + *=============================================================================================*/ +void HouseClass::Spend_Money(unsigned money) +{ + Validate(); + long oldtib = Tiberium; + if (money > Tiberium) { + money -= (unsigned)Tiberium; + Tiberium = 0; + Credits -= money; + } else { + Tiberium -= money; + } + Silo_Redraw_Check(oldtib, Capacity); + CreditsSpent += money; +} + + +/*********************************************************************************************** + * HouseClass::Refund_Money -- Refunds money to back to the house. * + * * + * Use this routine when money needs to be refunded back to the house. This can occur when * + * construction is aborted. At this point, the exact breakdown of Tiberium or initial * + * credits used for the orignal purchase is lost. Presume as much of the money is in the * + * form of Tiberium as storage capacity will allow. * + * * + * INPUT: money -- The number of credits to refund back to the house. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + * 06/01/1995 JLB : Refunded money is never lost * + *=============================================================================================*/ +void HouseClass::Refund_Money(unsigned money) +{ + Validate(); + Credits += money; +} + + +/*********************************************************************************************** + * HouseClass::Adjust_Capacity -- Adjusts the house Tiberium storage capacity. * + * * + * Use this routine to adjust the maximum storage capacity for the house. This storage * + * capacity will limit the number of Tiberium credits that can be stored at any one time. * + * * + * INPUT: adjust -- The adjustment to the Tiberium storage capacity. * + * * + * inanger -- Is this a forced adjustment to capacity due to some hostile event? * + * * + * OUTPUT: Returns with the number of Tiberium credits lost. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::Adjust_Capacity(int adjust, bool inanger) +{ + Validate(); + long oldcap = Capacity; + int retval = 0; + + Capacity += adjust; + Capacity = MAX(Capacity, 0L); + if (Tiberium > Capacity) { + retval = Tiberium - Capacity; + Tiberium = Capacity; + if (!inanger) { + Refund_Money(retval); + retval = 0; + } else { + IsMaxedOut = true; + } + } + Silo_Redraw_Check(Tiberium, oldcap); + return(retval); +} + + +/*********************************************************************************************** + * HouseClass::Silo_Redraw_Check -- Flags silos to be redrawn if necessary. * + * * + * Call this routine when either the capacity or tiberium levels change for a house. This * + * routine will determine if the aggregate tiberium storage level will result in the * + * silos changing their imagery. If this is detected, then all the silos for this house * + * are flagged to be redrawn. * + * * + * INPUT: oldtib -- Pre-change tiberium level. * + * * + * oldcap -- Pre-change tiberium storage capacity. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/02/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Silo_Redraw_Check(long oldtib, long oldcap) +{ + Validate(); + int oldratio = 0; + if (oldcap) oldratio = (oldtib * 5) / oldcap; + int newratio = 0; + if (Capacity) newratio = (Tiberium * 5) / Capacity; + + if (oldratio != newratio) { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * b = Buildings.Ptr(index); + if (b && !b->IsInLimbo && b->House == this && *b == STRUCT_STORAGE) { + b->Mark(MARK_CHANGE); + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Read_INI -- Reads house specific data from INI. * + * * + * This routine reads the house specific data for a particular * + * scenario from the scenario INI file. Typical data includes starting * + * credits, maximum unit count, etc. * + * * + * INPUT: buffer -- Pointer to loaded scenario INI file. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + * 05/18/1995 JLB : Creates all houses. * + *=============================================================================================*/ +void HouseClass::Read_INI(char *buffer) +{ + HouseClass *p; // Pointer to current player data. + char const *hname; // Pointer to house name. + char buf[128]; + + for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) { + hname = HouseTypeClass::As_Reference(index).IniName; + int maxunit = WWGetPrivateProfileInt(hname, "MaxUnit", EACH_UNIT_MAX, buffer); + + maxunit = MAX(maxunit, 150); + + int maxbuilding = WWGetPrivateProfileInt(hname, "MaxBuilding", EACH_BUILDING_MAX, buffer); + + maxbuilding = MAX(maxbuilding, 150); + + int credits = WWGetPrivateProfileInt(hname, "Credits", 0, buffer); + + p = new HouseClass(index); + + p->MaxBuilding = maxbuilding; + p->MaxUnit = maxunit; + p->Credits = (long)credits * 100; + p->InitialCredits = p->Credits; + WWGetPrivateProfileString(hname, "Edge", "", buf, sizeof(buf)-1, buffer); + p->Edge = Source_From_Name(buf); + if (p->Edge == SOURCE_NONE) { + p->Edge = SOURCE_NORTH; + } + + if (GameToPlay == GAME_NORMAL) { + WWGetPrivateProfileString(hname, "Allies", "", buf, sizeof(buf)-1, buffer); + if (strlen(buf)) { + char * tok = strtok(buf, ", \t"); + while (tok) { + HousesType h = HouseTypeClass::From_Name(tok); + p->Make_Ally(h); + tok = strtok(NULL, ", \t"); + } + + } else { + + /* + ** Special case for when no allies are specified in the INI file. + ** The GDI side defaults to considering the neutral side to be + ** friendly. + */ + if (p->Class->House == HOUSE_GOOD) { + p->Make_Ally(HOUSE_NEUTRAL); + } + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Write_INI -- Writes house specific data into INI file. * + * * + * Use this routine to write the house specific data (for all houses) into the INI file. * + * It is used by the scenario editor when saving the scenario. * + * * + * INPUT: buffer -- INI file staging buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Write_INI(char *buffer) +{ + for (HousesType i = HOUSE_FIRST; i < HOUSE_COUNT; i++) { + HouseClass * p = As_Pointer(i); + + if (p) { + WWWritePrivateProfileInt(p->Class->IniName, "Credits", (int)(p->Credits / 100), buffer); + WWWritePrivateProfileString(p->Class->IniName, "Edge", Name_From_Source(p->Edge), buffer); + WWWritePrivateProfileInt(p->Class->IniName, "MaxUnit", p->MaxUnit, buffer); + WWWritePrivateProfileInt(p->Class->IniName, "MaxBuilding", p->MaxBuilding, buffer); + + bool first = true; + char sbuffer[100] = ""; + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + if (p->Is_Ally(house)) { + if (!first) strcat(sbuffer, ","); + strcat(sbuffer, As_Pointer(house)->Class->IniName); + first = false; + } + } + WWWritePrivateProfileString(p->Class->IniName, "Allies", sbuffer, buffer); + } + } +} + + +/*********************************************************************************************** + * HouseClass::Is_Ally -- Determines if the specified house is an ally. * + * * + * This routine will determine if the house number specified is a ally to this house. * + * * + * INPUT: house -- The house number to check to see if it is an ally. * + * * + * OUTPUT: Is the house an ally? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Is_Ally(HousesType house) const +{ + Validate(); + if (house != HOUSE_NONE) { + return(((1<Class->House)); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Is_Ally -- Checks to see if the object is an ally. * + * * + * This routine will examine the specified object and return whether it is an ally or not. * + * * + * INPUT: object -- The object to examine to see if it is an ally. * + * * + * OUTPUT: Is the specified object an ally? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Is_Ally(ObjectClass const * object) const +{ + Validate(); + if (object) { + return(Is_Ally(object->Owner())); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Make_Ally -- Make the specified house an ally. * + * * + * This routine will make the specified house an ally to this house. An allied house will * + * not be considered a threat or potential target. * + * * + * INPUT: house -- The house to make an ally of this house. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 08/08/1995 JLB : Breaks off combat when ally commences. * + *=============================================================================================*/ +void HouseClass::Make_Ally(HousesType house) +{ + Validate(); + if (house != HOUSE_NONE && !Is_Ally(house)) { + + /* + ** If in normal game play but the house is defeated, then don't allow the ally + ** key to work. + */ + if (!ScenarioInit && (IsDefeated || house == HOUSE_JP)) return; + + Allies |= (1 << house); + +#ifdef CHEAT_KEYS + if (Debug_Flag) { + HouseClass * enemy = HouseClass::As_Pointer(house); + if (enemy && !enemy->Is_Ally(this)) { + enemy->Make_Ally(Class->House); + } + } +#endif + + if ((Debug_Flag || GameToPlay != GAME_NORMAL) && !ScenarioInit) { + char buffer[80]; + + /* + ** Sweep through all techno objects and perform a cheeseball tarcom clear to ensure + ** that fighting will most likely stop when the cease fire begins. + */ + for (int index = 0; index < Logic.Count(); index++) { + ObjectClass * object = Logic[index]; + + if (object && !object->IsInLimbo && object->Owner() == Class->House) { + TARGET target = ((TechnoClass *)object)->TarCom; + if (Target_Legal(target) && As_Techno(target)) { + if (Is_Ally(As_Techno(target))) { + ((TechnoClass *)object)->TarCom = TARGET_NONE; + } + } + } + } + + sprintf(buffer, Text_String(TXT_HAS_ALLIED), Name, HouseClass::As_Pointer(house)->Name); + Messages.Add_Message(buffer, MPlayerTColors[RemapColor], TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1200, 0, 0); + Map.Flag_To_Redraw(false); + } + } +} + + +/*********************************************************************************************** + * HouseClass::Make_Enemy -- Make an enemy of the house specified. * + * * + * This routine will flag the house specified so that it will be an enemy to this house. * + * Enemy houses are legal targets for attack. * + * * + * INPUT: house -- The house to make an enemy of this house. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 07/27/1995 JLB : Making war is a bilateral aaction. * + *=============================================================================================*/ +void HouseClass::Make_Enemy(HousesType house) +{ + Validate(); + if (house != HOUSE_NONE && Is_Ally(house)) { + HouseClass * enemy = HouseClass::As_Pointer(house); + Allies &= ~(1 << house); + if (enemy && enemy->Is_Ally(this)) { + enemy->Allies &= ~(1 << Class->House); + } + + if ((Debug_Flag || GameToPlay != GAME_NORMAL) && !ScenarioInit) { + char buffer[80]; + + sprintf(buffer, Text_String(TXT_AT_WAR), Name, enemy->Name); + Messages.Add_Message(buffer, MPlayerTColors[RemapColor], TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 600, 0, 0); + Map.Flag_To_Redraw(false); + } + } +} + + +/*********************************************************************************************** + * HouseClass::Remap_Table -- Fetches the remap table for this house object. * + * * + * This routine will return with the remap table to use when displaying an object owned * + * by this house. If the object is blushing (flashing), then the lightening remap table is * + * always used. The "unit" parameter allows proper remap selection for those houses that * + * have a different remap table for buildings or units. * + * * + * INPUT: blushing -- Is the object blushing (flashing)? * + * * + * unit -- Is the object a vehicle or infantry? * + * * + * OUTPUT: Returns with a pointer to the remap table to use when drawing this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +unsigned char const * HouseClass::Remap_Table(bool blushing, bool unit) const +{ + Validate(); + if (blushing) return(&Map.FadingLight[0]); + + /* + ** For normal game play, return the TypeClass's remap table for this + ** house type + */ + if (GameToPlay == GAME_NORMAL) { + /* + ** Special case exception for Nod and single player only. Remap + ** buildings to red as opposed to the default color of bluegrey. + */ + if (!unit && Class->House == HOUSE_BAD) { + return(RemapRed); + } + + return(Class->RemapTable); + } else { + + /* + ** For multiplayer games, return the remap table for this exact house instance. + */ + return(RemapTable); + } +} + + +/*********************************************************************************************** + * HouseClass::Suggested_New_Team -- Determine what team should be created. * + * * + * This routine examines the house condition and returns with the team that it thinks * + * should be created. The units that are not currently a member of a team are examined * + * to determine the team needed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the team type that should be created. If no team should * + * be created, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +TeamTypeClass const * HouseClass::Suggested_New_Team(bool alertcheck) +{ + Validate(); + return(TeamTypeClass::Suggested_New_Team(this, UScan, IScan, IsAlerted && alertcheck)); +} + + +/*********************************************************************************************** + * HouseClass::Adjust_Threat -- Adjust threat for the region specified. * + * * + * This routine is called when the threat rating for a region needs to change. The region * + * and threat adjustment are provided. * + * * + * INPUT: region -- The region that adjustment is to occur on. * + * * + * threat -- The threat adjustment to perform. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Adjust_Threat(int region, int threat) +{ + Validate(); + static int _val[] = { + -MAP_REGION_WIDTH - 1, -MAP_REGION_WIDTH, -MAP_REGION_WIDTH + 1, + -1, 0, 1, + MAP_REGION_WIDTH -1, MAP_REGION_WIDTH, MAP_REGION_WIDTH +1 + }; + static int _thr[] = { + 2, 1, 2, + 1, 0, 1, + 2, 1, 2 + }; + int neg; + int *val = &_val[0]; + int *thr = &_thr[0]; + + if (threat < 0) { + threat = -threat; + neg = true; + } else { + neg = false; + } + + for (int lp = 0; lp < 9; lp ++) { + Regions[region + *val].Adjust_Threat(threat >> *thr, neg); + val++; + thr++; + } +} + + +/*********************************************************************************************** + * HouseClass::Begin_Production -- Starts production of the specified object type. * + * * + * This routine is called from the event system. It will start production for the object * + * type specified. This will be reflected in the sidebar as well as the house factory * + * tracking variables. * + * * + * INPUT: type -- The type of object to begin production on. * + * * + * id -- The subtype of object. * + * * + * OUTPUT: Returns with the reason why, or why not, production was started. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +ProdFailType HouseClass::Begin_Production(RTTIType type, int id) +{ + Validate(); + int * factory = 0; + int result = true; + bool initial_start = false; + FactoryClass * fptr; + TechnoTypeClass const * tech = Fetch_Techno_Type(type, id); + + switch (type) { + case RTTI_AIRCRAFT: + case RTTI_AIRCRAFTTYPE: + factory = &AircraftFactory; + break; + + case RTTI_UNIT: + case RTTI_UNITTYPE: + factory = &UnitFactory; + break; + + case RTTI_BUILDING: + case RTTI_BUILDINGTYPE: + factory = &BuildingFactory; + break; + + case RTTI_INFANTRY: + case RTTI_INFANTRYTYPE: + factory = &InfantryFactory; + break; + + case RTTI_SPECIAL: + factory = &SpecialFactory; + break; + } + + /* + ** Check for legality of the production object type suggested. + */ + if (!factory) return(PROD_ILLEGAL); + + /* + ** If the house is already busy producing the requested object, then + ** return with this failure code, unless we are restarting production. + */ + if (*factory != -1) { + fptr = Factories.Raw_Ptr(*factory); + if (fptr->Is_Building()) + return(PROD_CANT); + } else { + fptr = new FactoryClass(); + if (!fptr) return(PROD_CANT); + *factory = Factories.ID(fptr); + result = (tech) ? fptr->Set(*tech, *this) : fptr->Set(id, *this); + initial_start = true; + } + + if (result) { + fptr->Start(); + + /* + ** Link this factory to the sidebar so that proper graphic feedback + ** can take place. + */ + if (PlayerPtr == this) { + Map.Factory_Link(*factory, type, id); + } + + return(PROD_OK); + } + return(PROD_CANT); +} + + +/*********************************************************************************************** + * HouseClass::Suspend_Production -- Temporarily puts production on hold. * + * * + * This routine is called from the event system whenever the production of the specified * + * type needs to be suspended. The suspended production will be reflected in the sidebar * + * as well as in the house control structure. * + * * + * INPUT: type -- The type of object that production is being suspended for. * + * * + * OUTPUT: Returns why, or why not, production was suspended. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +ProdFailType HouseClass::Suspend_Production(RTTIType type) +{ + Validate(); + int * factory = 0; + + switch (type) { + case RTTI_AIRCRAFT: + case RTTI_AIRCRAFTTYPE: + factory = &AircraftFactory; + break; + + case RTTI_UNIT: + case RTTI_UNITTYPE: + factory = &UnitFactory; + break; + + case RTTI_BUILDING: + case RTTI_BUILDINGTYPE: + factory = &BuildingFactory; + break; + + case RTTI_INFANTRY: + case RTTI_INFANTRYTYPE: + factory = &InfantryFactory; + break; + + case RTTI_SPECIAL: + factory = &SpecialFactory; + break; + } + + /* + ** Check for legality of the production object type suggested. + */ + if (!factory) return(PROD_ILLEGAL); + + /* + ** If the house is already busy producing the requested object, then + ** return with this failure code. + */ + if (*factory == -1) return(PROD_CANT); + + /* + ** Create the factory pointer object. + ** If the factory could not be created, then report this error condition. + */ + FactoryClass * fptr = Factories.Raw_Ptr(*factory); + if (!fptr) return(PROD_CANT); + + /* + ** Actually suspend the production. + */ + fptr->Suspend(); + + /* + ** Tell the sidebar that it needs to be redrawn because of this. + */ + if (PlayerPtr == this) { + Map.SidebarClass::IsToRedraw = true; + Map.SidebarClass::Column[0].IsToRedraw = true; + Map.SidebarClass::Column[1].IsToRedraw = true; + Map.Flag_To_Redraw(false); + } + + return(PROD_OK); +} + + +/*********************************************************************************************** + * HouseClass::Abandon_Production -- Abandons production of item type specified. * + * * + * This routine is called from the event system whenever production must be abandoned for * + * the type specified. This will remove the factory and pending object from the sidebar as * + * well as from the house factory record. * + * * + * INPUT: type -- The object type that production is being suspended for. * + * * + * OUTPUT: Returns the reason why or why not, production was suspended. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +ProdFailType HouseClass::Abandon_Production(RTTIType type) +{ + Validate(); + int * factory = 0; + + switch (type) { + case RTTI_AIRCRAFT: + case RTTI_AIRCRAFTTYPE: + factory = &AircraftFactory; + break; + + case RTTI_UNIT: + case RTTI_UNITTYPE: + factory = &UnitFactory; + break; + + case RTTI_BUILDING: + case RTTI_BUILDINGTYPE: + factory = &BuildingFactory; + break; + + case RTTI_INFANTRY: + case RTTI_INFANTRYTYPE: + factory = &InfantryFactory; + break; + + case RTTI_SPECIAL: + factory = &SpecialFactory; + break; + } + + /* + ** Check for legality of the production object type suggested. + */ + if (!factory) return(PROD_ILLEGAL); + + /* + ** If there is no factory to abandon, then return with a failure code. + */ + if (*factory == -1) return(PROD_CANT); + + /* + ** Fetch the factory pointer object. + */ + FactoryClass * fptr = Factories.Raw_Ptr(*factory); + if (!fptr) return(PROD_CANT); + + /* + ** Tell the sidebar that it needs to be redrawn because of this. + */ + if (PlayerPtr == this) { + Map.Abandon_Production(type, *factory); + + if (type == RTTI_BUILDINGTYPE || type == RTTI_BUILDING) { + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + Map.Set_Cursor_Shape(0); + } + } + + /* + ** Abandon production of the object. + */ + fptr->Abandon(); + delete fptr; + *factory = -1; + + return(PROD_OK); +} + + +/*********************************************************************************************** + * HouseClass::Special_Weapon_AI -- Fires special weapon. * + * * + * This routine will pick a good target to fire the special weapon specified. * + * * + * INPUT: id -- The special weapon id to fire. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 PWG : Created. * + *=============================================================================================*/ +void HouseClass::Special_Weapon_AI(SpecialWeaponType id) +{ + Validate(); + /* + ** Loop through all of the building objects on the map + ** and see which ones are available. + */ + BuildingClass * bestptr = NULL; + int best = -1; + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * b = Buildings.Ptr(index); + + /* + ** If the building is valid, not in limbo, not in the process of + ** being destroyed and not our ally, then we can consider it. + */ + if (b && !b->IsInLimbo && b->Strength && !Is_Ally(b)) { + if (b->Value() > best || best == -1) { + best = b->Value(); + bestptr = b; + } + } + } + + if (bestptr) { + CELL cell = Coord_Cell(bestptr->Center_Coord()); + Place_Special_Blast(id, cell); + } +} + + +/*********************************************************************************************** + * HouseClass::Place_Special_Blast -- Place a special blast effect at location specified. * + * * + * This routine will create a blast effect at the cell specified. This is the result of * + * the special weapons. * + * * + * INPUT: id -- The special weapon id number. * + * * + * cell -- The location where the special weapon attack is to occur. * + * * + * OUTPUT: Was the special weapon successfully fired at the location specified? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : commented. * + * 07/25/1995 JLB : Added scatter effect for nuclear bomb. * + * 07/28/1995 JLB : Revamped to use super weapon class controller. * + *=============================================================================================*/ +bool HouseClass::Place_Special_Blast(SpecialWeaponType id, CELL cell) +{ + Validate(); + BuildingClass * launchsite = 0; + AnimClass * anim = 0; + int index; + switch (id) { + + case SPC_ION_CANNON: + if (IonCannon.Is_Ready()) { + anim = new AnimClass(ANIM_ION_CANNON, Cell_Coord(cell)); + if (anim) anim->Owner = Class->House; + if (this == PlayerPtr) { + Map.IsTargettingMode = false; + } + IonCannon.Discharged(PlayerPtr == this); + IsRecalcNeeded = true; + } + break; + + case SPC_NUCLEAR_BOMB: + if (NukeStrike.Is_Ready()) { + + +#ifdef NEVER + /* + ** Scatter the nuclear bomb impact point into an adjacent cell. + */ + for (;;) { + CELL newcell = Adjacent_Cell(cell, Random_Pick(FACING_N, FACING_COUNT)); + if (Map.In_Radar(newcell)) { + cell = newcell; + break; + } + } +#endif + + /* + ** Search for a suitable launch site for this missile. + */ + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass * b = Buildings.Ptr(index); + if (b && !b->IsInLimbo && b->House == this && *b == STRUCT_TEMPLE) { + launchsite = b; + break; + } + } + + /* + ** If a launch site was found, then proceed with the normal launch + ** sequence. + */ + if (launchsite) { + launchsite->Assign_Mission(MISSION_MISSILE); + launchsite->Commence(); + NukeDest = cell; + NukePieces = 0; + + } else { + + /* + ** Only in the multiplayer version can the nuclear bomb be + ** sent from some off screen source. + */ + if (GameToPlay == GAME_NORMAL) return(false); + + /* + ** Since no launch site was found, just bring the missile in + ** directly from the North map edge. + */ + BulletClass *bullet = new BulletClass(BULLET_NUKE_DOWN); + if (bullet) { + COORDINATE start = Cell_Coord(XY_Cell(Cell_X(cell), 0)); + bullet->Assign_Target(::As_Target(cell)); + bullet->Payback = NULL; + bullet->Strength = 1; + if (!bullet->Unlimbo(start, DIR_S)) { + delete bullet; + } else { + bullet->PrimaryFacing.Set_Current(DIR_S); + } + Speak(VOX_INCOMING_NUKE); + Sound_Effect(VOC_NUKE_FIRE, start); + } + } + if (this == PlayerPtr) { + Map.IsTargettingMode = false; + } + NukeStrike.Discharged(this == PlayerPtr); + IsRecalcNeeded = true; + } + break; + + case SPC_AIR_STRIKE: + if (AirStrike.Is_Ready()) { + int strike = 1; + if (GameToPlay == GAME_NORMAL) { + strike = Bound(BuildLevel/3, 1, 3); + } else { + strike = Bound(MPlayerUnitCount/5, 1, 3); + } + Create_Air_Reinforcement(this, AIRCRAFT_A10, strike, MISSION_HUNT, ::As_Target(cell), TARGET_NONE); + if (this == PlayerPtr) { + Map.IsTargettingMode = false; + } + AirStrike.Discharged(this == PlayerPtr); + IsRecalcNeeded = true; + } + break; + } + return(true); +} + + +/*********************************************************************************************** + * HouseClass::Place_Object -- Places the object (building) at location specified. * + * * + * This routine is called when a building has been produced and now must be placed on * + * the map. When the player clicks on the map, this routine is ultimately called when the * + * event passes through the event queue system. * + * * + * INPUT: type -- The object type to place. The actual object is lifted from the sidebar. * + * * + * * + * cell -- The location to place the object on the map. * + * * + * OUTPUT: Was the placement successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Place_Object(RTTIType type, CELL cell) +{ + Validate(); + TechnoClass * tech = 0; + FactoryClass * factory = 0; + + switch (type) { + case RTTI_AIRCRAFT: + case RTTI_AIRCRAFTTYPE: + if (AircraftFactory != -1) { + factory = Factories.Raw_Ptr(AircraftFactory); + } + break; + + case RTTI_INFANTRY: + case RTTI_INFANTRYTYPE: + if (InfantryFactory != -1) { + factory = Factories.Raw_Ptr(InfantryFactory); + } + break; + + case RTTI_UNIT: + case RTTI_UNITTYPE: + if (UnitFactory != -1) { + factory = Factories.Raw_Ptr(UnitFactory); + } + break; + + case RTTI_BUILDING: + case RTTI_BUILDINGTYPE: + if (BuildingFactory != -1) { + factory = Factories.Raw_Ptr(BuildingFactory); + } + break; + } + + /* + ** Only if there is a factory active for this type, can it be "placed". + ** In the case of a missing factory, then this request is completely bogus -- + ** ignore it. This might occur if, between two events to exit the same + ** object, the mouse was clicked on the sidebar to start building again. + ** The second placement event should NOT try to place the object that is + ** just starting construction. + */ + if (factory && factory->Has_Completed()) { + tech = factory->Get_Object(); + + if (cell == -1) { + TechnoClass * pending = factory->Get_Object(); + if (pending) { + TechnoClass * builder = pending->Who_Can_Build_Me(false, false); + + if (builder && builder->Exit_Object(pending)) { + + /* + ** Since the object has left the factory under its own power, delete + ** the production manager tied to this slot in the sidebar. Its job + ** has been completed. + */ + factory->Completed(); + Abandon_Production(type); + } else { + + /* + ** The object could not leave under it's own power. Just wait + ** until the player tries to place the object again. + */ + return(false); + } + } + + } else { + + if (tech) { + TechnoClass * builder = tech->Who_Can_Build_Me(false, false); + if (builder) { + + /* + ** Ensures that the proximity check is performed even when the building is + ** placed by way of a remote event. + */ + if (tech->What_Am_I() != RTTI_BUILDING || ((BuildingClass *)tech)->Passes_Proximity_Check(cell)) { + builder->Transmit_Message(RADIO_HELLO, tech); + if (tech->Unlimbo(Cell_Coord(cell))) { + factory->Completed(); + Abandon_Production(type); + + if (PlayerPtr == this) { + Sound_Effect(VOC_SLAM); + Map.Set_Cursor_Shape(0); + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + } + return(true); + } else { + if (this == PlayerPtr) { + Speak(VOX_DEPLOY); + } + } + builder->Transmit_Message(RADIO_OVER_OUT); + } + } + return(false); + + } else { + + // Play a bad sound here? + return(false); + } + } + } + + return(true); +} + + +/*********************************************************************************************** + * HouseClass::Manual_Place -- Inform display system of building placement mode. * + * * + * This routine will inform the display system that building placement mode has begun. * + * The cursor will be created that matches the layout of the building shape. * + * * + * INPUT: builder -- The factory that is building this object. * + * * + * object -- The building that is going to be placed down on the map. * + * * + * OUTPUT: Was the building placement mode successfully initiated? * + * * + * WARNINGS: This merely adjusts the cursor shape. Nothing that affects networked games * + * is affected. * + * * + * HISTORY: * + * 05/04/1995 JLB : Created. * + * 05/30/1995 JLB : Uses the Bib_And_Offset() function to determine bib size. * + *=============================================================================================*/ +bool HouseClass::Manual_Place(BuildingClass * builder, BuildingClass * object) +{ + Validate(); + if (this == PlayerPtr && !Map.PendingObject && builder && object) { + + /* + ** Ensures that object selection doesn't remain when + ** building placement takes place. + */ + Unselect_All(); + + Map.Repair_Mode_Control(0); + Map.Sell_Mode_Control(0); + + Map.PendingObject = object->Class; + Map.PendingObjectPtr = object; + Map.PendingHouse = Class->House; + + Map.Set_Cursor_Shape(object->Occupy_List(true)); + Map.Set_Cursor_Pos(Coord_Cell(builder->Coord)); + builder->Mark(MARK_CHANGE); + return(true); + } + return(false); +} + + +#ifdef OBSOLETE +/*********************************************************************************************** + * HouseClass::Init_Ion_Cannon -- Initialize the ion cannon countdown. * + * * + * This routine will initiate the ion cannon charging countdown. It will add the ion * + * cannon to the sidebar if it isn't there and it is specified to be added. * + * * + * INPUT: first_time -- Set to true if the ion cannon must be added to the sidebar. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : commented * + *=============================================================================================*/ +void HouseClass::Init_Ion_Cannon(SpecialControlType control) +{ + Validate(); + switch (control) { + case CONTROL_RESET: + if (IonCannonPresent) { + IonOldStage = -1; + IonControl.Set(ION_CANNON_GONE_TIME); + if (PlayerPtr == this) { + Map.Add(RTTI_SPECIAL, SPC_ION_CANNON); + if (!ScenarioInit) { + Speak(VOX_ION_CHARGING); + } + } + } + break; + + /* + ** Adds the special no-prerequisite ion cannon option. + */ + case CONTROL_ONE_TIME: + if (!IonCannonPresent) { + Init_Ion_Cannon(CONTROL_ADD); + IonOneTimeFlag = true; + } + break; + + /* + ** Adds the normal legitimate ion cannon option. If there was + ** already a one-time ion cannon available, the charging state + ** is NOT interrupted. + */ + case CONTROL_ADD: + IonOneTimeFlag = false; + if (!IconCannonPresent) { + IonCannonPresent = true; + IonReady = false; + Init_Ion_Cannon(CONTROL_RESET); + } + break; + + case CONTROL_REMOVE: + break; + } + + + if (!(first_time && IonCannonPresent)) { + + if (IonCannonPresent && IonOneTimeFlag) { + IonOneTimeFlag = false; + if (this == PlayerPtr) Map.Recalc(); + return; + } + + if (first_time && this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_ION_CANNON); + } + + if (!ScenarioInit) { + if (this == PlayerPtr) { + Speak(VOX_ION_CHARGING); + } + } + + IonControl.Set(ION_CANNON_GONE_TIME); + IonCannonPresent = true; + IonReady = false; + IonOldStage = -1; + IonOneTimeFlag = one_time_effect; + } else { + if (first_time && IonCannonPresent && !one_time_effect && IonOneTimeFlag) { + IonOneTimeFlag = false; + } + } +} +#ifdef NEVER +void HouseClass::Init_Ion_Cannon(bool first_time, bool one_time_effect) +{ + Validate(); + if (!(first_time && IonCannonPresent)) { + + if (IonCannonPresent && IonOneTimeFlag) { + IonOneTimeFlag = false; + if (this == PlayerPtr) Map.Recalc(); + return; + } + + if (first_time && this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_ION_CANNON); + } + + if (!ScenarioInit) { + if (this == PlayerPtr) { + Speak(VOX_ION_CHARGING); + } + } + + IonControl.Set(ION_CANNON_GONE_TIME); + IonCannonPresent = true; + IonReady = false; + IonOldStage = -1; + IonOneTimeFlag = one_time_effect; + } else { + if (first_time && IonCannonPresent && !one_time_effect && IonOneTimeFlag) { + IonOneTimeFlag = false; + } + } +} +#endif + + +/*********************************************************************************************** + * HouseClass::Remove_Ion_Cannon -- Disables the ion cannon. * + * * + * This routine will disable the ion cannon. It is called when the ion cannon cannot * + * establish a command link to the ground (usually when there is insufficient power). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : commented * + *=============================================================================================*/ +void HouseClass::Remove_Ion_Cannon(void) +{ + Validate(); + if (IonCannonPresent) { + IonCannonPresent = false; + IonOneTimeFlag = false; + IonReady = false; + IonControl.Clear(); + IonOldStage = -1; + } +} +#endif + + +/*************************************************************************** + * HouseClass::Clobber_All -- removes house & all its objects * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/16/1995 BRR : Created. * + * 06/09/1995 JLB : Handles aircraft. * + *=========================================================================*/ +void HouseClass::Clobber_All(void) +{ + Validate(); + int i; + + for (i = 0; i < ::Aircraft.Count(); i++) { + if (::Aircraft.Ptr(i)->House == this) { + delete ::Aircraft.Ptr(i); + i--; + } + } + for (i = 0; i < ::Units.Count(); i++) { + if (::Units.Ptr(i)->House == this) { + delete ::Units.Ptr(i); + i--; + } + } + for (i = 0; i < Infantry.Count(); i++) { + if (Infantry.Ptr(i)->House == this) { + delete Infantry.Ptr(i); + i--; + } + } + for (i = 0; i < Buildings.Count(); i++) { + if (Buildings.Ptr(i)->House == this) { + delete Buildings.Ptr(i); + i--; + } + } + for (i = 0; i < TeamTypes.Count(); i++) { + if (TeamTypes.Ptr(i)->House == Class->House) { + delete TeamTypes.Ptr(i); + i--; + } + } + for (i = 0; i < Triggers.Count(); i++) { + if (Triggers.Ptr(i)->House == Class->House) { + delete Triggers.Ptr(i); + i--; + } + } + + delete this; +} + + +#ifdef NEVER +/*********************************************************************************************** + * HouseClass::Init_Nuke_Bomb -- Adds (if necessary) the atom bomb to the sidebar. * + * * + * Use this routine whenever a piece of atom bomb has been discovered (also at scenario * + * start). It will add the nuclear bomb button to the sidebar if necessary. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : commented * + *=============================================================================================*/ +void HouseClass::Init_Nuke_Bomb(bool first_time, bool one_time_effect) +{ + Validate(); + if (!first_time || !NukePresent) { + + if (NukePresent && NukeOneTimeFlag) { + NukeOneTimeFlag = false; + if (this == PlayerPtr) Map.Recalc(); + return; + } + + if (first_time && this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB); + } + + NukeControl.Set(NUKE_GONE_TIME); + NukePresent = true; + NukeReady = false; + NukeOldStage = -1; + NukeOneTimeFlag = one_time_effect; + + } else { + if (!one_time_effect && NukeOneTimeFlag) { + NukeOneTimeFlag = false; + } + } +} + + +/*********************************************************************************************** + * HouseClass::Remove_Nuke_Bomb -- Removes the nuclear bomb from the sidebar. * + * * + * This routine will remove the nuclear bomb from the sidebar. It should be called when * + * the nuclear strike has been launched. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : commented * + * 07/25/1995 JLB : Handles recharge reset logic. * + *=============================================================================================*/ +void HouseClass::Remove_Nuke_Bomb(void) +{ + Validate(); + if (NukePresent && !NukeOneTimeFlag) { + NukePresent = false; + NukeControl.Clear(); + NukeOldStage = -1; + NukeReady = false; + } +} + + +/*********************************************************************************************** + * HouseClass::Init_Air_Strike -- Add (or reset) the air strike sidebar button. * + * * + * This routine will activate (add if so indicated) the air strike button to the sidebar. * + * Call this routine when events indicate that a special air strike is available. * + * * + * INPUT: first_time -- If the air strike button is to be added, then this will be true. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : commented * + *=============================================================================================*/ +void HouseClass::Init_Air_Strike(bool first_time, bool one_time_effect) +{ + Validate(); + if (!(first_time && AirPresent)) { + + if (AirPresent && AirOneTimeFlag) { + AirOneTimeFlag = false; + AirPresent = false; + Map.Recalc(); + return; + } + + if (first_time) { + if (PlayerPtr == this) { + Map.Add(RTTI_SPECIAL, SPC_AIR_STRIKE); + } + AirControl.Set(0); + } else { + AirControl.Set(AIR_CANNON_GONE_TIME); + } + + AirReady = first_time; + AirPresent = true; + AirOldStage = -1; + AirOneTimeFlag = one_time_effect; + + if (AirReady && !IsHuman) { + Special_Weapon_AI(SPC_AIR_STRIKE); + } + } else { + if (first_time && AirPresent && !one_time_effect && AirOneTimeFlag) { + AirOneTimeFlag = false; + } + } +} + + +/*********************************************************************************************** + * HouseClass::Remove_Air_Strike -- Removes the air strike button from the sidebar. * + * * + * This routine will remove the air strike button from the sidebar. Call this routine when * + * the air strike has been launched. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : commented * + *=============================================================================================*/ +void HouseClass::Remove_Air_Strike(void) +{ + Validate(); + AirPresent = false; + AirReady = false; + AirControl.Clear(); + AirOldStage = -1; +} + + +/*********************************************************************************************** + * HouseClass::Make_Air_Strike_Available -- Make the airstrike available. * + * * + * This routine will make the airstrike available. Typically, this results in a new icon * + * added to the sidebar. * + * * + * INPUT: present -- The the airstrike being added? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Make_Air_Strike_Available(bool present, bool one_time_effect) +{ + Validate(); + Init_Air_Strike(true, one_time_effect); + AirPresent = present; +} +#endif + + +/*********************************************************************************************** + * HouseClass::Add_Nuke_Piece -- Add a nuclear piece to the collection. * + * * + * This routine will a the specified nuclear piece to the house collection of parts. When * + * all the pieces have been added, a nuclear strike ability is made available. * + * * + * INPUT: piece -- The nuclear piece to add. If equal to "-1", then the next possible piece * + * is added. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Add_Nuke_Piece(int piece) +{ + Validate(); + if (piece == -1) { + piece = 1; + if (!(NukePieces & 0x01)) { + piece = 1; + } + if (!(NukePieces & 0x02)) { + piece = 2; + } + if (!(NukePieces & 0x04)) { + piece = 3; + } + } + NukePieces |= 1 << (piece - 1); +// Init_Nuke_Bomb(false); +} + + +/*********************************************************************************************** + * HouseClass::Detach -- Removes specified object from house tracking systems. * + * * + * This routine is called when an object is to be removed from the game system. If the * + * specified object is part of the house tracking system, then it will be removed. * + * * + * INPUT: target -- The target value of the object that is to be removed from the game. * + * * + * all -- Is the target going away for good as opposed to just cloaking/hiding? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : commented * + *=============================================================================================*/ +void HouseClass::Detach(TARGET, bool ) +{ + Validate(); +// if (LaunchSite == target) { +// LaunchSite = TARGET_NONE; +// } +} + + +/*********************************************************************************************** + * HouseClass::Does_Enemy_Building_Exist -- Checks for enemy building of specified type. * + * * + * This routine will examine the enemy houses and if there is a building owned by one * + * of those house, true will be returned. * + * * + * INPUT: btype -- The building type to check for. * + * * + * OUTPUT: Does a building of the specified type exist for one of the enemy houses? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Does_Enemy_Building_Exist(StructType btype) const +{ + Validate(); + int bflag = 1L << btype; + for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) { + HouseClass * house = HouseClass::As_Pointer(index); + + if (house && !Is_Ally(house) && (house->BScan & bflag) != 0) { + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Suggest_New_Object -- Determine what would the next buildable object be. * + * * + * This routine will examine the house status and return with a techno type pointer to * + * the object type that it thinks should be created. The type is restricted to match the * + * type specified. Typical use of this routine is by computer controlled factories. * + * * + * INPUT: objecttype -- The type of object to restrict the scan for. * + * * + * OUTPUT: Returns with a pointer to a techno type for the object type that should be * + * created. If no object should be created, then NULL is returned. * + * * + * WARNINGS: This is a time consuming routine. Only call when necessary. * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +TechnoTypeClass const * HouseClass::Suggest_New_Object(RTTIType objecttype) const +{ + Validate(); + TechnoTypeClass const * techno = NULL; + + switch (objecttype) { + + /* + ** Unit construction is based on the rule that up to twice the number required + ** to fill all teams will be created. + */ + case RTTI_UNIT: + case RTTI_UNITTYPE: + if (CurUnits < MaxUnit) { + + /* + ** A computer controlled house will try to build a replacement + ** harvester if possible. Never replace harvesters if the game + ** is in easy mode. + */ + if (!Special.IsEasy && !IsHuman && (ActiveBScan & STRUCTF_REFINERY) && !(UScan & UNITF_HARVESTER)) { + techno = &UnitTypeClass::As_Reference(UNIT_HARVESTER); + if (techno->Scenario <= BuildLevel) break; + techno = 0; + } + + int counter[UNIT_COUNT]; + if (GameToPlay == GAME_NORMAL) { + memset(counter, 0x00, sizeof(counter)); + } else { + for (UnitType index = UNIT_FIRST; index < UNIT_COUNT; index++) { + if (Can_Build(index, Class->House) && UnitTypeClass::As_Reference(index).Level <= BuildLevel) { + counter[index] = 16; + } else { + counter[index] = 0; + } + } + } + + /* + ** Build a list of the maximum of each type we wish to produce. This will be + ** twice the number required to fill all teams. + */ + for (int index = 0; index < Teams.Count(); index++) { + TeamClass * tptr = Teams.Ptr(index); + if (tptr) { + TeamTypeClass const * team = tptr->Class; + + if ((/*team->IsReinforcable || */!tptr->IsFullStrength) && team->House == Class->House) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + if (team->Class[subindex]->What_Am_I() == RTTI_UNITTYPE) { + counter[((UnitTypeClass const *)(team->Class[subindex]))->Type] = 1; +// counter[((UnitTypeClass const *)(team->Class[subindex]))->Type] += team->DesiredNum[subindex]*2; + } + } + } + } + } + + /* + ** Team types that are flagged as prebuilt, will always try to produce enough + ** to fill one team of this type regardless of whether there is a team active + ** of that type. + */ + for (index = 0; index < TeamTypes.Count(); index++) { + TeamTypeClass const * team = TeamTypes.Ptr(index); + if (team) { + if (team->House == Class->House && team->IsPrebuilt && (!team->IsAutocreate || IsAlerted)) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + if (team->Class[subindex]->What_Am_I() == RTTI_UNITTYPE) { + int subtype = ((UnitTypeClass const *)(team->Class[subindex]))->Type; + counter[subtype] = MAX(counter[subtype], team->DesiredNum[subindex]); + } + } + } + } + } + + /* + ** Reduce the theoretical maximum by the actual number of objects currently + ** in play. + */ + for (int uindex = 0; uindex < Units.Count(); uindex++) { + UnitClass * unit = Units.Ptr(uindex); + if (unit && !unit->Team && unit->House == this && (unit->Mission != MISSION_GUARD_AREA && unit->Mission != MISSION_HUNT && unit->Mission != MISSION_STICKY && unit->Mission != MISSION_SLEEP)) { + counter[unit->Class->Type]--; + } + } + + /* + ** Pick to build the most needed object but don't consider those object that + ** can't be built because of scenario restrictions or insufficient cash. + */ + int bestval = -1; + int bestcount = 0; + UnitType bestlist[UNIT_COUNT]; + for (UnitType utype = UNIT_FIRST; utype < UNIT_COUNT; utype++) { + if (counter[utype] > 0 && Can_Build(utype, Class->House) && UnitTypeClass::As_Reference(utype).Cost_Of() <= Available_Money()) { + if (bestval == -1 || bestval < counter[utype]) { + bestval = counter[utype]; + bestcount = 0; + } + bestlist[bestcount++] = utype; + } + } + + /* + ** The unit type to build is now known. Fetch a pointer to the techno type class. + */ + if (bestcount) { + techno = &UnitTypeClass::As_Reference(bestlist[Random_Pick(0, bestcount-1)]); + } + } + break; + + /* + ** Infantry construction is based on the rule that up to twice the number required + ** to fill all teams will be created. + */ + case RTTI_INFANTRY: + case RTTI_INFANTRYTYPE: + if (CurUnits < MaxUnit) { + int counter[INFANTRY_COUNT]; + if (GameToPlay == GAME_NORMAL) { + memset(counter, 0x00, sizeof(counter)); + } else { + for (InfantryType index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) { + if (Can_Build(index, Class->House) && InfantryTypeClass::As_Reference(index).Level <= BuildLevel) { + counter[index] = 16; + } else { + counter[index] = 0; + } + } + } + + /* + ** Build a list of the maximum of each type we wish to produce. This will be + ** twice the number required to fill all teams. + */ + for (int index = 0; index < Teams.Count(); index++) { + TeamClass * tptr = Teams.Ptr(index); + if (tptr) { + TeamTypeClass const * team = tptr->Class; + + if ((team->IsReinforcable || !tptr->IsFullStrength) && team->House == Class->House) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + if (team->Class[subindex]->What_Am_I() == RTTI_INFANTRYTYPE) { + counter[((InfantryTypeClass const *)(team->Class[subindex]))->Type] += team->DesiredNum[subindex]+1; + } + } + } + } + } + + /* + ** Team types that are flagged as prebuilt, will always try to produce enough + ** to fill one team of this type regardless of whether there is a team active + ** of that type. + */ + for (index = 0; index < TeamTypes.Count(); index++) { + TeamTypeClass const * team = TeamTypes.Ptr(index); + if (team) { + if (team->House == Class->House && team->IsPrebuilt && (!team->IsAutocreate || IsAlerted)) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + if (team->Class[subindex]->What_Am_I() == RTTI_INFANTRYTYPE) { + int subtype = ((InfantryTypeClass const *)(team->Class[subindex]))->Type; +// counter[subtype] = 1; + counter[subtype] = MAX(counter[subtype], team->DesiredNum[subindex]); + counter[subtype] = MIN(counter[subtype], 5); + } + } + } + } + } + + /* + ** Reduce the theoretical maximum by the actual number of objects currently + ** in play. + */ + for (int uindex = 0; uindex < Infantry.Count(); uindex++) { + InfantryClass * infantry = Infantry.Ptr(uindex); + if (infantry && !infantry->Team && infantry->House == this && (infantry->Mission != MISSION_GUARD_AREA && infantry->Mission != MISSION_HUNT && infantry->Mission != MISSION_STICKY && infantry->Mission != MISSION_SLEEP)) { + counter[infantry->Class->Type]--; + } + } + + /* + ** Pick to build the most needed object but don't consider those object that + ** can't be built because of scenario restrictions or insufficient cash. + */ + int bestval = -1; + int bestcount = 0; + InfantryType bestlist[INFANTRY_COUNT]; + for (InfantryType utype = INFANTRY_FIRST; utype < INFANTRY_COUNT; utype++) { + if (counter[utype] > 0 && Can_Build(utype, Class->House) && InfantryTypeClass::As_Reference(utype).Cost_Of() <= Available_Money()) { + if (bestval == -1 || bestval < counter[utype]) { + bestval = counter[utype]; + bestcount = 0; + } + bestlist[bestcount++] = utype; + } + } + + /* + ** The infantry type to build is now known. Fetch a pointer to the techno type class. + */ + if (bestcount) { + techno = &InfantryTypeClass::As_Reference(bestlist[Random_Pick(0, bestcount-1)]); + } + } + break; + + /* + ** Building construction is based upon the preconstruction list. + */ + case RTTI_BUILDING: + case RTTI_BUILDINGTYPE: + if (CurBuildings < MaxBuilding) { + BaseNodeClass * node = Base.Next_Buildable(); + if (node) { + techno = &BuildingTypeClass::As_Reference(node->Type); + } + } + break; + } + return(techno); +} + + +/*********************************************************************************************** + * HouseClass::Flag_Remove -- Removes the flag from the specified target. * + * * + * This routine will remove the flag attached to the specified target object or cell. * + * Call this routine before placing the object down. This is called inherently by the * + * the Flag_Attach() functions. * + * * + * INPUT: target -- The target that the flag was attached to but will be removed from. * + * * + * set_home -- if true, clears the flag's waypoint designation * + * * + * OUTPUT: Was the flag successfully removed from the specified target? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_Remove(TARGET target, bool set_home) +{ + Validate(); + int rc; + + if (Target_Legal(target)) { + + /* + ** Remove the flag from a unit + */ + UnitClass * object = As_Unit(target); + if (object) { + rc = object->Flag_Remove(); + if (rc && FlagLocation == target) { + FlagLocation = TARGET_NONE; + } + + } else { + + /* + ** Remove the flag from a cell + */ + CELL cell = As_Cell(target); + if (Map.In_Radar(cell)) { + rc = Map[cell].Flag_Remove(); + if (rc && FlagLocation == target) { + FlagLocation = TARGET_NONE; + } + } + } + + /* + ** Handle the flag home cell: + ** If 'set_home' is set, clear the home value & the cell's overlay + */ + if (set_home) { + if (FlagHome) { + Map[FlagHome].Overlay = OVERLAY_NONE; + Map.Flag_Cell(FlagHome); + FlagHome = 0; + } + } + + return(rc); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Flag_Attach -- Attach flag to specified cell (or thereabouts). * + * * + * This routine will attach the house flag to the location specified. If the location * + * cannot contain the flag, then a suitable nearby location will be selected. * + * * + * INPUT: cell -- The desired cell location to place the flag. * + * * + * set_home -- if true, resets the flag's waypoint designation * + * * + * OUTPUT: Was the flag successfully placed? * + * * + * WARNINGS: The cell picked for the flag might very likely not be the cell requested. * + * Check the FlagLocation value to determine the final cell resting spot. * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_Attach(CELL cell, bool set_home) +{ + Validate(); + bool rc; + bool clockwise; + FacingType rot; + FacingType fcounter; + + /* + ** Randomly decide if we're going to search cells clockwise or counter- + ** clockwise + */ + clockwise = IRandom(0,1); + + /* + ** Only continue if this cell is a legal placement cell. + */ + if (Map.In_Radar(cell)) { + + /* + ** If the flag already exists, then it must be removed from the object + ** it is attached to. + */ + Flag_Remove(FlagLocation, set_home); + + /* + ** Attach the flag to the cell specified. If it can't be placed, then pick + ** a nearby cell where it can be placed. + */ + CELL newcell = cell; + rc = Map[newcell].Flag_Place(Class->House); + if (!rc) { + + /* + ** Loop for increasing distance from the desired cell. + ** For each distance, randomly pick a starting direction. Between + ** this and the clockwise/counterclockwise random value, the flag + ** should appear to be placed fairly randomly. + */ + for (int dist = 1; dist < 32; dist++) { + + /* + ** Clockwise search. + */ + if (clockwise) { + rot = (FacingType)IRandom(FACING_N, FACING_NW); + for (fcounter = FACING_N; fcounter <= FACING_NW; fcounter++) { + newcell = Coord_Cell(Coord_Move(Cell_Coord(cell), Facing_Dir(rot), dist*256)); + if (Map.In_Radar(newcell) && Map[newcell].Flag_Place(Class->House)) { + dist = 32; + rc = true; + break; + } + rot++; + if (rot > FACING_NW) rot = FACING_N; + } + } else { + + /* + ** Counter-clockwise search + */ + rot = (FacingType)IRandom (FACING_N, FACING_NW); + for (fcounter = FACING_NW; fcounter >= FACING_N; fcounter--) { + newcell = Coord_Cell(Coord_Move(Cell_Coord(cell), Facing_Dir(rot), dist*256)); + if (Map.In_Radar(newcell) && Map[newcell].Flag_Place(Class->House)) { + dist = 32; + rc = true; + break; + } + rot--; + if (rot < FACING_N) + rot = FACING_NW; + } + } + } + } + + /* + ** If we've found a spot for the flag, place the flag at the new cell. + ** if 'set_home' is set, OR this house has no current flag home cell, + ** mark that cell as this house's flag home cell. + */ + if (rc) { + FlagLocation = As_Target(newcell); + + if (set_home || FlagHome == 0) { + Map[newcell].Overlay = OVERLAY_FLAG_SPOT; + FlagHome = newcell; + } + } + + return(rc); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Flag_Attach -- Attaches the house flag the specified unit. * + * * + * This routine will attach the house flag to the specified unit. This routine is called * + * when a unit drives over a cell containing a flag. * + * * + * INPUT: object -- Pointer to the object that the house flag is to be attached to. * + * * + * set_home -- if true, clears the flag's waypoint designation * + * * + * OUTPUT: Was the flag attached successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_Attach(UnitClass * object, bool set_home) +{ + Validate(); + if (object && !object->IsInLimbo) { + Flag_Remove(FlagLocation, set_home); + + /* + ** Attach the flag to the object. + */ + object->Flag_Attach(Class->House); + FlagLocation = object->As_Target(); + return(true); + } + return(false); +} + + +/*************************************************************************** + * HouseClass::MPlayer_Defeated -- multiplayer; house is defeated * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/25/1995 BRR : Created. * + *=========================================================================*/ +void HouseClass::MPlayer_Defeated(void) +{ + Validate(); + char txt[80]; + int i,j,k; + unsigned char id; + HousesType house; + HouseClass *hptr; + HouseClass *hptr2; + int num_alive; + int num_humans; + int all_allies; + int max_index; + int max_count; + int count; + int score_index[MAX_PLAYERS]; // array of each multi-player's index into + // the score array + + /*------------------------------------------------------------------------ + Set the defeat flag for this house + ------------------------------------------------------------------------*/ + IsDefeated = true; + + /*------------------------------------------------------------------------ + Remove this house's flag & flag home cell + ------------------------------------------------------------------------*/ + if (Special.IsCaptureTheFlag) { + if (FlagLocation) { + Flag_Remove(FlagLocation,true); + } else { + if (FlagHome) { + Flag_Remove(FlagHome,true); + } + } + } + + /*------------------------------------------------------------------------ + If this is me: + - Set MPlayerObiWan, so I can only send messages to all players, and + not just one (so I can't be obnoxiously omnipotent) + - Reveal the map + - Add my defeat message + ------------------------------------------------------------------------*/ + if (PlayerPtr == this) { + MPlayerObiWan = 1; + Debug_Unshroud = true; + HiddenPage.Clear(); + Map.Flag_To_Redraw(true); + + /*..................................................................... + Pop up a message showing that I was defeated + .....................................................................*/ + sprintf(txt,Text_String(TXT_PLAYER_DEFEATED), MPlayerName); + Messages.Add_Message(txt, MPlayerTColors[MPlayerColorIdx], TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 600, 0, 0); + Map.Flag_To_Redraw(false); + + } else { + + /*------------------------------------------------------------------------ + If it wasn't me, find out who was defeated + ------------------------------------------------------------------------*/ + if (IsHuman) { + sprintf(txt, Text_String(TXT_PLAYER_DEFEATED), Text_String(TXT_UNKNOWN)); + id = 0; + for (i = 0; i < MPlayerCount; i++) { + house = MPlayerHouses[i]; + if (HouseClass::As_Pointer(house) == this) { + sprintf (txt,Text_String(TXT_PLAYER_DEFEATED), MPlayerNames[i]); + id = MPlayerID[i]; + } + } + + Messages.Add_Message(txt, MPlayerTColors[MPlayerID_To_ColorIndex(id)], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, 0, 0); + Map.Flag_To_Redraw(false); + } + } + + /*------------------------------------------------------------------------ + Find out how many players are left alive. + ------------------------------------------------------------------------*/ + num_alive = 0; + num_humans = 0; + for (i = 0; i < MPlayerMax; i++) { + hptr = HouseClass::As_Pointer((HousesType)(HOUSE_MULTI1 + i)); + if (hptr && hptr->IsDefeated==0) { + if (hptr->IsHuman) + num_humans++; + num_alive++; + } + } + + /*------------------------------------------------------------------------ + If all the houses left alive are allied with each other, then in reality + there's only one player left: + ------------------------------------------------------------------------*/ + all_allies = 1; + for (i = 0; i < MPlayerMax; i++) { + /*..................................................................... + Get a pointer to this house + .....................................................................*/ + hptr = HouseClass::As_Pointer((HousesType)(HOUSE_MULTI1 + i)); + if (!hptr || hptr->IsDefeated) + continue; + + /*..................................................................... + Loop through all houses; if there's one left alive that this house + isn't allied with, then all_allies will be false + .....................................................................*/ + for (j = 0; j < MPlayerMax; j++) { + hptr2 = HouseClass::As_Pointer((HousesType)(HOUSE_MULTI1 + j)); + if (!hptr2) + continue; + if (!hptr2->IsDefeated && !hptr->Is_Ally(hptr2)) { + all_allies = 0; + break; + } + } + if (!all_allies) + break; + } + /*........................................................................ + If all houses left are allies, set 'num_alive' to 1; game over. + ........................................................................*/ + if (all_allies) + num_alive = 1; + + /*------------------------------------------------------------------------ + If there's only one human player left or no humans left, the game is over: + - Determine whether this player wins or loses, based on the state of the + MPlayerObiWan flag + - Find all players' indices in the MPlayerScore array + - Tally up scores for this game + ------------------------------------------------------------------------*/ + if (num_alive == 1 || num_humans == 0) { + if (PlayerPtr->IsDefeated) { + PlayerLoses = true; + } else { + PlayerWins = true; + } + + /*--------------------------------------------------------------------- + Find each player's score index + ---------------------------------------------------------------------*/ + for (i = 0; i < MPlayerCount; i++) { + score_index[i] = -1; + + /*.................................................................. + Search for this player's name in the MPlayerScore array + ..................................................................*/ + for (j = 0; j < MPlayerNumScores; j++) { + if (!stricmp(MPlayerNames[i],MPlayerScore[j].Name)) { + score_index[i] = j; + break; + } + } + + /*.................................................................. + If the index is still -1, the name wasn't found; add a new entry. + ..................................................................*/ + if (score_index[i] == -1) { + if (MPlayerNumScores < MAX_MULTI_NAMES) { + score_index[i] = MPlayerNumScores; + MPlayerNumScores++; + } else { + + /*............................................................... + For each player in the scores array, count the # of '-1' entries + from this game backwards; the one with the most is the one that + hasn't played the longest; replace him with this new guy. + ...............................................................*/ + max_index = 0; + max_count = 0; + for (j = 0; j < MPlayerNumScores; j++) { + count = 0; + for (k = MPlayerNumScores - 1; k >= 0; k--) { + if (MPlayerScore[j].Kills[k]==-1) { + count++; + } else { + break; + } + } + if (count > max_count) { + max_count = count; + max_index = j; + } + } + score_index[i] = max_index; + } + + /*............................................................... + Initialize this score entry + ...............................................................*/ + MPlayerScore[score_index[i]].Wins = 0; + strcpy (MPlayerScore[score_index[i]].Name,MPlayerNames[i]); + for (j = 0; j < MAX_MULTI_GAMES; j++) + MPlayerScore[score_index[i]].Kills[j] = -1; + } + + /*.................................................................. + Init this player's Kills to 0 (-1 means he didn't play this round; + 0 means he played but got no kills). + ..................................................................*/ + MPlayerScore[score_index[i]].Kills[MPlayerCurGame] = 0; + + /*.................................................................. + Init this player's color to his last-used color index + ..................................................................*/ + MPlayerScore[score_index[i]].Color = MPlayerID_To_ColorIndex(MPlayerID[i]); + } + +#if 0 // (This is the old method of tallying scores: + /*--------------------------------------------------------------------- + Tally up the scores for this game: + - For each house: + - If this house is human & wasn't defeated, its the winner + - If this house was defeated, find out who did it & increment their + Kills value. + ---------------------------------------------------------------------*/ + for (house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + MPlayerMax); house++) { + hptr = HouseClass::As_Pointer(house); + if (!hptr) continue; + + if (!hptr->IsDefeated) { + + /*............................................................... + If this is the winning house, find which player it was & increment + their 'Wins' value + ...............................................................*/ + if (hptr->IsHuman) { + for (i = 0; i < MPlayerCount; i++) { + if (house == MPlayerHouses[i]) { + MPlayerScore[score_index[i]].Wins++; + MPlayerWinner = score_index[i]; + } + } + } + } else { + + /*.................................................................. + This house was defeated; find which player who defeated him & increment + his 'Kills' value for this game + ..................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + if (hptr->WhoLastHurtMe == MPlayerHouses[i]) { + MPlayerScore[score_index[i]].Kills[MPlayerCurGame]++; + } + } + } + } + +#else // This is the new method: + + /*--------------------------------------------------------------------- + Tally up the scores for this game: + - For each player: + - If this player is undefeated this round, he's the winner + - Each player's Kills value is the sum of the unit's they killed + ---------------------------------------------------------------------*/ + for (i = 0; i < MPlayerCount; i++) { + hptr = HouseClass::As_Pointer(MPlayerHouses[i]); + + /*.................................................................. + If this house was undefeated, it must have been the winner. (If + no human houses are undefeated, the computer won.) + ..................................................................*/ + if (!hptr->IsDefeated) { + MPlayerScore[score_index[i]].Wins++; + MPlayerWinner = score_index[i]; + } + + /*.................................................................. + Tally up all kills for this player + ..................................................................*/ + for (house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + + MPlayerScore[score_index[i]].Kills[MPlayerCurGame] += + hptr->UnitsKilled[house]; + + MPlayerScore[score_index[i]].Kills[MPlayerCurGame] += + hptr->BuildingsKilled[house]; + } + } +#endif + + /*--------------------------------------------------------------------- + Destroy all the IPX connections, since we have to go through the rest + of the Main_Loop() before we detect that the game is over, and we'll + end up waiting for frame sync packets from the other machines. + ---------------------------------------------------------------------*/ + if (GameToPlay==GAME_IPX || GameToPlay == GAME_INTERNET) { + i = 0; + while (Ipx.Num_Connections() && (i++ < 1000) ) { + id = Ipx.Connection_ID(0); + Ipx.Delete_Connection(id); + } + MPlayerCount = 0; + } + } + + /*------------------------------------------------------------------------ + Be sure our messages get displayed, even if we're about to exit. + ------------------------------------------------------------------------*/ + Map.Render(); +} + + +/*************************************************************************** + * HouseClass::Blowup_All -- blows up everything * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/16/1995 BRR : Created. * + * 06/09/1995 JLB : Handles aircraft. * + *=========================================================================*/ +void HouseClass::Blowup_All(void) +{ + Validate(); + int i; + int damage; + UnitClass *uptr; + InfantryClass *iptr; + BuildingClass *bptr; + int count; + WarheadType warhead; + + /* + ** Find everything owned by this house & blast it with a huge amount of damage + ** at zero range. Do units before infantry, so the units' drivers are killed + ** too. Using Explosion_Damage is like dropping a big bomb right on the + ** object; it will also damage anything around it. + */ + for (i = 0; i < ::Units.Count(); i++) { + if (::Units.Ptr(i)->House == this && !::Units.Ptr(i)->IsInLimbo) { + uptr = ::Units.Ptr(i); + + /* + ** Some units can't be killed with one shot, so keep damaging them until + ** they're gone. The unit will destroy itself, and put an infantry in + ** its place. When the unit destroys itself, decrement 'i' since + ** its pointer will be removed from the active pointer list. + */ + count = 0; + while (::Units.Ptr(i)==uptr && uptr->Strength) { + damage = 0x7fff; + Explosion_Damage(uptr->Center_Coord(), damage, NULL, WARHEAD_HE); + count++; + if (count > 5) { + delete uptr; + break; + } + } + i--; + } + } + + /* + ** Destroy all aircraft owned by this house. + */ + for (i = 0; i < ::Aircraft.Count(); i++) { + if (::Aircraft.Ptr(i)->House == this && !::Aircraft.Ptr(i)->IsInLimbo) { + AircraftClass * aptr = ::Aircraft.Ptr(i); + + damage = 0x7fff; + aptr->Take_Damage(damage, 0, WARHEAD_HE, NULL); + if (!aptr->IsActive) { + i--; + } + } + } + + /* + ** Buildings don't delete themselves when they die; they shake the screen + ** and begin a countdown, so don't decrement 'i' when it's destroyed. + */ + for (i = 0; i < Buildings.Count(); i++) { + if (Buildings.Ptr(i)->House == this && !Buildings.Ptr(i)->IsInLimbo) { + bptr = Buildings.Ptr(i); + + count = 0; + bptr->IsSurvivorless = true; + while (Buildings.Ptr(i)==bptr && bptr->Strength) { + damage = 0x7fff; + Explosion_Damage(bptr->Center_Coord(), damage, NULL, WARHEAD_HE); + count++; + if (count > 5) { + delete bptr; + break; + } + } + } + } + + /* + ** Infantry don't delete themselves when they die; they go into a death- + ** animation sequence, so there's no need to decrement 'i' when they die. + ** Infantry should die by different types of warheads, so their death + ** anims aren't all synchronized. + */ + for (i = 0; i < Infantry.Count(); i++) { + if (Infantry.Ptr(i)->House == this && !Infantry.Ptr(i)->IsInLimbo) { + iptr = Infantry.Ptr(i); + + count = 0; + while (Infantry.Ptr(i)==iptr && iptr->Strength) { + damage = 0x7fff; + warhead = (WarheadType)IRandom (WARHEAD_SA, WARHEAD_FIRE); + Explosion_Damage(iptr->Center_Coord(), damage, NULL, warhead); + if (iptr->IsActive) { + damage = 0x7fff; + iptr->Take_Damage(damage, 0, warhead); + } + + count++; + if (count > 5) { + delete iptr; + break; + } + } + } + } + +#ifdef NEVER + /* + ** Just delete the teams & triggers for this house. + */ + for (i = 0; i < TeamTypes.Count(); i++) { + if (TeamTypes.Ptr(i)->House == Class->House) { + delete TeamTypes.Ptr(i); + i--; + } + } + for (i = 0; i < Triggers.Count(); i++) { + if (Triggers.Ptr(i)->House == Class->House) { + delete Triggers.Ptr(i); + i--; + } + } +#endif +} + + +/*********************************************************************************************** + * HouseClass::Flag_To_Die -- Flags the house to blow up soon. * + * * + * When this routine is called, the house will blow up after a period of time. Typically * + * this is called when the flag is captured or the HQ destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: Was the house flagged to blow up? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_To_Die(void) +{ + Validate(); + if (!IsToWin && !IsToDie && !IsToLose) { + IsToDie = true; + if (IsV107) { + BorrowedTime = TICKS_PER_SECOND * 3; + } else { + BorrowedTime = TICKS_PER_SECOND * 1; + } + } + return(IsToDie); +} + + +/*********************************************************************************************** + * HouseClass::Flag_To_Win -- Flags the house to win soon. * + * * + * When this routine is called, the house will be declared the winner after a period of * + * time. * + * * + * INPUT: none * + * * + * OUTPUT: Was the house flagged to win? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_To_Win(void) +{ + Validate(); + if (!IsToWin && !IsToDie && !IsToLose) { + IsToWin = true; + if (IsV107) { + BorrowedTime = TICKS_PER_SECOND * 3; + } else { + BorrowedTime = TICKS_PER_SECOND * 1; + } + } + return(IsToWin); +} + + +/*********************************************************************************************** + * HouseClass::Flag_To_Lose -- Flags the house to die soon. * + * * + * When this routine is called, it will spell the doom of this house. In a short while * + * all of the object owned by this house will explode. Typical use of this routine is when * + * the flag has been captured or the command vehicle has been destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: Has the doom been initiated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_To_Lose(void) +{ + Validate(); + IsToWin = false; + if (!IsToDie && !IsToLose) { + IsToLose = true; + if (IsV107) { + BorrowedTime = TICKS_PER_SECOND * 3; + } else { + BorrowedTime = TICKS_PER_SECOND * 1; + } + } + return(IsToLose); +} + + +/*********************************************************************************************** + * HouseClass::Init_Data -- Initializes the multiplayer color data. * + * * + * This routine is called when initializing the color and remap data for this house. The * + * primary user of this routine is the multiplayer version of the game. * + * * + * INPUT: color -- The color of this house. * + * * + * house -- The house that this should act like. * + * * + * credits -- The initial credits to assign to this house. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Init_Data(PlayerColorType color, HousesType house, int credits) +{ + Validate(); + Credits = InitialCredits = credits; + ActLike = house; + RemapColor = color; + switch (color) { + case REMAP_YELLOW: + RemapTable = RemapYellow; + ((unsigned char &)Class->Color) = 157; + ((unsigned char &)Class->BrightColor) = 5; + break; + + case REMAP_RED: + RemapTable = RemapRed; + ((unsigned char &)Class->Color) = 123; + ((unsigned char &)Class->BrightColor) = 127; + break; + + case REMAP_AQUA: + RemapTable = RemapBlueGreen; + ((unsigned char &)Class->Color) = 135; + ((unsigned char &)Class->BrightColor) = 2; + break; + + case REMAP_ORANGE: + RemapTable = RemapOrange; + ((unsigned char &)Class->Color) = 26; + ((unsigned char &)Class->BrightColor) = 24; + break; + + case REMAP_GREEN: + RemapTable = RemapGreen; + ((unsigned char &)Class->Color) = 167; + ((unsigned char &)Class->BrightColor) = 159; + break; + + case REMAP_BLUE: + RemapTable = RemapBlue; + ((unsigned char &)Class->Color) = 203; + ((unsigned char &)Class->BrightColor) = 201; + break; + } +} + + +/*********************************************************************************************** + * HouseClass::Power_Fraction -- Fetches the current power output rating. * + * * + * Use this routine to fetch the current power output as a fixed point fraction. The * + * value 0x0100 is 100% power. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with power rating as a fixed pointer number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/22/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::Power_Fraction(void) const +{ + Validate(); + if (Power) { + if (Drain) { + return(Cardinal_To_Fixed(Drain, Power)); + } else { + return(0x0100); + } + } + return(0); +} + + +/*********************************************************************************************** + * HouseClass::Has_Nuke_Device -- Deteremines if the house has a nuclear device. * + * * + * This routine checks to see if the house has a nuclear device to launch. A nuclear * + * device is available when the necessary parts have been retrieved in earlier scenarios * + * or if this is the multiplayer version. * + * * + * INPUT: none * + * * + * OUTPUT: Does the house have a nuclear device? * + * * + * WARNINGS: This does not check to see if there is a suitable launch facility (i.e., the * + * Temple of Nod), only that there is a nuclear device potential. * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Has_Nuke_Device(void) +{ + Validate(); + if (GameToPlay != GAME_NORMAL || !IsHuman) return(true); + return((NukePieces & 0x07) == 0x07); +} + + +/*********************************************************************************************** + * HouseClass::Sell_Wall -- Tries to sell the wall at the specified location. * + * * + * This routine will try to sell the wall at the specified location. If there is a wall * + * present and it is owned by this house, then it can be sold. * + * * + * INPUT: cell -- The cell that wall selling is desired. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Sell_Wall(CELL cell) +{ + Validate(); + if ((unsigned)cell > 0) { + OverlayType overlay = Map[cell].Overlay; + + if (overlay != OVERLAY_NONE && Map[cell].Owner == Class->House) { + OverlayTypeClass const & optr = OverlayTypeClass::As_Reference(overlay); + + if (optr.IsWall) { + int cost = 0; + switch (overlay) { + case OVERLAY_SANDBAG_WALL: + cost = BuildingTypeClass::As_Reference(STRUCT_SANDBAG_WALL).Cost_Of(); + break; + + case OVERLAY_CYCLONE_WALL: + cost = BuildingTypeClass::As_Reference(STRUCT_CYCLONE_WALL).Cost_Of(); + break; + + case OVERLAY_BRICK_WALL: + cost = BuildingTypeClass::As_Reference(STRUCT_BRICK_WALL).Cost_Of(); + break; + + case OVERLAY_BARBWIRE_WALL: + cost = BuildingTypeClass::As_Reference(STRUCT_BARBWIRE_WALL).Cost_Of(); + break; + + case OVERLAY_WOOD_WALL: + cost = BuildingTypeClass::As_Reference(STRUCT_WOOD_WALL).Cost_Of(); + break; + + default: + cost = 0; + break; + } + Refund_Money(cost/2); + Map[cell].Overlay = OVERLAY_NONE; + Map[cell].OverlayData = 0; + Map[cell].Owner = HOUSE_NONE; + Map[cell].Wall_Update(); + Map[cell].Recalc_Attributes(); + Map[cell].Redraw_Objects(); + ObjectClass::Detach_This_From_All(::As_Target(cell), true); + } + } + } +} diff --git a/HOUSE.H b/HOUSE.H new file mode 100644 index 0000000..89a1aa3 --- /dev/null +++ b/HOUSE.H @@ -0,0 +1,570 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\house.h_v 2.21 16 Oct 1995 16:46:14 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HOUSE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 21, 1994 * + * * + * Last Update : May 21, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef HOUSE_H +#define HOUSE_H + +#include "type.h" +#include "region.h" +#include "vector.h" + +//extern "C" { +//unsigned Cardinal_To_Fixed(unsigned base, unsigned cardinal); +//} +class TriggerClass; +/**************************************************************************** +** Player control structure. Each player (computer or human) has one of +** these structures associated. These are located in a global array. +*/ +//#define REBUILD_MAX 5 // Maximum number of structures to rebuild. +class HouseClass { + public: + /* + ** Pointer to the HouseTypeClass that this house is "owned" by. + ** All constant data for a house type is stored in that class. + */ + HouseTypeClass const * const Class; + + /* + ** This is the house type that this house object should act like. This + ** value controls production choices and radar cover plate imagery. + */ + HousesType ActLike; + + /* + ** Is this player active? Usually that answer is true, but for civilians, it + ** might possibly be false. + */ + unsigned IsActive:1; + + /* + ** If this house is controlled by the player, then this flag will be true. The + ** computer controls all other active houses. + */ + unsigned IsHuman:1; + + /* + ** When the computer becomes alerted to the presence of the player's forces, it + ** begins production and attack logic. This flag is set to true if the human + ** player has been discovered by the computer. + */ + unsigned IsStarted:1; + + /* + ** When alerted, the house will create teams of the special "auto" type and + ** will generate appropriate units to fill those team types. + */ + unsigned IsAlerted:1; + + /* + ** If the house has been discovered, then this flag will be set + ** to true. However, the trigger even associated with discovery + ** will only be executed during the next house AI process. + */ + unsigned IsDiscovered:1; + + /* + ** If Tiberium storage is maxed out, then this flag will be set. At some point + ** the player is told of this fact and then this flag is cleared. This allows the + ** player to be told, but only occationally rather than continuously. + */ + unsigned IsMaxedOut:1; + + /* + ** If this house is played by a human in a multiplayer game, this flag + ** keeps track of whether this house has been defeated or not. + */ + unsigned IsDefeated:1; + + /* + ** These flags are used in conjunction with the BorrowedTime timer. When + ** that timer expires and one of these flags are set, then that event is + ** applied to the house. This allows a dramatic pause between the event + ** trigger and the result. + */ + unsigned IsToDie:1; + unsigned IsToWin:1; + unsigned IsToLose:1; + + /* + ** This flag is set when a transport carrying a civilian has been + ** successfully evacuated. It is presumed that a possible trigger + ** event will be sprung by this event. + */ + unsigned IsCivEvacuated:1; + + /* + ** If potentially something changed that might affect the sidebar list of + ** buildable objects, then this flag indicates that at the first LEGAL opportunity, + ** the sidebar will be recalculated. + */ + unsigned IsRecalcNeeded:1; + + /* + ** If the map has been completely revealed to the player, then this flag + ** will be set to true. By examining this flag, a second "reveal all map" + ** crate won't be given to the player. + */ + unsigned IsVisionary:1; + + /* + ** If a trigger has indicated that the airstrike option should appear, this flag + ** will be set to true. It is up to the normal house AI processing to actually + ** add the airstrike to the sidebar. + */ + unsigned IsAirstrikePending:1; + + /* + ** This records the existance of the three nuke weapon pieces. + */ + unsigned NukePieces:3; + + /* + ** This flag indicates that a free harvester is pending and will be + ** created when the FreeHarvester timer expires. + */ + unsigned IsFreeHarvester:1; + + TCountDownTimerClass FreeHarvester; + + /* + ** These super weapon control objects are used to control the recharge + ** and availability of these special weapons to this house. + */ + SuperClass IonCannon; + SuperClass AirStrike; + SuperClass NukeStrike; + + /* + ** This is a record of the last building that was built. For buildings that + ** were built as a part of scenario creation, it will be the last one + ** discovered. + */ + StructType JustBuilt; + + /* + ** This records the number of triggers associated with this house that are + ** blocking a win condition. A win will only occur if all the blocking + ** triggers have been deleted. + */ + int Blockage; + + /* + ** This timer controls the computer auto-attack logic. When this timer expires + ** and the house has been alerted, then it will create a set of attack + ** teams. + */ + TCountDownTimerClass AlertTime; + + /* + ** This timer is used to handle the delay between some catastrophic + ** event trigger and when it is actually carried out. + */ + TCountDownTimerClass BorrowedTime; + + /* + ** This is the last working scan bits for buildings. If a building is + ** active and owned by this house, it will have a bit set in this element + ** that corresponds to the building type number. Since this value is + ** accumulated over time, the "New" element contains the under-construction + ** version. + */ + unsigned long BScan; + unsigned long ActiveBScan; + unsigned long NewBScan; + unsigned long NewActiveBScan; + + /* + ** This is the last working scan bits for units. For every existing unit + ** type owned by this house, a corresponding bit is set in this element. As + ** the scan bits are being constructed, they are built into the "New" element + ** and then duplicated into the regular element at the end of every logic cycle. + */ + unsigned long UScan; + unsigned long ActiveUScan; + unsigned long NewUScan; + unsigned long NewActiveUScan; + + /* + ** Infantry type existence bits. Similar to unit and building bits. + */ + unsigned long IScan; + unsigned long ActiveIScan; + unsigned long NewIScan; + unsigned long NewActiveIScan; + + /* + ** Aircraft type existence bits. Similar to unit and building buts. + */ + unsigned long AScan; + unsigned long ActiveAScan; + unsigned long NewAScan; + unsigned long NewActiveAScan; + + /* + ** Record of gains and losses for this house during the course of the + ** scenario. + */ + unsigned CreditsSpent; + unsigned HarvestedCredits; + + /* + ** This is the running count of the number of units owned by this house. This + ** value is used to keep track of ownership limits. + */ + unsigned CurUnits; + unsigned CurBuildings; + + /* + ** This is the maximum number allowed to be built by this house. The + ** value depends on the scenario being played. + */ + unsigned MaxUnit; + unsigned MaxBuilding; + + /* + ** This is the running total of the number of credits this house has accumulated. + */ + long Tiberium; + long Credits; + long InitialCredits; + long Capacity; + + /* + ** Did this house lose via resignation? + */ + unsigned Resigned:1; + + /* + ** Did this house lose because the player quit? + */ + unsigned IGaveUp:1; + + /* + ** Stuff to keep track of the total number of units built by this house. + */ + UnitTrackerClass *AircraftTotals; + UnitTrackerClass *InfantryTotals; + UnitTrackerClass *UnitTotals; + UnitTrackerClass *BuildingTotals; + + /* + ** Total number of units destroyed by this house + */ + UnitTrackerClass *DestroyedAircraft; + UnitTrackerClass *DestroyedInfantry; + UnitTrackerClass *DestroyedUnits; + UnitTrackerClass *DestroyedBuildings; + + /* + ** Total number of enemy buildings captured by this house + */ + UnitTrackerClass *CapturedBuildings; + + /* + ** Total number of crates found by this house + */ + UnitTrackerClass *TotalCrates; + + /* + ** Records the number of infantry and vehicle factories active. This value is + ** used to regulate the speed of production. + */ + int AircraftFactories; + int InfantryFactories; + int UnitFactories; + int BuildingFactories; + int SpecialFactories; + + /* + ** This is the accumulation of the total power and drain factors. From these + ** values a ratio can be derived. This ratio is used to control the rate + ** of building decay. + */ + int Power; // Current power output. + int Drain; // Power consumption. + + /* + ** For generic (unspecified) reinforcements, they arrive by a common method. This + ** specifies which method is to be used. + */ + SourceType Edge; + + /* + ** For human controlled houses, only one type of unit can be produced + ** at any one instant. These factory objects control this production. + */ + int AircraftFactory; + int InfantryFactory; + int UnitFactory; + int BuildingFactory; + int SpecialFactory; + + /* + ** This target value specifies where the flag is located. It might be a cell + ** or it might be an object. + */ + TARGET FlagLocation; + + /* + ** This is the flag-home-cell for this house. This is where we must bring + ** another house's flag back to, to defeat that house. + */ + CELL FlagHome; + + /* + ** For multiplayer games, each house instance has a remap table; the table + ** in the HousesTypeClass isn't used. This variable is set to the remap + ** table for the color the player wants to play. + */ + unsigned char const * RemapTable; + PlayerColorType RemapColor; + char Name[MPLAYER_NAME_MAX]; + + /* + ** For multiplayer games, each house needs to keep track of how many + ** objects of each other house they've killed. + */ + unsigned UnitsKilled[HOUSE_COUNT]; + unsigned UnitsLost; + unsigned BuildingsKilled[HOUSE_COUNT]; + unsigned BuildingsLost; + + /* + ** For multiplayer games, this keeps track of the last house to destroy + ** one of my units. + */ + HousesType WhoLastHurtMe; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + static void * operator new(size_t size); + static void operator delete(void *ptr); + HouseClass(void) : Class(0) {}; + HouseClass(HousesType house); + ~HouseClass(void); + operator HousesType(void) const; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + ProdFailType Begin_Production(RTTIType type, int id); + ProdFailType Suspend_Production(RTTIType type); + ProdFailType Abandon_Production(RTTIType type); + bool Place_Object(RTTIType type, CELL cell); + bool Manual_Place(BuildingClass * builder, BuildingClass * object); + void Special_Weapon_AI(SpecialWeaponType id); + bool Place_Special_Blast(SpecialWeaponType id, CELL cell); + bool Flag_Attach(CELL cell, bool set_home = false); + bool Flag_Attach(UnitClass * object, bool set_home = false); + bool Flag_Remove(TARGET target, bool set_home = false); + void Init_Data(PlayerColorType color, HousesType house, int credits); + + void Sell_Wall(CELL cell); + bool Flag_To_Die(void); + bool Flag_To_Win(void); + bool Flag_To_Lose(void); + void Make_Ally(HousesType house); + void Make_Ally(ObjectClass * object) {if (object) Make_Ally(object->Owner());}; + void Make_Enemy(HousesType house); + void Make_Enemy(ObjectClass * object) {if (object) Make_Enemy(object->Owner());}; + bool Is_Ally(HousesType house) const; + bool Is_Ally(HouseClass const * house) const; + bool Is_Ally(ObjectClass const * object) const; + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const; + #endif + void AI(void); + bool Can_Build(StructType structure, HousesType house) const; + bool Can_Build(InfantryType infantry, HousesType house) const; + bool Can_Build(UnitType unit, HousesType) const; + bool Can_Build(AircraftType aircraft, HousesType house) const; + bool Can_Build(TechnoTypeClass const * type, HousesType house) const; + unsigned char const * Remap_Table(bool blushing=false, bool unit=false) const; + + TechnoTypeClass const * Suggest_New_Object(RTTIType objectype) const; + bool Does_Enemy_Building_Exist(StructType) const; + void Harvested(unsigned tiberium); + long Available_Money(void) const; + void Spend_Money(unsigned money); + void Refund_Money(unsigned money); + void Attacked(void); + void Adjust_Power(int adjust) {Power += adjust;}; + void Adjust_Drain(int adjust) {Drain += adjust;}; + int Adjust_Capacity(int adjust, bool inanger=false); + int Power_Fraction(void) const; + int Tiberium_Fraction(void) {return (!Tiberium) ? 0 : Cardinal_To_Fixed(Capacity, Tiberium);}; + void Begin_Production(void) {IsStarted = true;}; + TeamTypeClass const * Suggested_New_Team(bool alertcheck = false); + void Adjust_Threat(int region, int threat); + + static void Init(void); + static void One_Time(void); + static HouseClass * As_Pointer(HousesType house); + + /* + ** File I/O. + */ + static void Read_INI(char *buffer); + static void Write_INI(char *buffer); + static void Read_Flag_INI(char *buffer); + static void Write_Flag_INI(char *buffer); + bool Load(FileClass & file); + bool Save(FileClass & file); + void Code_Pointers(void); + void Decode_Pointers(void); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + /* + ** Special house actions. + */ +// void Init_Ion_Cannon(bool first_time, bool one_time_effect = false); +// void Init_Air_Strike(bool first_time, bool one_time_effect = false); +// void Init_Nuke_Bomb(bool first_time, bool one_time_effect = false); +// void Remove_Ion_Cannon(void); +// void Remove_Air_Strike(void); +// void Remove_Nuke_Bomb(void); + void Detach(TARGET target, bool all); + void Add_Nuke_Piece(int piece=-1); +// void Make_Air_Strike_Available(bool present, bool one_time_effect = false); + bool Has_Nuke_Device(void); + + /* + ** This vector holds the recorded status of the map regions. It is through + ** this region information that team paths are calculated. + */ + RegionClass Regions[MAP_TOTAL_REGIONS]; + +#ifdef OBSOLETE + /* + ** This count down timer class handles decrements and then changes + ** the ion cannon state. If the Ion cannon was out of range it is + ** now in range. If the Ion cannon was in range it will toggle out + ** of range. + */ + TCountDownTimerClass IonControl; + int IonOldStage; + + TCountDownTimerClass AirControl; + int AirOldStage; + + TCountDownTimerClass NukeControl; + int NukeOldStage; +#endif + + + /* + ** This timer is for multiplayer mode; for a computer-controlled house, + ** it determines how long until this player "blitzes" the hapless humans. + */ + TCountDownTimerClass BlitzTime; + + /* + ** This count down timer class decrements and then changes + ** the Atomic Bomb state. + */ + CELL NukeDest; + + /* + ** This routine completely removes this house & all its objects from the game. + */ + void Clobber_All(void); + + /* + ** This routine blows up everything in this house. Fun! + */ + void Blowup_All(void); + + /* + ** This routine gets called in multiplayer games when every unit, building, + ** and infantry for a house is destroyed. + */ + void MPlayer_Defeated(void); + + private: + void Silo_Redraw_Check(long oldtib, long oldcap); + + /* + ** This is a bit field record of all the other houses that are allies with + ** this house. It is presumed that any house that isn't an ally, is therefore + ** an enemy. A house is always considered allied with itself. + */ + unsigned Allies; + + /* + ** This is the standard delay time between announcements concerning the + ** state of the base or other intermittent house related events. + */ + enum SpeakDelayEnum { + SPEAK_DELAY=TICKS_PER_MINUTE*2, + TEAM_DELAY=TICKS_PER_MINUTE/10, + DAMAGE_DELAY=TICKS_PER_MINUTE + }; + + /* + ** General low-power related damaged is doled out whenever this timer + ** expires. + */ + TCountDownTimerClass DamageTime; + + /* + ** Team creation is done whenever this timer expires. + */ + TCountDownTimerClass TeamTime; + + /* + ** This controls the rate that the trigger time logic is processed. + */ + TCountDownTimerClass TriggerTime; + + /* + ** At various times, the computer may announce the player's condition. The following + ** variables are used as countdown timers so that these announcements are paced + ** far enough appart to reduce annoyance. + */ + TCountDownTimerClass SpeakAttackDelay; + TCountDownTimerClass SpeakPowerDelay; + TCountDownTimerClass SpeakMoneyDelay; + TCountDownTimerClass SpeakMaxedDelay; +}; +#endif + diff --git a/IDATA.CPP b/IDATA.CPP new file mode 100644 index 0000000..538bfb2 --- /dev/null +++ b/IDATA.CPP @@ -0,0 +1,2154 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\idata.cpv 2.12 02 Aug 1995 17:00:30 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 15, 1994 * + * * + * Last Update : June 29, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * InfantryTypeClass::Create_And_Place -- Creates and places infantry object onto the map. * + * InfantryTypeClass::Create_One_Of -- Creates an infantry object. * + * InfantryTypeClass::Display -- Displays a generic infantry object. * + * InfantryTypeClass::From_Name -- Converts an ASCII name into an infantry type number. * + * InfantryTypeClass::Full_Name -- Fetches the full name text number. * + * InfantryTypeClass::Get_Cameo_Data -- Fetches the small cameo shape for sidebar strip. * + * InfantryTypeClass::InfantryTypeClass -- Constructor for infantry type class objects. * + * InfantryTypeClass::Occupy_List -- Returns with default infantry occupation list. * + * InfantryTypeClass::One_Time -- Performs any one time processing for infantry system. * + * InfantryTypeClass::Prep_For_Add -- Prepares the scenario editor for adding of infantry object.* + * InfantryTypeClass::Who_Can_Build_Me -- Determines what can build the infantry object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "type.h" + + + +/* + * There were too many parameters for the InfantryTypeClass constructor so I have + * created a table of Do Controls for each unit type and I am passing a pointer + * to the table to the constructor instead of passing each value as it was before. + * + * If this offends anyones C++ sensibilities then please feel free to implement a + * more elegant oop solution. + * + * Steve T. 10/3/95 10:13AM + * + */ + +// Minigunners + +int MiniGunnerDos [DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 8, 1, 1, // DO_STAND_GUARD + 192, 1, 8, // DO_PRONE + 16, 6, 6, // DO_WALK + 64, 8, 8, // DO_FIRE_WEAPON + 128, 2, 2, // DO_LIE_DOWN + 144, 4, 4, // DO_CRAWL + 176, 2, 2, // DO_GET_UP + 192, 6, 8, // DO_FIRE_PRONE + 256, 16,0, // DO_IDLE1 + 272, 16,0, // DO_IDLE2 + 288, 13,0, // DO_ON_GUARD + 292, 10,0, // DO_FIGHT_READY + 301, 2, 0, // DO_PUNCH + 303, 6, 0, // DO_KICK + 309, 2, 0, // DO_PUNCH_HIT1 + 311, 4, 0, // DO_PUNCH_HIT2 + 315, 5, 0, // DO_PUNCH_DEATH + 319, 2, 0, // DO_KICK_HIT1 + 321, 4, 0, // DO_KICK_HIT2 + 325, 5, 0, // DO_KICK_DEATH + 330, 5, 0, // DO_READY_WEAPON + 382, 8, 0, // DO_GUN_DEATH + 398, 8, 0, // DO_EXPLOSION_DEATH + 398, 8, 0, // DO_EXPLOSION2_DEATH + 406, 12,0, // DO_GRENADE_DEATH + 418, 18,0, // DO_FIRE_DEATH + 436, 3, 3, // DO_GESTURE1 + 460, 3, 3, // DO_SALUTE1 + 484, 3, 3, // DO_GESTURE2 + 508, 3, 3, // DO_SALUTE2 + 0, 1, 1, // DO_PULL_GUN // N/A + 0, 1, 1, // DO_PLEAD // N/A + 0, 1, 1 // DO_PLEAD_DEATH // N/A +}; + + +static InfantryTypeClass const E1( + INFANTRY_E1, // Infantry type number. + TXT_E1, // Translate name number for infantry type. + "E1", // INI name for infantry. + 1, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + true, // Is a leader type? + true, // Has crawling animation frames? + false, // Is this a civlian? + false, // Always use the given name for the infantry? + false, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + -1, // Number of shots it has (default). + &MiniGunnerDos[0][0], // Ptr to minigunner 'DO' table above + 2, // Frame of projectile launch. + 2, // Frame of projectile launch while prone. + 50, // Strength of infantry (in damage points). + 1, // Sight range. + 100, // Cost of infantry (in credits). + 1, // Scenario when they first appear. + 80,10, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // Who can own this infantry unit. + WEAPON_M16,WEAPON_NONE, + MPH_SLOW // Maximum speed of infantry. +); + + +// Grenadiers + + +int GrenadierDos [DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 8, 1, 1, // DO_STAND_GUARD + 288, 1, 12, // DO_PRONE + 16, 6, 6, // DO_WALK + 64, 20,20, // DO_FIRE_WEAPON + 224, 2, 2, // DO_LIE_DOWN + 240, 4, 4, // DO_CRAWL + 272, 2, 2, // DO_GET_UP + 288, 8, 12, // DO_FIRE_PRONE + 384, 16,0, // DO_IDLE1 + 400, 16,0, // DO_IDLE2 + 416, 13,0, // DO_ON_GUARD + 420, 10,0, // DO_FIGHT_READY + 429, 2, 0, // DO_PUNCH + 431, 6, 0, // DO_KICK + 437, 2, 0, // DO_PUNCH_HIT1 + 439, 4, 0, // DO_PUNCH_HIT2 + 443, 4, 0, // DO_PUNCH_DEATH + 447, 2, 0, // DO_KICK_HIT1 + 449, 4, 0, // DO_KICK_HIT2 + 453, 5, 0, // DO_KICK_DEATH + 458, 5, 0, // DO_READY_WEAPON + 510, 8, 0, // DO_GUN_DEATH + 526, 8, 0, // DO_EXPLOSION_DEATH + 526, 8, 0, // DO_EXPLOSION2_DEATH + 534, 12,0, // DO_GRENADE_DEATH + 546, 18,0, // DO_FIRE_DEATH + 564, 3, 3, // DO_GESTURE1 + 588, 3, 3, // DO_SALUTE1 + 612, 3, 3, // DO_GESTURE2 + 636, 3, 3, // DO_SALUTE2 + 0, 1, 1, // DO_PULL_GUN // N/A + 0, 1, 1, // DO_PLEAD // N/A + 0, 1, 1 // DO_PLEAD_DEATH // N/A +}; + +static InfantryTypeClass const E2( + INFANTRY_E2, // Infantry type number. + TXT_E2, // Translate name number for infantry type. + "E2", // INI name for infantry. + 1, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + true, // Is a leader type? + true, // Has crawling animation frames? + false, // Is this a civlian? + false, // Always use the given name for the infantry? + false, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + -1, // Number of shots it has (default). + &GrenadierDos[0][0], // Ptr to grenadier DO table (above) + 14, // Frame of projectile launch. + 6, // Frame of projectile launch while prone. + 50, // Strength of infantry (in damage points). + 1, // Sight range. + 160, // Cost of infantry (in credits). + 3, // Scenario when they first appear. + 80,10, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD, // Who can own this infantry unit. + WEAPON_GRENADE,WEAPON_NONE, + MPH_SLOW_ISH // Maximum speed of infantry. +); + + + + +// Bazooka + +int BazookaDos [DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 8, 1, 1, // DO_STAND_GUARD + 192, 1,10, // DO_PRONE + 16, 6, 6, // DO_WALK + 64, 8, 8, // DO_FIRE_WEAPON + 128, 2, 2, // DO_LIE_DOWN + 144, 4, 4, // DO_CRAWL + 176, 2, 2, // DO_GET_UP + 192, 10,10, // DO_FIRE_PRONE + 272, 16,0, // DO_IDLE1 + 288, 16,0, // DO_IDLE2 + 304, 13,0, // DO_ON_GUARD + 308, 10,0, // DO_FIGHT_READY + 307, 2, 0, // DO_PUNCH + 319, 6, 0, // DO_KICK + 325, 2, 0, // DO_PUNCH_HIT1 + 327, 4, 0, // DO_PUNCH_HIT2 + 331, 4, 0, // DO_PUNCH_DEATH + 335, 2, 0, // DO_KICK_HIT1 + 337, 4, 0, // DO_KICK_HIT2 + 341, 5, 0, // DO_KICK_DEATH + 346, 5, 0, // DO_READY_WEAPON + 398, 8, 0, // DO_GUN_DEATH + 414, 8, 0, // DO_EXPLOSION_DEATH + 414, 8, 0, // DO_EXPLOSION2_DEATH + 422, 12,0, // DO_GRENADE_DEATH + 434, 18,0, // DO_FIRE_DEATH + 452, 3, 3, // DO_GESTURE1 + 476, 3, 3, // DO_SALUTE1 + 500, 3, 3, // DO_GESTURE2 + 524, 3, 3, // DO_SALUTE2 + 0, 1, 1, // DO_PULL_GUN // N/A + 0, 1, 1, // DO_PLEAD // N/A + 0, 1, 1 // DO_PLEAD_DEATH // N/A +}; + + + +static InfantryTypeClass const E3( + INFANTRY_E3, // Infantry type number. + TXT_E3, // Translate name number for infantry type. + "E3", // INI name for infantry. + 2, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + true, // Is a leader type? + true, // Has crawling animation frames? + false, // Is this a civlian? + false, // Always use the given name for the infantry? + false, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + -1, // Number of shots it has (default). + &BazookaDos[0][0], // Ptr to DO table (above) + 3, // Frame of projectile launch. + 3, // Frame of projectile launch while prone. + 25, // Strength of infantry (in damage points). + 2, // Sight range. + 300, // Cost of infantry (in credits). + 3, // Scenario when they first appear. + 80,10, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // Who can own this infantry unit. + WEAPON_DRAGON,WEAPON_NONE, + MPH_KINDA_SLOW // Maximum speed of infantry. +); + +// Flamethrower + +int FlamethrowerDos [DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 8, 1, 1, // DO_STAND_GUARD + 256, 1,16, // DO_PRONE + 16, 6, 6, // DO_WALK + 64, 16,16, // DO_FIRE_WEAPON + 192, 2, 2, // DO_LIE_DOWN + 208, 4, 4, // DO_CRAWL + 240, 2, 2, // DO_GET_UP + 256, 16,16, // DO_FIRE_PRONE + 384, 16,0, // DO_IDLE1 + 400, 16,0, // DO_IDLE2 + 416, 13,0, // DO_ON_GUARD + 420, 10,0, // DO_FIGHT_READY + 429, 2, 0, // DO_PUNCH + 431, 6, 0, // DO_KICK + 437, 2, 0, // DO_PUNCH_HIT1 + 439, 4, 0, // DO_PUNCH_HIT2 + 443, 4, 0, // DO_PUNCH_DEATH + 447, 2, 0, // DO_KICK_HIT1 + 449, 4, 0, // DO_KICK_HIT2 + 453, 5, 0, // DO_KICK_DEATH + 458, 5, 0, // DO_READY_WEAPON + 510, 8, 0, // DO_GUN_DEATH + 526, 8, 0, // DO_EXPLOSION_DEATH + 526, 8, 0, // DO_EXPLOSION2_DEATH + 534, 12,0, // DO_GRENADE_DEATH + 546, 18,0, // DO_FIRE_DEATH + 564, 3, 3, // DO_GESTURE1 + 588, 3, 3, // DO_SALUTE1 + 612, 3, 3, // DO_GESTURE2 + 636, 3, 3, // DO_SALUTE2 + 0, 1, 1, // DO_PULL_GUN // N/A + 0, 1, 1, // DO_PLEAD // N/A + 0, 1, 1 // DO_PLEAD_DEATH // N/A +}; + + + +static InfantryTypeClass const E4( + INFANTRY_E4, // Infantry type number. + TXT_E4, // Translate name number for infantry type. + "E4", // INI name for infantry. + 1, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + true, // Is a leader type? + true, // Has crawling animation frames? + false, // Is this a civlian? + false, // Always use the given name for the infantry? + false, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + -1, // Number of shots it has (default). + &FlamethrowerDos[0][0], // ptr to DO table (above) + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 70, // Strength of infantry (in damage points). + 1, // Sight range. + 200, // Cost of infantry (in credits). + 5, // Scenario when they first appear. + 80,10, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // Who can own this infantry unit. + WEAPON_FLAMETHROWER,WEAPON_NONE, + MPH_SLOW_ISH +); + + +// Chemwarrior + +int ChemwarriorDos [DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 8, 1, 1, // DO_STAND_GUARD + 256, 1,16, // DO_PRONE + 16, 6, 6, // DO_WALK + 64, 16,16, // DO_FIRE_WEAPON + 192, 2, 2, // DO_LIE_DOWN + 208, 4, 4, // DO_CRAWL + 240, 2, 2, // DO_GET_UP + 256, 16,16, // DO_FIRE_PRONE + 384, 16,0, // DO_IDLE1 + 400, 16,0, // DO_IDLE2 + 416, 13,0, // DO_ON_GUARD + 420, 10,0, // DO_FIGHT_READY + 429, 2, 0, // DO_PUNCH + 431, 6, 0, // DO_KICK + 437, 2, 0, // DO_PUNCH_HIT1 + 439, 4, 0, // DO_PUNCH_HIT2 + 443, 4, 0, // DO_PUNCH_DEATH + 447, 2, 0, // DO_KICK_HIT1 + 449, 4, 0, // DO_KICK_HIT2 + 453, 5, 0, // DO_KICK_DEATH + 458, 5, 0, // DO_READY_WEAPON + 510, 8, 0, // DO_GUN_DEATH + 526, 8, 0, // DO_EXPLOSION_DEATH + 526, 8, 0, // DO_EXPLOSION2_DEATH + 534, 12,0, // DO_GRENADE_DEATH + 546, 18,0, // DO_FIRE_DEATH + 564, 3, 3, // DO_GESTURE1 + 588, 3, 3, // DO_SALUTE1 + 612, 3, 3, // DO_GESTURE2 + 636, 3, 3, // DO_SALUTE2 + 0, 1, 1, // DO_PULL_GUN // N/A + 0, 1, 1, // DO_PLEAD // N/A + 0, 1, 1 // DO_PLEAD_DEATH // N/A +}; + + +static InfantryTypeClass const E5( + INFANTRY_E5, // Infantry type number. + TXT_E5, // Translate name number for infantry type. + "E5", // INI name for infantry. + 7, // Build level. + STRUCTF_EYE, // Building prerequisite. + false, // Is this a female type? + true, // Is a leader type? + true, // Has crawling animation frames? + false, // Is this a civlian? + false, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + -1, // Number of shots it has (default). + &ChemwarriorDos[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 70, // Strength of infantry (in damage points). + 1, // Sight range. + 300, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 80,10, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| +// HOUSEF_GOOD| + HOUSEF_BAD, // Who can own this infantry unit. + WEAPON_CHEMSPRAY,WEAPON_NONE, + MPH_SLOW +); + + +// Engineer + +int EngineerDos[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 8, 1, 1, // DO_STAND_GUARD + 82, 1, 4, // DO_PRONE + 16, 6, 6, // DO_WALK + 0, 0, 0, // DO_FIRE_WEAPON + 67, 2, 2, // DO_LIE_DOWN + 82, 4, 4, // DO_CRAWL + 114, 2, 2, // DO_GET_UP + 0, 0, 0, // DO_FIRE_PRONE + 130, 16,0, // DO_IDLE1 + 0, 0, 0, // DO_IDLE2 + 0, 0, 0, // DO_ON_GUARD + 0, 0, 0, // DO_FIGHT_READY + 0, 0, 0, // DO_PUNCH + 0, 0, 0, // DO_KICK + 0, 0, 0, // DO_PUNCH_HIT1 + 0, 0, 0, // DO_PUNCH_HIT2 + 0, 0, 0, // DO_PUNCH_DEATH + 0, 0, 0, // DO_KICK_HIT1 + 0, 0, 0, // DO_KICK_HIT2 + 0, 0, 0, // DO_KICK_DEATH + 0, 0, 0, // DO_READY_WEAPON + 146, 8, 0, // DO_GUN_DEATH + 154, 8, 0, // DO_EXPLOSION_DEATH + 162, 8, 0, // DO_EXPLOSION2_DEATH + 162, 12,0, // DO_GRENADE_DEATH + 182, 18,0, // DO_FIRE_DEATH + 200, 3, 3, // DO_GESTURE1 + 224, 3, 3, // DO_SALUTE1 + 200, 3, 3, // DO_GESTURE2 + 224, 3, 3, // DO_SALUTE2 + 0, 1, 1, // DO_PULL_GUN // N/A + 0, 1, 1, // DO_PLEAD // N/A + 0, 1, 1 // DO_PLEAD_DEATH // N/A +}; + +static InfantryTypeClass const E7( + INFANTRY_E7, // Infantry type number. + TXT_E7, // Translate name number for infantry type. + "E6", // INI name for infantry. + 3, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + false, // Is a leader type? + false, // Has crawling animation frames? + false, // Is this a civlian? + false, // Always use the given name for the infantry? + false, // Is this a "fraidycat" run-away type infantry? + true, // Can this infantry type capture a building? + false, // Theater specific graphic image? + -1, // Number of shots it has (default). + &EngineerDos[0][0], // ptr to DO table + 3, // Frame of projectile launch. + 3, // Frame of projectile launch while prone. + 25, // Strength of infantry (in damage points). + 2, // Sight range. + 500, // Cost of infantry (in credits). + 2, // Scenario when they first appear. + 80,75, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD| + HOUSEF_GOOD, // Who can own this infantry unit. + WEAPON_NONE,WEAPON_NONE, + MPH_SLOW // Maximum speed of infantry. +); + +// Commandos + +int CommandoDos[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 8, 1, 1, // DO_STAND_GUARD + 160, 1, 4, // DO_PRONE + 16, 6, 6, // DO_WALK + 64, 4, 4, // DO_FIRE_WEAPON + 96, 2, 2, // DO_LIE_DOWN + 112, 4, 4, // DO_CRAWL + 144, 2, 2, // DO_GET_UP + 160, 4, 4, // DO_FIRE_PRONE + 192, 16,0, // DO_IDLE1 + 208, 16,0, // DO_IDLE2 + 224, 13,0, // DO_ON_GUARD + 228, 9, 0, // DO_FIGHT_READY + 237, 2, 0, // DO_PUNCH + 239, 6, 0, // DO_KICK + 245, 2, 0, // DO_PUNCH_HIT1 + 247, 4, 0, // DO_PUNCH_HIT2 + 251, 4, 0, // DO_PUNCH_DEATH + 255, 2, 0, // DO_KICK_HIT1 + 257, 4, 0, // DO_KICK_HIT2 + 261, 5, 0, // DO_KICK_DEATH + 266, 5, 0, // DO_READY_WEAPON + 318, 8, 0, // DO_GUN_DEATH + 334, 8, 0, // DO_EXPLOSION_DEATH + 334, 8, 0, // DO_EXPLOSION2_DEATH + 342, 12,0, // DO_GRENADE_DEATH + 354, 18,0, // DO_FIRE_DEATH + 372, 3, 3, // DO_GESTURE1 + 396, 3, 3, // DO_SALUTE1 + 420, 3, 3, // DO_GESTURE2 + 444, 3, 3, // DO_SALUTE2 + 0, 1, 1, // DO_PULL_GUN // N/A + 0, 1, 1, // DO_PLEAD // N/A + 0, 1, 1 // DO_PLEAD_DEATH // N/A + +}; +static InfantryTypeClass const Commando( + INFANTRY_RAMBO, // Infantry type number. + TXT_RAMBO, // Translate name number for infantry type. + "RMBO", // INI name for infantry. + 7, // Build level. + STRUCTF_EYE, // Building prerequisite. + false, // Is this a female type? + true, // Is a leader type? + true, // Has crawling animation frames? + false, // Is this a civlian? + false, // Always use the given name for the infantry? + false, // Is this a "fraidycat" run-away type infantry? + true, // Can this infantry type capture a building? + false, // Theater specific graphic image? + -1, // Number of shots it has (default). + &CommandoDos[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 2, // Frame of projectile launch while prone. + 80, // Strength of infantry (in damage points). + 5, // Sight range. + 1000, // Cost of infantry (in credits). + 98, // Scenario when they first appear. + 80,75, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // Who can own this infantry unit. + WEAPON_RIFLE,WEAPON_NONE, + MPH_SLOW_ISH // Maximum speed of infantry. +); + +// Civilians + +int CivilianDos1[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 205, 4, 4, // DO_FIRE_PRONE + 189, 10,0, // DO_IDLE1 + 199, 6, 0, // DO_IDLE2 + 104, 3, 0, // DO_ON_GUARD + 107, 7, 0, // DO_FIGHT_READY + 114, 2, 0, // DO_PUNCH + 116, 6, 0, // DO_KICK + 122, 2, 0, // DO_PUNCH_HIT1 + 124, 4, 0, // DO_PUNCH_HIT2 + 128, 4, 0, // DO_PUNCH_DEATH + 133, 2, 0, // DO_KICK_HIT1 + 135, 4, 0, // DO_KICK_HIT2 + 139, 5, 0, // DO_KICK_DEATH + 144, 3, 0, // DO_READY_WEAPON + 329, 8, 0, // DO_GUN_DEATH + 337, 8, 0, // DO_EXPLOSION_DEATH + 337, 8, 0, // DO_EXPLOSION2_DEATH + 345, 12,0, // DO_GRENADE_DEATH + 357, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 199, 6, 0, // DO_PULL_GUN + 237, 40,0, // DO_PLEAD + 277, 6, 0 // DO_PLEAD_DEATH +}; + +static InfantryTypeClass const C1( + INFANTRY_C1, // Infantry type number. + TXT_C1, // Translate name number for infantry type. + "C1", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + true, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 10, // Number of shots it has (default). + &CivilianDos1[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 25, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,1, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_PISTOL,WEAPON_NONE, + MPH_SLOW_ISH +); + + +int CivilianDos2[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 205, 4, 4, // DO_FIRE_PRONE + 189, 10,0, // DO_IDLE1 + 199, 6, 0, // DO_IDLE2 + 104, 3, 0, // DO_ON_GUARD + 107, 7, 0, // DO_FIGHT_READY + 114, 2, 0, // DO_PUNCH + 116, 6, 0, // DO_KICK + 122, 2, 0, // DO_PUNCH_HIT1 + 124, 4, 0, // DO_PUNCH_HIT2 + 128, 4, 0, // DO_PUNCH_DEATH + 133, 2, 0, // DO_KICK_HIT1 + 135, 4, 0, // DO_KICK_HIT2 + 139, 5, 0, // DO_KICK_DEATH + 144, 3, 0, // DO_READY_WEAPON + 329, 8, 0, // DO_GUN_DEATH + 337, 8, 0, // DO_EXPLOSION_DEATH + 337, 8, 0, // DO_EXPLOSION2_DEATH + 345, 12,0, // DO_GRENADE_DEATH + 357, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 199, 6, 0, // DO_PULL_GUN + 237, 40,0, // DO_PLEAD + 277, 6, 0 // DO_PLEAD_DEATH +}; + +static InfantryTypeClass const C2( + INFANTRY_C2, // Infantry type number. + TXT_C2, // Translate name number for infantry type. + "C2", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + false, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 0, // Number of shots it has (default). + &CivilianDos2[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 5, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,1, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_NONE,WEAPON_NONE, + MPH_SLOW_ISH +); + + +int CivilianDos3[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 205, 4, 4, // DO_FIRE_PRONE + 189, 10,0, // DO_IDLE1 + 199, 6, 0, // DO_IDLE2 + 104, 3, 0, // DO_ON_GUARD + 107, 7, 0, // DO_FIGHT_READY + 114, 2, 0, // DO_PUNCH + 116, 6, 0, // DO_KICK + 122, 2, 0, // DO_PUNCH_HIT1 + 124, 4, 0, // DO_PUNCH_HIT2 + 128, 4, 0, // DO_PUNCH_DEATH + 133, 2, 0, // DO_KICK_HIT1 + 135, 4, 0, // DO_KICK_HIT2 + 139, 5, 0, // DO_KICK_DEATH + 144, 3, 0, // DO_READY_WEAPON + 329, 8, 0, // DO_GUN_DEATH + 337, 8, 0, // DO_EXPLOSION_DEATH + 337, 8, 0, // DO_EXPLOSION2_DEATH + 345, 12,0, // DO_GRENADE_DEATH + 357, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 199, 6, 0, // DO_PULL_GUN + 237, 40,0, // DO_PLEAD + 277, 6, 0 // DO_PLEAD_DEATH + +}; + +static InfantryTypeClass const C3( + INFANTRY_C3, // Infantry type number. + TXT_C3, // Translate name number for infantry type. + "C3", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + true, // Is this a female type? + false, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 0, // Number of shots it has (default). + &CivilianDos3[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 5, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,1, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_NONE,WEAPON_NONE, + MPH_SLOW_ISH +); + + +int CivilianDos4[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 205, 4, 4, // DO_FIRE_PRONE + 189, 10,0, // DO_IDLE1 + 199, 6, 0, // DO_IDLE2 + 104, 3, 0, // DO_ON_GUARD + 107, 7, 0, // DO_FIGHT_READY + 114, 2, 0, // DO_PUNCH + 116, 6, 0, // DO_KICK + 122, 2, 0, // DO_PUNCH_HIT1 + 124, 4, 0, // DO_PUNCH_HIT2 + 128, 4, 0, // DO_PUNCH_DEATH + 133, 2, 0, // DO_KICK_HIT1 + 135, 4, 0, // DO_KICK_HIT2 + 139, 5, 0, // DO_KICK_DEATH + 144, 3, 0, // DO_READY_WEAPON + 329, 8, 0, // DO_GUN_DEATH + 337, 8, 0, // DO_EXPLOSION_DEATH + 337, 8, 0, // DO_EXPLOSION2_DEATH + 345, 12,0, // DO_GRENADE_DEATH + 357, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 199, 6, 0, // DO_PULL_GUN + 237, 40,0, // DO_PLEAD + 277, 6, 0, // DO_PLEAD_DEATH +}; + + +static InfantryTypeClass const C4( + INFANTRY_C4, // Infantry type number. + TXT_C4, // Translate name number for infantry type. + "C4", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + true, // Is this a female type? + false, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 0, // Number of shots it has (default). + &CivilianDos4[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 5, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,1, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_NONE,WEAPON_NONE, + MPH_SLOW_ISH +); + + +int CivilianDos5[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 205, 4, 4, // DO_FIRE_PRONE + 189, 10,0, // DO_IDLE1 + 199, 6, 0, // DO_IDLE2 + 104, 3, 0, // DO_ON_GUARD + 107, 7, 0, // DO_FIGHT_READY + 114, 2, 0, // DO_PUNCH + 116, 6, 0, // DO_KICK + 122, 2, 0, // DO_PUNCH_HIT1 + 124, 4, 0, // DO_PUNCH_HIT2 + 128, 4, 0, // DO_PUNCH_DEATH + 133, 2, 0, // DO_KICK_HIT1 + 135, 4, 0, // DO_KICK_HIT2 + 139, 5, 0, // DO_KICK_DEATH + 144, 3, 0, // DO_READY_WEAPON + 329, 8, 0, // DO_GUN_DEATH + 337, 8, 0, // DO_EXPLOSION_DEATH + 337, 8, 0, // DO_EXPLOSION2_DEATH + 345, 12,0, // DO_GRENADE_DEATH + 357, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 199, 6, 0, // DO_PULL_GUN + 237, 40,0, // DO_PLEAD + 277, 6, 0 // DO_PLEAD_DEATH +}; + +static InfantryTypeClass const C5( + INFANTRY_C5, // Infantry type number. + TXT_C5, // Translate name number for infantry type. + "C5", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + false, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 0, // Number of shots it has (default). + &CivilianDos5[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 5, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,1, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_NONE,WEAPON_NONE, + MPH_SLOW_ISH +); + + +int CivilianDos6[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 205, 4, 4, // DO_FIRE_PRONE + 189, 10,0, // DO_IDLE1 + 199, 6, 0, // DO_IDLE2 + 104, 3, 0, // DO_ON_GUARD + 107, 7, 0, // DO_FIGHT_READY + 114, 2, 0, // DO_PUNCH + 116, 6, 0, // DO_KICK + 122, 2, 0, // DO_PUNCH_HIT1 + 124, 4, 0, // DO_PUNCH_HIT2 + 128, 4, 0, // DO_PUNCH_DEATH + 133, 2, 0, // DO_KICK_HIT1 + 135, 4, 0, // DO_KICK_HIT2 + 139, 5, 0, // DO_KICK_DEATH + 144, 3, 0, // DO_READY_WEAPON + 329, 8, 0, // DO_GUN_DEATH + 337, 8, 0, // DO_EXPLOSION_DEATH + 337, 8, 0, // DO_EXPLOSION2_DEATH + 345, 12,0, // DO_GRENADE_DEATH + 357, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 199, 6, 0, // DO_PULL_GUN + 237, 40,0, // DO_PLEAD + 277, 6, 0 // DO_PLEAD_DEATH +}; + +static InfantryTypeClass const C6( + INFANTRY_C6, // Infantry type number. + TXT_C6, // Translate name number for infantry type. + "C6", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + false, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 0, // Number of shots it has (default). + &CivilianDos6[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 5, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,1, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_NONE,WEAPON_NONE, + MPH_SLOW_ISH +); + + +int CivilianDos7[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 205, 4, 4, // DO_FIRE_PRONE + 189, 10,0, // DO_IDLE1 + 199, 6, 0, // DO_IDLE2 + 104, 3, 0, // DO_ON_GUARD + 107, 7, 0, // DO_FIGHT_READY + 114, 2, 0, // DO_PUNCH + 116, 6, 0, // DO_KICK + 122, 2, 0, // DO_PUNCH_HIT1 + 124, 4, 0, // DO_PUNCH_HIT2 + 128, 4, 0, // DO_PUNCH_DEATH + 133, 2, 0, // DO_KICK_HIT1 + 135, 4, 0, // DO_KICK_HIT2 + 139, 5, 0, // DO_KICK_DEATH + 144, 3, 0, // DO_READY_WEAPON + 329, 8, 0, // DO_GUN_DEATH + 337, 8, 0, // DO_EXPLOSION_DEATH + 337, 8, 0, // DO_EXPLOSION2_DEATH + 345, 12,0, // DO_GRENADE_DEATH + 357, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 199, 6, 0, // DO_PULL_GUN + 237, 40,0, // DO_PLEAD + 277, 6, 0 // DO_PLEAD_DEATH +}; + +static InfantryTypeClass const C7( + INFANTRY_C7, // Infantry type number. + TXT_C7, // Translate name number for infantry type. + "C7", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + true, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 10, // Number of shots it has (default). + &CivilianDos7[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 5, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,1, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_PISTOL,WEAPON_NONE, + MPH_SLOW_ISH +); + +int CivilianDos8[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 205, 4, 4, // DO_FIRE_PRONE + 189, 10,0, // DO_IDLE1 + 199, 6, 0, // DO_IDLE2 + 104, 3, 0, // DO_ON_GUARD + 107, 7, 0, // DO_FIGHT_READY + 114, 2, 0, // DO_PUNCH + 116, 6, 0, // DO_KICK + 122, 2, 0, // DO_PUNCH_HIT1 + 124, 4, 0, // DO_PUNCH_HIT2 + 128, 4, 0, // DO_PUNCH_DEATH + 133, 2, 0, // DO_KICK_HIT1 + 135, 4, 0, // DO_KICK_HIT2 + 139, 5, 0, // DO_KICK_DEATH + 144, 3, 0, // DO_READY_WEAPON + 329, 8, 0, // DO_GUN_DEATH + 337, 8, 0, // DO_EXPLOSION_DEATH + 337, 8, 0, // DO_EXPLOSION2_DEATH + 345, 12,0, // DO_GRENADE_DEATH + 357, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 199, 6, 0, // DO_PULL_GUN + 237, 40,0, // DO_PLEAD + 277, 6, 0 // DO_PLEAD_DEATH +}; + +static InfantryTypeClass const C8( + INFANTRY_C8, // Infantry type number. + TXT_C8, // Translate name number for infantry type. + "C8", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + false, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 0, // Number of shots it has (default). + &CivilianDos8[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 5, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,1, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_NONE,WEAPON_NONE, + MPH_SLOW_ISH +); + + +int CivilianDos9[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 205, 4, 4, // DO_FIRE_PRONE + 189, 10,0, // DO_IDLE1 + 199, 6, 0, // DO_IDLE2 + 104, 3, 0, // DO_ON_GUARD + 107, 7, 0, // DO_FIGHT_READY + 114, 2, 0, // DO_PUNCH + 116, 6, 0, // DO_KICK + 122, 2, 0, // DO_PUNCH_HIT1 + 124, 4, 0, // DO_PUNCH_HIT2 + 128, 4, 0, // DO_PUNCH_DEATH + 133, 2, 0, // DO_KICK_HIT1 + 135, 4, 0, // DO_KICK_HIT2 + 139, 5, 0, // DO_KICK_DEATH + 144, 3, 0, // DO_READY_WEAPON + 329, 8, 0, // DO_GUN_DEATH + 337, 8, 0, // DO_EXPLOSION_DEATH + 337, 8, 0, // DO_EXPLOSION2_DEATH + 345, 12,0, // DO_GRENADE_DEATH + 357, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 199, 6, 0, // DO_PULL_GUN + 237, 40,0, // DO_PLEAD + 277, 6, 0 // DO_PLEAD_DEATH +}; + +static InfantryTypeClass const C9( + INFANTRY_C9, // Infantry type number. + TXT_C9, // Translate name number for infantry type. + "C9", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + false, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 0, // Number of shots it has (default). + &CivilianDos9[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 5, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,1, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_NONE,WEAPON_NONE, + MPH_SLOW_ISH +); + + +int NikoombaDos[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 205, 4, 4, // DO_FIRE_PRONE + 189, 10,0, // DO_IDLE1 + 199, 6, 0, // DO_IDLE2 + 104, 3, 0, // DO_ON_GUARD + 107, 7, 0, // DO_FIGHT_READY + 114, 2, 0, // DO_PUNCH + 116, 6, 0, // DO_KICK + 122, 2, 0, // DO_PUNCH_HIT1 + 124, 4, 0, // DO_PUNCH_HIT2 + 128, 4, 0, // DO_PUNCH_DEATH + 133, 2, 0, // DO_KICK_HIT1 + 135, 4, 0, // DO_KICK_HIT2 + 139, 5, 0, // DO_KICK_DEATH + 144, 3, 0, // DO_READY_WEAPON + 329, 8, 0, // DO_GUN_DEATH + 337, 8, 0, // DO_EXPLOSION_DEATH + 337, 8, 0, // DO_EXPLOSION2_DEATH + 345, 12,0, // DO_GRENADE_DEATH + 357, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 199, 6, 0, // DO_PULL_GUN + 237, 40,0, // DO_PLEAD + 277, 6, 0, // DO_PLEAD_DEATH + +}; + +// Nikoomba +static InfantryTypeClass const C10( + INFANTRY_C10, // Infantry type number. + TXT_C10, // Translate name number for infantry type. + "C10", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + false, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 0, // Number of shots it has (default). + &NikoombaDos[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 50, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,1, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_NONE,WEAPON_NONE, + MPH_SLOW_ISH +); + +int MoebiusDos[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 0, 0, 0, // DO_FIRE_PRONE + 104, 16,0, // DO_IDLE1 + 120, 20,0, // DO_IDLE2 + 0, 0, 0, // DO_ON_GUARD + 0, 0, 0, // DO_FIGHT_READY + 0, 0, 0, // DO_PUNCH + 0, 0, 0, // DO_KICK + 0, 0, 0, // DO_PUNCH_HIT1 + 0, 0, 0, // DO_PUNCH_HIT2 + 0, 0, 0, // DO_PUNCH_DEATH + 0, 0, 0, // DO_KICK_HIT1 + 0, 0, 0, // DO_KICK_HIT2 + 0, 0, 0, // DO_KICK_DEATH + 0, 0, 0, // DO_READY_WEAPON + 212, 8, 0, // DO_GUN_DEATH + 220, 8, 0, // DO_EXPLOSION_DEATH + 228, 12,0, // DO_EXPLOSION2_DEATH + 228, 12,0, // DO_GRENADE_DEATH + 240, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 0, 0, 0, // DO_PULL_GUN + 120, 31,0, // DO_PLEAD + 151, 14,0 // DO_PLEAD_DEATH +}; + +static InfantryTypeClass const Moebius( + INFANTRY_MOEBIUS, // Infantry type number. + TXT_MOEBIUS, // Translate name number for infantry type. + "MOEBIUS", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + false, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 0, // Number of shots it has (default). + &MoebiusDos[0][0], // ptr to DO table + 0, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 50, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,10, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_NONE,WEAPON_NONE, + MPH_SLOW_ISH +); + +int DelphiDos[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 205, 4, 4, // DO_FIRE_PRONE + 189, 10,0, // DO_IDLE1 + 199, 6, 0, // DO_IDLE2 + 104, 3, 0, // DO_ON_GUARD + 107, 7, 0, // DO_FIGHT_READY + 114, 2, 0, // DO_PUNCH + 116, 6, 0, // DO_KICK + 122, 2, 0, // DO_PUNCH_HIT1 + 124, 4, 0, // DO_PUNCH_HIT2 + 128, 4, 0, // DO_PUNCH_DEATH + 133, 2, 0, // DO_KICK_HIT1 + 135, 4, 0, // DO_KICK_HIT2 + 139, 5, 0, // DO_KICK_DEATH + 144, 3, 0, // DO_READY_WEAPON + 329, 8, 0, // DO_GUN_DEATH + 337, 8, 0, // DO_EXPLOSION_DEATH + 337, 8, 0, // DO_EXPLOSION2_DEATH + 345, 12,0, // DO_GRENADE_DEATH + 357, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 199, 6, 0, // DO_PULL_GUN + 237, 40,0, // DO_PLEAD + 277, 6, 0, // DO_PLEAD_DEATH +}; + +static InfantryTypeClass const Delphi( + INFANTRY_DELPHI, // Infantry type number. + TXT_DELPHI, // Translate name number for infantry type. + "DELPHI", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + false, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 10, // Number of shots it has (default). + &DelphiDos[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 25, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,0, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_PISTOL,WEAPON_NONE, + MPH_SLOW_ISH +); + + +int DrChanDos[DO_COUNT][3]={ + 0, 1, 1, // DO_STAND_READY + 0, 1, 1, // DO_STAND_GUARD + 0, 1, 1, // DO_PRONE // N/A + 56, 6, 6, // DO_WALK + 205, 4, 4, // DO_FIRE_WEAPON + 0, 1, 1, // DO_LIE_DOWN // N/A + 8, 6, 6, // DO_CRAWL + 0, 1, 1, // DO_GET_UP // N/A + 0, 0, 0, // DO_FIRE_PRONE + 104, 16,0, // DO_IDLE1 + 120, 20,0, // DO_IDLE2 + 0, 0, 0, // DO_ON_GUARD + 0, 0, 0, // DO_FIGHT_READY + 0, 0, 0, // DO_PUNCH + 0, 0, 0, // DO_KICK + 0, 0, 0, // DO_PUNCH_HIT1 + 0, 0, 0, // DO_PUNCH_HIT2 + 0, 0, 0, // DO_PUNCH_DEATH + 0, 0, 0, // DO_KICK_HIT1 + 0, 0, 0, // DO_KICK_HIT2 + 0, 0, 0, // DO_KICK_DEATH + 0, 0, 0, // DO_READY_WEAPON + 212, 8, 0, // DO_GUN_DEATH + 220, 8, 0, // DO_EXPLOSION_DEATH + 228, 12,0, // DO_EXPLOSION2_DEATH + 228, 12,0, // DO_GRENADE_DEATH + 240, 18,0, // DO_FIRE_DEATH + 0, 0, 0, // DO_GESTURE1 // N/A + 0, 0, 0, // DO_SALUTE1 // N/A + 0, 0, 0, // DO_GESTURE2 // N/A + 0, 0, 0, // DO_SALUTE2 // N/A + 0, 0, 0, // DO_PULL_GUN + 120, 31,0, // DO_PLEAD + 151, 14,0 // DO_PLEAD_DEATH +}; + +static InfantryTypeClass const DrChan( + INFANTRY_CHAN, // Infantry type number. + TXT_CHAN, // Translate name number for infantry type. + "CHAN", // INI name for infantry. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Is this a female type? + false, // Is a leader type? + false, // Has crawling animation frames? + true, // Is this a civlian? + true, // Always use the given name for the infantry? + true, // Is this a "fraidycat" run-away type infantry? + false, // Can this infantry type capture a building? + false, // Theater specific graphic image? + 10, // Number of shots it has (default). + &DrChanDos[0][0], // ptr to DO table + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 25, // Strength of infantry (in damage points). + 0, // Sight range. + 10, // Cost of infantry (in credits). + 99, // Scenario when they first appear. + 0,1, // Risk/Reward of this infantry unit. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_NEUTRAL, // Who can own this infantry unit. + WEAPON_NONE,WEAPON_NONE, + MPH_SLOW_ISH +); + + +/* +** This is the array of pointers to the static data associated with each +** infantry type. +*/ +InfantryTypeClass const * const InfantryTypeClass::Pointers[INFANTRY_COUNT] = { + &E1, + &E2, + &E3, + &E4, + &E5, +// &E6, + &E7, + &Commando, + &C1, + &C2, + &C3, + &C4, + &C5, + &C6, + &C7, + &C8, + &C9, + &C10, + &Moebius, + &Delphi, + &DrChan +}; + + +/*********************************************************************************************** + * InfantryTypeClass::InfantryTypeClass -- Constructor for infantry type class objects. * + * * + * This routine will construct the infantry type objects. It is use to create the static * + * infantry types that are used to give each of the infantry objects their characteristics. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +InfantryTypeClass::InfantryTypeClass ( + InfantryType type, int name, char const *ininame, + unsigned char level, long pre, + bool is_female, + bool is_leader, + bool is_crawling, + bool is_civilian, + bool is_nominal, + bool is_fraidycat, + bool is_capture, + bool is_theater, + int ammo, + int *do_table, + int firelaunch, int pronelaunch, + unsigned short strength, int sightrange, + int cost, int scenario, int risk, int reward, int ownable, + WeaponType primary, WeaponType secondary, + MPHType maxspeed) + : TechnoTypeClass(name, ininame, level, pre, + is_leader, true, is_nominal, false, false, true, true, true, true, false, false, + is_theater, false, false, false, true, false, + ammo, strength, maxspeed, sightrange, cost, + scenario, risk, reward, ownable, + primary, secondary, + ARMOR_NONE) +{ + IsFemale = is_female; + IsCrawling = is_crawling; + IsCapture = is_capture; + IsFraidyCat = is_fraidycat; + IsCivilian = is_civilian; + Type = type; + FireLaunch = firelaunch; + ProneLaunch = pronelaunch; + + /* + ** Set the animation sequence custom values. + */ + + for ( int i=0 ; iClass->House)); +} + + +/*********************************************************************************************** + * InfantryTypeClass::Create_And_Place -- Creates and places infantry object onto the map. * + * * + * This routine is used by the scenario editor to create and place an infantry object onto * + * the map at the location specified. * + * * + * INPUT: cell -- The cell location to place the infantry object at. * + * * + * house -- The owner of the infantry object. * + * * + * OUTPUT: bool; Was the infantry object successfully created and placed at the location * + * specified? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryTypeClass::Create_And_Place(CELL cell, HousesType house) const +{ + InfantryClass * i = new InfantryClass(Type, house); + if (i) { + COORDINATE coord = Map[cell].Closest_Free_Spot(Cell_Coord(cell)); + if (coord) { + return(i->Unlimbo(coord, DIR_E)); + } else { + delete i; + } + } + return(false); +} + + +/*********************************************************************************************** + * InfantryTypeClass::Occupy_List -- Returns with default infantry occupation list. * + * * + * This routine will return with a cell offset occupation list for a generic infantry * + * object. This is typically just a single cell since infantry are never bigger than one * + * cell and this routine presumes the infantry is located in the center of the cell. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a cell offset list for the infantry object as if it were located * + * in the center of a cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +short const * InfantryTypeClass::Occupy_List(bool) const +{ + static short const _list[] = {0, REFRESH_EOL}; + + return(&_list[0]); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * InfantryTypeClass::Display -- Displays a generic infantry object. * + * * + * This routine is used by the scenario editor to display a generic representation of the * + * infantry object for the scenario editor. It simply draws a single (nice profile) view * + * of the infantry type. * + * * + * INPUT: x,y -- The display coordinates to render the infantry object at. * + * * + * window -- The window that the display coordinates are relative to. * + * * + * house -- The house colors to use when rendering this infantry object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryTypeClass::Display(int x, int y, WindowNumberType window, HousesType house) const +{ + if (house != HOUSE_NONE) { + + int shape = 0; + void const * ptr = Get_Cameo_Data(); + if (!ptr) { + ptr = Get_Image_Data(); + shape = 2; + } + + CC_Draw_Shape(ptr, shape, x, y, window, SHAPE_NORMAL|SHAPE_CENTER|SHAPE_WIN_REL); + } +} + + +/*********************************************************************************************** + * InfantryTypeClass::Prep_For_Add -- Prepares the scenario editor for adding of infantry object.* + * * + * This routine will prepare the scenario editor so that the infantry objects appear on * + * the object list. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryTypeClass::Prep_For_Add(void) +{ + for (InfantryType index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) { + Map.Add_To_List(&As_Reference(index)); + } +} +#endif + + +/*********************************************************************************************** + * InfantryTypeClass::From_Name -- Converts an ASCII name into an infantry type number. * + * * + * This routine is used to convert the infantry ASCII name as specified into an infantry * + * type number. This is called from the INI reader routine in the process if creating the * + * infantry objects needed for the scenario. * + * * + * INPUT: name -- The ASCII name to convert into an infantry type number. * + * * + * OUTPUT: Returns with the infantry type number that corresponds to the infantry ASCII name * + * specified. If no match could be found, then INFANTRY_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +InfantryType InfantryTypeClass::From_Name(char const *name) +{ + if (name) { + for (InfantryType classid = INFANTRY_FIRST; classid < INFANTRY_COUNT; classid++) { + if (stricmp(Pointers[classid]->IniName, name) == 0) { + return(classid); + } + } + } + return(INFANTRY_NONE); +} + + +/*********************************************************************************************** + * InfantryTypeClass::One_Time -- Performs any one time processing for infantry system. * + * * + * This routine will perform one time processing for the infantry type system. This is * + * generally restricted to loading of the infantry shape data. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryTypeClass::One_Time(void) +{ + InfantryType index; + + for (index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + InfantryTypeClass const *uclass; + CCFileClass file; + + uclass = &As_Reference(index); + + /* + ** Generic shape for all houses load method. + */ + _makepath(fullname, NULL, NULL, uclass->IniName, ".SHP"); + ((void const *&)uclass->ImageData) = MixFileClass::Retrieve(fullname); + + /* + ** The small build image icon sized shapes are always generic. + */ + char buffer[_MAX_FNAME]; + if ( Get_Resolution_Factor() ) { + sprintf(buffer, "%.4sICNH", uclass->IniName); + } else { + sprintf(buffer, "%.4sICON", uclass->IniName); + } + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + ((void const *&)uclass->CameoData) = MixFileClass::Retrieve(fullname); + } +} + + + + + + +/*********************************************************************************************** + * ITC::Init -- load up terrain set dependant sidebar icons * + * * + * * + * * + * INPUT: theater type * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 4/25/96 0:33AM ST : Created * + *=============================================================================================*/ + +void InfantryTypeClass::Init(TheaterType theater) +{ + if ( Get_Resolution_Factor() ) { + + if (theater != LastTheater){ + InfantryType index; + char buffer[_MAX_FNAME]; + char fullname[_MAX_FNAME+_MAX_EXT]; + void const * cameo_ptr; + + for (index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) { + InfantryTypeClass const *uclass; + CCFileClass file; + + uclass = &As_Reference(index); + + ((void const *&)uclass->CameoData) = NULL; + + sprintf(buffer, "%.4sICNH", uclass->IniName); + _makepath (fullname, NULL, NULL, buffer, Theaters[theater].Suffix); + cameo_ptr = MixFileClass::Retrieve(fullname); + if (cameo_ptr){ + ((void const *&)uclass->CameoData) = cameo_ptr; + } + } + } + } +} + + + + + + + + + +/*********************************************************************************************** + * InfantryTypeClass::Who_Can_Build_Me -- Determines what can build the infantry object. * + * * + * Use this routine to determine what building can produce the infantry object. This is * + * typically used to maintain the construction list sidebar. * + * * + * INPUT: intheory -- If no regard is to be given to whether the construction building is * + * currently busy, then this flag should be true. In such a case, only * + * the existance of the building is sufficient to achieve success. * + * * + * legal -- Should building prerequisite legality checks be performed as well? * + * For building placements, this is usually false. For sidebar button * + * adding, this is usually true. * + * * + * house -- The house of the infantry to be produced. Only construction buildings * + * of the same house are considered. * + * * + * OUTPUT: Returns with a pointer to the object (building) that can produce the infantry * + * type. If there are no available buildings then the return value is NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/29/1994 JLB : Created. * + *=============================================================================================*/ +BuildingClass * InfantryTypeClass::Who_Can_Build_Me(bool intheory, bool legal, HousesType house) const +{ + BuildingClass * anybuilding = NULL; + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + if (building && + !building->IsInLimbo && + building->House->Class->House == house && + building->Mission != MISSION_DECONSTRUCTION && + building->Class->ToBuild == RTTI_INFANTRYTYPE && + ((1L << building->ActLike) & Ownable) && + (!legal || building->House->Can_Build(Type, building->ActLike)) && + (intheory || !building->In_Radio_Contact())) { + + anybuilding = building; + if (building->IsLeader) return(building); + } + } + return(anybuilding); +} + + +/*********************************************************************************************** + * InfantryTypeClass::Full_Name -- Fetches the full name text number. * + * * + * This routine will fetch the full name text number for this infantry type. It examines * + * the special custom name flag to determine whether the custom name or the generic name * + * is to be used. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with text number for the name to give this infantry type object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/29/1995 JLB : Created. * + *=============================================================================================*/ +int InfantryTypeClass::Full_Name(void) const +{ + if (Debug_Map || !IsNominal || Special.IsNamed || Type == INFANTRY_C10 || Type == INFANTRY_DELPHI || Type == INFANTRY_MOEBIUS) { + return(TechnoTypeClass::Full_Name()); + } + return(TXT_CIVILIAN); +} diff --git a/INFANTRY.CPP b/INFANTRY.CPP new file mode 100644 index 0000000..939cf9f --- /dev/null +++ b/INFANTRY.CPP @@ -0,0 +1,3221 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\infantry.cpv 2.19 16 Oct 1995 16:50:30 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INFANTRY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 15, 1994 * + * * + * Last Update : August 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * InfantryClass::AI -- Handles the infantry non-graphic related AI processing. * + * InfantryClass::Active_Click_With -- Handles action when clicking with infantry soldier. * + * InfantryClass::As_Target -- Converts the infantry unit into a target value. * + * InfantryClass::Assign_Destination -- Gives the infantry a movement destination. * + * InfantryClass::Assign_Mission -- Make sure he's out of boxing mode first * + * InfantryClass::Assign_Target -- Gives the infantry a combat target. * + * InfantryClass::Can_Enter_Cell -- Determines if the infantry can enter the cell specified. * + * InfantryClass::Can_Fire -- Can the infantry fire its weapon? * + * InfantryClass::Clear_Occupy_Bit -- Clears occupy bit and given cell * + * InfantryClass::Debug_Dump -- Displays debug information about infantry unit. * + * InfantryClass::Detach -- Removes the specified target from targeting computer. * + * InfantryClass::Do_Action -- Launches the infantry into an animation sequence. * + * InfantryClass::Draw_It -- Draws a unit object. * + * InfantryClass::Enter_Idle_Mode -- The infantry unit enters idle mode by this routine. * + * InfantryClass::Fire_At -- Fires projectile from infantry unit. * + * InfantryClass::Fire_Coord -- Calculates the origin point for projectiles fired. * + * InfantryClass::Greatest_Threat -- Determines greatest threat (target) for infantry unit. * + * InfantryClass::InfantryClass -- The constructor for infantry objects. * + * InfantryClass::Init -- Initialize the infantry object system. * + * InfantryClass::Limbo -- Performs cleanup operations needed when limboing. * + * InfantryClass::Look -- The infantry performs a look operation. * + * InfantryClass::Made_A_Kill -- Marks a kill caused by this infantry soldier. * + * InfantryClass::Overlap_List -- The list of cells that the infantry overlaps, but doesn't occ* + * InfantryClass::Per_Cell_Process -- Handles special operations that occur once per cell. * + * InfantryClass::Random_Animate -- Randomly animate the infantry (maybe) * + * InfantryClass::Read_INI -- Reads units from scenario INI file. * + * InfantryClass::Rearm_Delay -- Return Arming delay for infantry if boxing * + * InfantryClass::Receive_Message -- Process radio messages * + * InfantryClass::Response_Attack -- Plays infantry audio response to attack order. * + * InfantryClass::Response_Move -- Plays infantry response to movement order. * + * InfantryClass::Response_Select -- Plays infantry audio response due to being selected. * + * InfantryClass::Scatter -- Causes the infantry to scatter to nearby cell. * + * InfantryClass::Set_Occupy_Bit -- Sets the occupy bit cell and bit pos * + * InfantryClass::Set_Primary_Facing -- Change infantry primary facing -- always and instantl* + * InfantryClass::Start_Driver -- Handles giving immediate destination and move orders. * + * InfantryClass::Stop_Driver -- Stops the infantry from moving any further. * + * InfantryClass::Take_Damage -- Applies damage to the infantry unit. * + * InfantryClass::Unlimbo -- Unlimbo infantry unit in legal sub-location. * + * InfantryClass::What_Action -- Infantry units might be able to capture -- check. * + * InfantryClass::Write_INI -- Writes all the infantry out to an INI file. * + * InfantryClass::operator delete -- Returns the infantry object back to the free pool * + * InfantryClass::operator new -- Allocates an infantry object from the free pool. * + * InfantryClass::~InfantryClass -- Default destructor for infantry units. * + * InfantryClass::Full_Name -- Fetches the full name of the infantry unit. * + * InfantryClass::Mission_Attack -- Intercept attack mission for special handling. * + * InfantryClass::Validate -- validates infantry pointer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +int const InfantryClass::HumanShape[32] = {0,0,7,7,7,7,6,6,6,6,5,5,5,5,5,4,4,4,3,3,3,3,2,2,2,2,1,1,1,1,1,0}; + +int Infantry_Kick_Damage[] = {10,15}; +int Infantry_Punch_Damage[] = { 4, 7}; + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * InfantryClass::VTable; + + +/*************************************************************************** +** This is the array of constant data associated with infantry maneuvers. It +** specifies the frame rate as well as if the animation can be aborted. +*/ +// interruptable, mobile, randomstart, rate +DoStruct const InfantryClass::MasterDoControls[DO_COUNT] = { + {true, false, false, 0}, // DO_STAND_READY + {true, false, false, 0}, // DO_STAND_GUARD + {true, false, false, 0}, // DO_PRONE + {true, true, true, 2}, // DO_WALK + {true, false, false, 1}, // DO_FIRE_WEAPON + {false, true, false, 2}, // DO_LIE_DOWN + {true, true, true, 2}, // DO_CRAWL + {false, false, false, 3}, // DO_GET_UP + {true, false, false, 1}, // DO_FIRE_PRONE + {true, false, false, 2}, // DO_IDLE1 + {true, false, false, 2}, // DO_IDLE2 + {false, false, false, 2}, // DO_ON_GUARD + {true, false, false, 2}, // DO_FIGHT_READY + {false, false, false, 2}, // DO_PUNCH + {false, false, false, 2}, // DO_KICK + {false, false, false, 2}, // DO_PUNCH_HIT1 + {false, false, false, 2}, // DO_PUNCH_HIT2 + {false, false, false, 1}, // DO_PUNCH_DEATH + {false, false, false, 2}, // DO_KICK_HIT1 + {false, false, false, 2}, // DO_KICK_HIT2 + {false, false, false, 1}, // DO_KICK_DEATH + {false, false, false, 2}, // DO_READY_WEAPON + {false, false, false, 2}, // DO_GUN_DEATH + {false, false, false, 2}, // DO_EXPLOSION_DEATH + {false, false, false, 2}, // DO_EXPLOSION2_DEATH + {false, false, false, 2}, // DO_GRENADE_DEATH + {false, false, false, 2}, // DO_FIRE_DEATH + {false, false, false, 2}, // DO_GESTURE1 + {false, false, false, 2}, // DO_SALUTE1 + {false, false, false, 2}, // DO_GESTURE2 + {false, false, false, 2}, // DO_SALUTE2 + {true, false, false, 2}, // DO_PULL_GUN + {true, false, false, 2}, // DO_PLEAD + {true, false, false, 2}, // DO_PLEAD_DEATH +}; + + +/*********************************************************************************************** + * InfantryClass::Validate -- validates infantry pointer. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int InfantryClass::Validate(void) const +{ + int num; + + num = Infantry.ID(this); + if (num < 0 || num >= INFANTRY_MAX) { + Validate_Error("INFANTRY"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * InfantryClass::Debug_Dump -- Displays debug information about infantry unit. * + * * + * This routine is used by the debug version to display pertinent information about the * + * infantry unit. * + * * + * INPUT: mono -- The monochrome screen to display the debug information to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Debug_Dump(MonoClass *mono) const +{ + Validate(); + mono->Set_Cursor(0, 0);mono->Print( + "ÚName:ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂMission:ÄÄÄÂTarCom:ÂNavCom:ÂRadio:ÂCoord:ÄÄÂHeadTo:ÄÂSt:Ä¿\n" + "³ ³ ³ ³ ³ ³ ³ ³ ³\n" + "ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂNÂYÂHealth:ÄÂBody:ÂTurret:ÂSpeed:ÂPath:ÁÄÄÄÄÄÄÂCargo:ÄÄÄÄÁÄÄÄÄ´\n" + "³Active........³ ³ ³ ³ ³ ³ ³ ³ ³\n" + "³Limbo.........³ ³ ÃÄÄÄÄÄÄÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n" + "³Owned.........³ ³ ³Last Message: ³\n" + "³Discovered....³ ³ ÃTimer:ÂArm:ÂTrack:ÂTiberium:ÂFlash:ÂStage:ÂTeam:ÄÄÄÄÂArch:´\n" + "³Selected......³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³\n" + "³Teathered.....³ ³ ÃÄÄÄÄÄÄÁÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÙ\n" + "³Locked on Map.³ ³ ³ \n" + "³Is Prone......³ ³ ³ \n" + "³Is A Loner....³ ³ ³ \n" + "³Deploying.....³ ³ ³ \n" + "³Rotating......³ ³ ³ \n" + "³Firing........³ ³ ³ \n" + "³Driving.......³ ³ ³ \n" + "³To Look.......³ ³ ³ \n" + "³Recoiling.....³ ³ ³ \n" + "³To Display....³ ³ ³ \n" + "ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÁÄÙ \n"); + mono->Set_Cursor(1, 1);mono->Printf("%s:%s", House->Class->IniName, Class->IniName); + mono->Text_Print("X", 16 + (IsProne?2:0), 10); + mono->Set_Cursor(33, 7);mono->Printf("%2d", Fear); + mono->Set_Cursor(41, 7);mono->Printf("%2d", Doing); + FootClass::Debug_Dump(mono); +} +#endif + +InfantryClass::InfantryClass(void) : Class(0) {}; // Default constructor does nothing. + + +/*********************************************************************************************** + * InfantryClass::InfantryClass -- The constructor for infantry objects. * + * * + * This is the constructor used when creating an infantry unit. All values are required * + * except for facing and position. If these are absent, then the infantry is created in * + * a state of limbo -- not placed upon the map. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +InfantryClass::InfantryClass(InfantryType classid, HousesType house) : + Class(&InfantryTypeClass::As_Reference(classid)), + FootClass(house) +{ + + /* + ** For two shooters, clear out the second shot flag -- it will be set the first time + ** the object fires. For non two shooters, set the flag since it will never be cleared + ** and the second shot flag tells the system that normal rearm times apply -- this is + ** what is desired for non two shooters. + */ + if (Class->IsTwoShooter) { + IsSecondShot = false; + } else { + IsSecondShot = true; + } + Doing = DO_NOTHING; + Fear = 0; // Starts completely brave. + IsProne = false; + IsStoked = false; + IsBoxing = false; + IsTechnician = false; + Strength = Class->MaxStrength; + + /* + ** Civilians carry much less ammo than soldiers do. + */ + Ammo = Class->MaxAmmo; + + /* + ** Keep count of the number of units created. Dont track civilians. + */ + if (!Class->IsCivilian && GameToPlay == GAME_INTERNET){ + House->InfantryTotals->Increment_Unit_Total((int)classid); + } + +} + + +/*********************************************************************************************** + * InfantryClass::~InfantryClass -- Default destructor for infantry units. * + * * + * This is the default destructor for infantry type units. It will put the infantry into * + * a limbo state if it isn't already in that state and the game is still active. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/10/1995 JLB : Created. * + *=============================================================================================*/ +InfantryClass::~InfantryClass(void) +{ + if (GameActive && Class) { + Limbo(); + } + if (GameActive && Team) Team->Remove(this); +} + + +/*********************************************************************************************** + * InfantryClass::operator new -- Allocates an infantry object from the free pool. * + * * + * This will allocate an infantry object from the infantry object free pool. If there is * + * no available slot, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocated infantry object or NULL if none could be * + * allocated. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void * InfantryClass::operator new(size_t) +{ + void * ptr = Infantry.Allocate(); + if (ptr) { + ((InfantryClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * InfantryClass::operator delete -- Returns the infantry object back to the free pool * + * * + * This routine is used return an infantry object back to the system. * + * * + * INPUT: ptr -- Pointer to the infantry object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::operator delete(void *ptr) +{ + if (ptr) { + ((InfantryClass *)ptr)->IsActive = false; + } + Infantry.Free((InfantryClass *)ptr); +} + + +/*********************************************************************************************** + * InfantryClass::Take_Damage -- Applies damage to the infantry unit. * + * * + * This routine applies the damage specified to the infantry object. It is possible that * + * this routine will DESTROY the infantry unit in the process. * + * * + * INPUT: damage -- The damage points to inflict. * + * * + * distance -- The distance from the damage center point to the object's center point.* + * * + * warhead -- The warhead type that is inflicting the damage. * + * * + * source -- Who is responsible for inflicting the damage. * + * * + * OUTPUT: bool; Was the infantry unit destroyed by this damage? * + * * + * WARNINGS: Since the infantry unit could be destroyed by this routine, be sure to check * + * for this in the code that follows the call to Take_Damage(). * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + * 11/22/1994 JLB : Shares base damage handler for techno objects. * + * 03/31/1995 JLB : Revenge factor. * + *=============================================================================================*/ +ResultType InfantryClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source) +{ + Validate(); + ResultType res = RESULT_NONE; + + IsFiring = false; + + /* + ** Prone infantry take only half damage, but never below one damage point. + */ + if (IsProne && damage) { + damage >>= 1; +// damage = MAX(damage, 1); + } + + +//Mono_Printf("Infantry Take_Damage(%d, %d, %d, %p)\r", damage, distance, warhead, source); +//Get_Key(); + + res = FootClass::Take_Damage(damage, distance, warhead, source); + + /* + ** Flame thrower guys take more damage because of the exposed pilot light + ** on their flame gun. + */ + if (damage && res != RESULT_DESTROYED && *this == INFANTRY_E4) { + damage = 5; + ResultType newres = FootClass::Take_Damage(damage, distance, warhead, source); + res = MAX(res, newres); + } + + if (res == RESULT_NONE) return(res); + + if (res == RESULT_DESTROYED) { + Death_Announcement(source); + Stop_Driver(); + Stun(); + Mission = MISSION_NONE; + Assign_Mission(MISSION_GUARD); + Commence(); + + /* + ** Flame thrower infantry always go out with a bang. + */ + if (*this == INFANTRY_E4) { + new AnimClass(ANIM_NAPALM1, Coord); + Explosion_Damage(Coord, 80, 0, WARHEAD_FIRE); + } + + if (*this == INFANTRY_E2) { + new AnimClass(ANIM_ART_EXP1, Coord); + Explosion_Damage(Coord, 30, 0, WARHEAD_HE); + } + + if (*this == INFANTRY_E5) { + new AnimClass(ANIM_CHEM_BALL, Coord); + Explosion_Damage(Coord, 80, 0, WARHEAD_HE); + } + + VocType sound; + VocType altsound; + if (*this == INFANTRY_RAMBO) { +// if (Sim_Random_Pick(0, 3) != 1) { + sound = VOC_RAMBO_YELL; +// } else { +// sound = VOC_RAMBO_OHSH; +// } + altsound = sound; + } else { + sound = Sim_Random_Pick(VOC_SCREAM1, VOC_SCREAM5); + altsound = VOC_YELL1; + } + + /* + ** The type of warhead determines the animation the infantry + ** will perform when killed. + */ + switch (warhead) { + case WARHEAD_FEEDME: + if (source) { + source->Strength += 30; + if (source->Strength > source->Class_Of().MaxStrength) { + source->Strength = source->Class_Of().MaxStrength; + } + } + // Fall thru to WARHEAD_SA: + + case WARHEAD_HEADBUTT: + case WARHEAD_SPORE: + case WARHEAD_HOLLOW_POINT: + case WARHEAD_SA: + Sound_Effect(sound, Coord); + Do_Action(DO_GUN_DEATH, true); + break; + + case WARHEAD_HE: + Sound_Effect(sound, Coord); + Do_Action(DO_EXPLOSION_DEATH, true); + break; + + case WARHEAD_AP: + Sound_Effect(sound, Coord); + Do_Action(DO_GRENADE_DEATH, true); + break; + + case WARHEAD_PB: + case WARHEAD_LASER: + case WARHEAD_FIRE: + Sound_Effect(altsound, Coord); + Do_Action(DO_FIRE_DEATH, true); + break; + + case WARHEAD_FIST: + Sound_Effect(sound, Coord); + Do_Action(DO_PUNCH_DEATH,true); + break; + + case WARHEAD_FOOT: + Sound_Effect(sound, Coord); + Do_Action(DO_KICK_DEATH,true); + break; + } + + return(res); + } + + /* + ** When infantry gets hit, it gets scared. + */ + if (res != RESULT_DESTROYED) { + COORDINATE c4 = (source) ? source->Coord : NULL; + if (source) { + Scatter(c4); + } + +#ifdef BOXING + if (IsBoxing) { + int addval = 0; + + switch (warhead) { + case WARHEAD_FIST: + if (damage == Infantry_Punch_Damage[1]) addval++; + Do_Action( (DoType) ( (int)DO_PUNCH_HIT1 + addval),true); + break; + + case WARHEAD_FOOT: + if (damage == Infantry_Kick_Damage[1]) addval++; + Do_Action( (DoType) ( (int)DO_KICK_HIT1 + addval),true); + break; + } + } else { +#endif + if (source && Fear < FEAR_SCARED) { + if (Class->IsFraidyCat) { + Fear = FEAR_PANIC; + } else { + Fear = FEAR_SCARED; + } + } else { + int morefear = FEAR_ANXIOUS; + if (Health_Ratio() > 0x0080) morefear /= 4; + Fear = MIN((int)Fear + morefear, FEAR_MAXIMUM); + } +#ifdef BOXING + } +#endif + } + return(res); +} + + +/*********************************************************************************************** + * InfantryClass::Draw_It -- Draws a unit object. * + * * + * This routine is the one that actually draws a unit object. It displays the unit * + * according to its current state flags and centered at the location specified. * + * * + * INPUT: x,y -- The X and Y coordinate of where to draw the unit. * + * * + * window -- The clipping window to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + * 06/27/1994 JLB : Takes a window parameter. * + * 08/15/1994 JLB : Converted to infantry support. * + *=============================================================================================*/ +void InfantryClass::Draw_It(int x, int y, WindowNumberType window) +{ + Validate(); + void const * shapefile; // Working shape file pointer. + int facing = Facing_To_32(PrimaryFacing.Current()); + + /* + ** Verify the legality of the unit class. + */ + shapefile = Class->Get_Image_Data(); + if (!shapefile) return; + + y += 4; + x -= 2; + + /* + ** Fetch the basic body shape pointer. This requires taking into account + ** the current animation stage. + */ + int shapenum; + int facenum; + + shapenum = 0; + facenum = HumanShape[facing]; + + /* + ** Fetch the shape pointer to use for the infantry. This is controlled by what + ** choreograph sequence the infantry is performing, it's facing, and whether it + ** is prone. + */ + DoType doit = Doing; + if (doit == DO_NOTHING) doit = DO_STAND_READY; + + shapenum = Class->DoControls[doit].Count; + shapenum = Fetch_Stage() % MAX(shapenum, 1); + if (Class->DoControls[doit].Jump) { + shapenum += facenum * Class->DoControls[doit].Jump; + } + shapenum += Class->DoControls[doit].Frame; + +#ifdef BOXING +// BG hack to get him to face right when he's supposed to. + if (IsBoxing && BodyFacing<128) shapenum += 47; +#endif + + /* + ** Actually draw the root body of the unit. + */ + Techno_Draw_Object(shapefile, shapenum, x, y, window); +// CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, House->Remap_Table(IsBlushing, true), Map.UnitShadow); + + FootClass::Draw_It(x, y, window); +} + + +/*********************************************************************************************** + * InfantryClass::Per_Cell_Process -- Handles special operations that occur once per cell. * + * * + * This routine will handle any special operations that need to be performed once each * + * cell travelled. This includes radioing a transport that is is now clear and the * + * transport is free to leave. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + * 03/01/1995 JLB : Capture building options. * + * 05/31/1995 JLB : Capture is always successful now. * + *=============================================================================================*/ +void InfantryClass::Per_Cell_Process(bool center) +{ + Validate(); + CellClass *cellptr = &Map[Coord_Cell(Coord)]; + + /* + ** If the infantry unit is entering a cell that contains the building it is trying to + ** capture, then capture it. + */ + if (center && Mission == MISSION_CAPTURE) { + TechnoClass * tech = cellptr->Cell_Techno(); + if (tech && tech->As_Target() == NavCom) { + tech->Captured(House); + delete this; + return; + } else { +//#ifdef NEVER + if (!Target_Legal(NavCom)) { + Enter_Idle_Mode(); + if (Map[Coord_Cell(Coord)].Cell_Building()) { + Scatter(0, true); + } + } +//#endif + } + } + + /* + ** Infantry entering a transport vehicle will break radio contact + ** at attach itself to the transporter. + */ + TechnoClass * techno = Contact_With_Whom(); + if (center && Mission == MISSION_ENTER && techno && Coord_Cell(Coord) == Coord_Cell(techno->Coord) && techno == As_Techno(NavCom)) { + if (Transmit_Message(RADIO_IM_IN) == RADIO_ATTACH) { + Limbo(); + techno->Attach(this); + } + return; + } + + /* + ** If the infantry unit is entering a cell that contains the building it is trying to + ** sabotage, then sabotage it. + */ + if (center && Mission == MISSION_SABOTAGE) { + BuildingClass *building = cellptr->Cell_Building(); + if (building && building->As_Target() == NavCom) { + int temp = Special.IsScatter; + + building->IsGoingToBlow = true; + building->Clicked_As_Target(20); + building->CountDown.Set(20); + building->WhomToRepay = As_Target(); + Special.IsScatter = true; + NavCom = TARGET_NONE; + Do_Uncloak(); + Arm = Rearm_Delay(true); + Scatter(building->Center_Coord(), true); // RUN AWAY! + Special.IsScatter = temp; + return; + } + } + + /* + ** If this unit is on a teather, then cut it at this time so that + ** the "parent" unit is free to proceed. Note that the parent + ** unit might actually be a building. + */ + if (center && IsTethered) { + Transmit_Message(RADIO_UNLOADED); + if (House->Class->House == HOUSE_GOOD) { + Do_Action(DO_GESTURE1); + } else { + Do_Action(DO_GESTURE2); + } + + /* + ** Rambo types give a gung-ho comment when unloaded. + */ + if (*this == INFANTRY_RAMBO) { + Sound_Effect(VOC_RAMBO_ROCK, Coord); + } + + /* + ** If the cell is now full of infantry, tell them all to scatter + ** in order to make room for more. + */ + if ((cellptr->Flag.Composite & 0x01F) == 0x01F) { + cellptr->Incoming(0, true); + } + } + + /* + ** When the infantry reaches the center of the cell, it may begin a new mission. + */ + if (center) { + Commence(); + } + + Look(true); + FootClass::Per_Cell_Process(center); + + /* + ** If over Tiberium, then this infantry unit will take damage. + */ + if (IsActive && !IsInLimbo && center && cellptr->Land_Type() == LAND_TIBERIUM && *this != INFANTRY_E5) { + int damage = 2; + Take_Damage(damage, 0, WARHEAD_FIRE); + } +} + + +/*********************************************************************************************** + * InfantryClass::Detach -- Removes the specified target from targeting computer. * + * * + * This is a support routine that removes the target specified from any targeting or * + * navigation computers. When a target is destroyed or removed from the game system, * + * the target must be removed from any tracking systems of the other units. This routine * + * handles removal for infantry units. * + * * + * INPUT: target -- The target to remove from the infantry unit's tracking systems. * + * * + * all -- Is the target going away for good as opposed to just cloaking/hiding? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Detach(TARGET target, bool all) +{ + Validate(); + if (TarCom == target) { + IsFiring = false; + } + FootClass::Detach(target, all); +} + + +/*********************************************************************************************** + * InfantryClass::As_Target -- Converts the infantry unit into a target value. * + * * + * This support routine is used to convert the infantry object (as a pointer) into a target * + * value (which is a number). * + * * + * INPUT: none * + * * + * OUTPUT: Returns the infantry unit as a target value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +TARGET InfantryClass::As_Target(void) const +{ + Validate(); + return(Build_Target(KIND_INFANTRY, Infantry.ID(this))); +} + + +/*********************************************************************************************** + * InfantryClass::Init -- Initialize the infantry object system. * + * * + * This routine will force the infantry object system into its empty initial state. It * + * is called when the scenario needs to be cleared in preparation for a scenario load. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Init(void) +{ + InfantryClass *ptr; + + Infantry.Free_All(); + + ptr = new InfantryClass(); + VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0]; + delete ptr; +} + + +/*********************************************************************************************** + * InfantryClass::Look -- The infantry performs a look operation. * + * * + * This routine will cause the infantry unit to "look". For player owned infantry, this * + * causes the dark shroud to be pushed back. * + * * + * INPUT: incremental -- If it is known that the infantry performed a look in the last cell * + * it was in AND it has only moved one cell, then setting this * + * parameter to true will perform a faster "incremental" look * + * operation. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is a relatively slow routine. Call ONLY when necessary. * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Look(bool incremental) +{ + Validate(); + int sight; // Number of cells to sight. + + if (!IsInLimbo) { + if (IsOwnedByPlayer) { + sight = Class->SightRange; + + if (sight) { + Map.Sight_From(Coord_Cell(Coord), sight, incremental); + } + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Assign_Destination -- Gives the infantry a movement destination. * + * * + * This routine updates the infantry's navigation computer so that the infantry will * + * travel to the destination target specified. * + * * + * INPUT: target -- The target to have the infantry unit move to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Assign_Destination(TARGET target) +{ + Validate(); + /* + ** Special flag so that infantry will start heading in the right direction immediately. + */ + if (Target_Legal(target)) { + Stop_Driver(); + } + + /* + ** When telling an infantry soldier to move to a location twice, then this + ** means that movement is more important than safety. Get up and run! + */ + if (House->IsHuman && Target_Legal(target) && NavCom == target && IsProne && !Class->IsFraidyCat) { + Do_Action(DO_GET_UP); + } + + /* + ** Handle entry logic here. + */ + if (Mission == MISSION_ENTER || MissionQueue == MISSION_ENTER) { + + /* + ** If not already in radio contact (presumed with the transport), then + ** either try to establish contact if allowed, or just move close and + ** wait until radio contact can be established. + */ + if (!In_Radio_Contact()) { + TechnoClass * techno = As_Techno(target); + + if (techno) { + + /* + ** Determine if the transport is already in radio contact. If so, then just move + ** toward the transport and try to establish contact at a later time. + */ + if (techno->In_Radio_Contact()) { + +// TCTCTC -- call for an update from the transport to get a good rondezvous position. + + ArchiveTarget = target; + } else { + if (Transmit_Message(RADIO_HELLO, techno) == RADIO_ROGER) { + if (Transmit_Message(RADIO_DOCKING) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT); + Assign_Mission(MISSION_MOVE); + } + } else { + Assign_Mission(MISSION_MOVE); + } + } + } + } else { + Path[0] = FACING_NONE; + } + } else { + Path[0] = FACING_NONE; + } + FootClass::Assign_Destination(target); +} + + +/*********************************************************************************************** + * InfantryClass::Assign_Target -- Gives the infantry a combat target. * + * * + * This routine will update the infantry's targeting computer so that it will try to * + * attack the target specified. This might result in it moving to be within range and thus * + * also cause adjustment of the navigation computer. * + * * + * INPUT: target -- The target that this infantry should attack. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + * 06/30/1995 JLB : Tries to capture target if possible. * + *=============================================================================================*/ +void InfantryClass::Assign_Target(TARGET target) +{ + Validate(); + Path[0] = FACING_NONE; + FootClass::Assign_Target(target); + + /* + ** If this is an infantry that can only capture, then also assign its destination to the + ** target specified. + */ + if (!Target_Legal(NavCom) && Class->IsCapture && Class->Primary == WEAPON_NONE) { + BuildingClass const * building = As_Building(target); + if (building && building->Class->IsCaptureable && (GameToPlay != GAME_NORMAL || *building != STRUCT_EYE && Scenario < 13)) { + Assign_Destination(target); + } + } +} + + +/*********************************************************************************************** + * InfantryClass::AI -- Handles the infantry non-graphic related AI processing. * + * * + * This routine is used to handle the non-graphic AI processing the infantry requires. * + * Call this routine ONCE per game frame. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::AI(void) +{ + Validate(); + FootClass::AI(); + + if (IsUnloading) Mark(MARK_CHANGE); + + /* + ** Special hack to make sure that if this infantry is in firing animation, but the + ** stage class isn't set, then abort the firing flag. + */ + if (IsFiring && !Fetch_Rate()) { + IsFiring = false; + } + + /* + ** Delete this unit if it finds itself off the edge of the map and it is in + ** guard or other static mission mode. + */ + if (!Team && Mission == MISSION_GUARD && !Map.In_Radar(Coord_Cell(Coord))) { + Stun(); + delete this; + return; + } + + /* + ** Act on new orders if the unit is at a good position to do so. + */ + if (!IsDriving && (Doing == DO_NOTHING || MasterDoControls[Doing].Interrupt)) { + Commence(); + } + + /* + ** After a time, the infantry will gain courage. + */ + if (Fear) { + + /* + ** Nikumba is really a coward at heart. He never becomes un-afraid. + */ + if (*this != INFANTRY_C10) { + Fear--; + + /* + ** When an armed civilian becomes unafraid, he will then reload + ** another clip into his pistol. + */ + if (Fear == 0 && Ammo == 0 && Class->Primary != WEAPON_NONE) { + Ammo = Class->MaxAmmo; + } + } + + /* + ** Stand up if brave and lie down if afraid. + */ + if (IsProne) { + if (Fear < FEAR_ANXIOUS) { + Do_Action(DO_GET_UP); + } + } else { + + /* + ** Drop to the ground if anxious. Don't drop to the ground while moving + ** and the special elite flag is active. + */ + if (Fear >= FEAR_ANXIOUS && ((!Target_Legal(NavCom) && !IsDriving) || !Special.IsDefenderAdvantage)) { + Do_Action(DO_LIE_DOWN); + } + } + } + + /* + ** When is darkness or in doubt, + ** run in circles, scream, and shout. + */ + if (Class->IsFraidyCat && Fear > FEAR_ANXIOUS && !IsDriving && !Target_Legal(NavCom)) { + Scatter(true); + } + + /* + ** Special victory dance action. + */ + if (!Target_Legal(NavCom) && !IsProne && IsStoked && Comment.Expired()) { + IsStoked = false; + Do_Action((Random_Pick(0, 1) == 0) ? DO_GESTURE1 : DO_GESTURE2); + if (*this == INFANTRY_RAMBO) { + VocType _response[] = { + VOC_RAMBO_LEFTY, + VOC_RAMBO_LAUGH, + VOC_RAMBO_COMIN, + VOC_RAMBO_TUFF + }; + Sound_Effect(_response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)], Coord); + } + } + + /* + ** Determine if this infantry unit should fire off an + ** attack or not. + */ + switch (Can_Fire(TarCom, 0)) { + case FIRE_CLOAKED: + Do_Uncloak(); + break; + + case FIRE_OK: +#ifdef BOXING + ObjectClass *object = As_Object(TarCom); + + if (object) { + /* If we're engaged in hand-to-hand combat, keep boxing */ + if (IsBoxing) { + IsFiring = true; + if (((InfantryClass *)object)->Doing == DO_FIGHT_READY) { + Do_Action((DoType) ((int)DO_PUNCH + (DoType)(Random_Pick(0, 1) == 1)),true); + } + } else { + + if (Is_Target_Infantry(TarCom) && (Distance(TarCom)<=0x80) && (Coord_Y(Coord) == Coord_Y(object->Coord))) { + + // Too close to shoot, so start hand-to-hand combat + if (Establish_Contact((TechnoClass *)object)) { + if (Transmit_Message(RADIO_PREPARE_TO_BOX) == RADIO_ROGER) { + IsBoxing = true; + Do_Action(DO_ON_GUARD,true); + } + } + } else { +#endif + + /* + ** Start firing animation. + */ + if (IsProne) { + Do_Action(DO_FIRE_PRONE); + IsFiring = true; + } else { + Do_Action(DO_FIRE_WEAPON); + IsFiring = true; + } +#ifdef BOXING + } +#endif + + PrimaryFacing.Set(Direction8(Center_Coord(), As_Coord(TarCom))); + + /* + ** If the target is in range, and the NavCom is the same, then just + ** stop and keep firing. + */ + if (TarCom == NavCom) { + NavCom = TARGET_NONE; + Path[0] = FACING_NONE; + } +#ifdef BOXING + } + } +#endif + break; + } + + /* + ** If in the middle of firing animation, then only + ** process that. Infantry cannot fire and move simultaneously. + ** At some point in the firing animation process, a projectile + ** will be launched. When the required animation frames have + ** been completed, the firing animation stops. + */ + int firestage = Class->FireLaunch; + if (IsProne) firestage = Class->ProneLaunch; + +#ifdef BOXING + if (IsBoxing) { + firestage = 1; + if (Doing == DO_KICK) firestage = 2; + } +#endif + + if (IsFiring && Fetch_Stage() == firestage) { + Fire_At(TarCom, 0); + + if (Class->Primary == WEAPON_GRENADE) { + Map[::As_Cell(TarCom)].Incoming(Coord, true); + } + } + + /* + ** Handle the completion of the animation sequence. + */ + + if (Doing == DO_NOTHING || Fetch_Stage() >= Class->DoControls[Doing].Count) { + switch (Doing) { + default: + if (IsDriving) { + if (IsProne) { + Do_Action(DO_CRAWL, true); + } else { + Do_Action(DO_WALK, true); + } + } else { + if (IsProne) { + Do_Action(DO_PRONE, true); + } else { + Do_Action(DO_STAND_READY, true); + } + } + break; + +#ifdef BOXING + case DO_FIGHT_READY: + case DO_ON_GUARD: + case DO_PUNCH: + case DO_KICK: + case DO_PUNCH_HIT1: + case DO_PUNCH_HIT2: + case DO_KICK_HIT1: + case DO_KICK_HIT2: + if (In_Radio_Contact()) { + Do_Action(DO_FIGHT_READY, true); + } else { + IsBoxing = false; + Do_Action(DO_READY_WEAPON); + } + break; +#endif + + /* + ** When death is due to hand-to-hand combat, use the gunfire death + ** decay animation since there is no custom animation available - yet. + */ + case DO_PUNCH_DEATH: + case DO_KICK_DEATH: + // Fall into next case. + + case DO_GUN_DEATH: + case DO_EXPLOSION_DEATH: + case DO_EXPLOSION2_DEATH: + case DO_GRENADE_DEATH: + case DO_FIRE_DEATH: + delete this; + return; + } + } + + /* + ** Perform movement operations at this time. + */ + if (!IsFiring /*&& !IsBoxing*/) { + if (!IsDriving) { + + /* + ** When in guard mode, never allow a valid navcom. + */ + if (Mission == MISSION_GUARD && MissionQueue == MISSION_NONE && Target_Legal(NavCom)) { + Assign_Destination(TARGET_NONE); +// if (IsTethered) Scatter(0, true); + } + + /* + ** A head to coordinate is needed. If there is no path + ** available, then create one. + */ + if (Target_Legal(NavCom) && Strength && Mission != MISSION_GUARD) { + + /* + ** Determine if the next cell in the list is available + ** to be entered. If not, then abort the path and try + ** again. + */ + if (Path[0] != FACING_NONE && Can_Enter_Cell(Adjacent_Cell(Coord_Cell(Center_Coord()), Path[0])) != MOVE_OK) { + Path[0] = FACING_NONE; + } + + /* + ** Check to see if the target is closer than expected. This occurs + ** when heading toward a moving object and that object is heading + ** toward the unit. Shorten the precalculated path to be no longer + ** than the distance to the target. + */ + int d = Lepton_To_Cell(Distance(NavCom)); + if (d < CONQUER_PATH_MAX) { + Path[d] = FACING_NONE; + } + + /* + ** Find a path to follow if one isn't already calculated. + */ + if (Path[0] == FACING_NONE) { + + /* + ** Calculate the path from the current location to the + ** destination indicated by the navigation computer. If there + ** was a fundamental error with finding a path, then this + ** indicates that basic path & movement logic needs to be + ** aborted. + */ + if (!PathDelay.Expired()) { + return; + } + if (!Basic_Path()) { +//Mono_Printf("Infantry Basic_Path is failing.\n");Get_Key(); + if (Distance(NavCom) < 0x0280 && !IsTethered) { + Assign_Destination(TARGET_NONE); + } else { + if (TryTryAgain) { + TryTryAgain--; + } else { + if (IsNewNavCom) Sound_Effect(VOC_SCOLD); + IsNewNavCom = false; + } + } + Stop_Driver(); + return; + } + TryTryAgain = PATH_RETRY; + } + + /* + ** Determine the coordinate to head to based on the infantry's + ** current location and the next location in the path. + */ + COORDINATE acoord = Adjacent_Cell(Coord, Path[0]); + CELL acell = Coord_Cell(acoord); + + if (Can_Enter_Cell(acell) != MOVE_OK) { + + if ((Mission == MISSION_MOVE || Mission == MISSION_ENTER) && !IsTethered && House->IsHuman && Distance(NavCom) < 0x0200) { + Assign_Destination(TARGET_NONE); + } + + /* + ** If blocked by a moving block then just exit start of move and + ** try again next tick. + */ + if (Can_Enter_Cell(acell) == MOVE_DESTROYABLE) { + if (Map[acell].Cell_Object()) { + if (!House->Is_Ally(Map[acell].Cell_Object())) { + Override_Mission(MISSION_ATTACK, Map[acell].Cell_Object()->As_Target(), TARGET_NONE); + } + } else { + if (Map[acell].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[acell].Overlay).IsWall) { + Override_Mission(MISSION_ATTACK, ::As_Target(acell), TARGET_NONE); + } + } + } + + Path[0] = FACING_NONE; + Stop_Driver(); + if (IsNewNavCom) Sound_Effect(VOC_SCOLD); + IsNewNavCom = false; + + } else { + if (Start_Driver(acoord)) { + PrimaryFacing.Set(Direction8(Center_Coord(), Head_To_Coord())); + Set_Speed(0xFF); + if (IsProne) { + Do_Action(DO_CRAWL); + } else { + Do_Action(DO_WALK); + } + } + } + } + + } else { + + /* + ** The infantry knows where it should be headed, so head there. Check + ** to see if the infantry is "close enough" to the desired location that + ** it should just consider itself to have arrived. In this case, force + ** the infantry to the destination location and mark this path step + ** as complete. + */ + Mark(MARK_UP); + if (Distance(Head_To_Coord()) < 0x0010) { + + memcpy(&Path[0], &Path[1], sizeof(Path)-sizeof(Path[0])); + Path[(sizeof(Path)/sizeof(Path[0]))-1] = FACING_NONE; + Coord = Head_To_Coord(); + Stop_Driver(); + Per_Cell_Process(true); + + if (!IsActive || IsInLimbo) return; + + if (Coord_Cell(Coord) == As_Cell(NavCom)) { + NavCom = TARGET_NONE; + if (Mission == MISSION_MOVE) { + Enter_Idle_Mode(); + } + //Stop_Driver(); + Path[0] = FACING_NONE; + } + } else { + int movespeed = Speed; + + /* + ** When prone, the infantry moves at half speed or double + ** speed. This depends on whether the infantry actually has + ** prone animation stages. Civilians don't, and so they + ** run instead. + */ + if (IsProne) { + if (Class->IsFraidyCat && !Class->IsCrawling) { + movespeed = Speed*2; + } else { + movespeed = Speed/2; + } + } + + if (IsTethered) { + Transmit_Message(RADIO_REDRAW); + } + + /* + ** Advance the infantry as far as it should go. + */ + Coord = Coord_Move(Coord, Direction(Head_To_Coord()), Fixed_To_Cardinal(Class->MaxSpeed, movespeed)); + } + Mark(MARK_DOWN); + } + IsNewNavCom = false; + } +} + + +#ifdef NEVER +/*************************************************************************** + * InfantryClass::Blocking_Object -- Determines how a object blocks an inf * + * * + * INPUT: TechnoClass * pointer to object that is blocking unit * + * CELL the cell the unit is being blocked in * + * * + * OUTPUT: MoveBitType the way that the object is blocking the unit * + * * + * HISTORY: * + * 06/08/1995 PWG : Created. * + *=========================================================================*/ +MoveBitType InfantryClass::Blocking_Object(TechnoClass const *techno, CELL cell) const +{ + Validate(); + bool inf = (techno->What_Am_I() == RTTI_INFANTRY); + bool unit = (techno->What_Am_I() == RTTI_UNIT) || inf; + + CellClass const * cellptr = &Map[cell]; + + if (House->Is_Ally(techno)) { + + /* + ** Logic to handle a trasport type object. + */ + if (NavCom == techno->As_Target() && Contact_With_Whom() == techno) { + return(MOVEF_OK); + } + + /* + ** If the object is of type infantry, then the square is blocked only + ** if the cell is completely full of infantry. + */ + if (inf && ((cellptr->Flag.Composite & 0x1f) != 0x1f)) { + return(MOVEF_OK); + } + + if (unit) { + + /* + ** If the unit in question has a destination than we should + ** be prepared to wait for the unit to get out of our way. + */ + if (((FootClass *)techno)->NavCom != TARGET_NONE) { + return(MOVEF_MOVING_BLOCK); + } + return(MOVEF_TEMP); + } + } else { + + /* + ** If its an enemy unit, things are dealt with a little differently + */ + if (unit) { + + + /* + ** If the object is cloaked, then consider it passable for findpath purposes, + ** but not so for all other cases. + */ + if (techno->Cloak == CLOAKED) { + if (IsFindPath) return(MOVEF_OK); + return(MOVEF_CLOAK); + } + + /* + ** If our vehicle is weapon equipped, then report that the cell occupier + ** needs only to be destroyed in order to make the cell passable. + */ + if (Class->Primary != WEAPON_NONE) { + return(MOVEF_DESTROYABLE); + } + } + } + return(MOVEF_NO); +} +#endif + + + +/*********************************************************************************************** + * InfantryClass::Can_Enter_Cell -- Determines if the infantry can enter the cell specified. * + * * + * This routine is used to examine the cell specified and determine if the infantry is * + * allowed to enter it. It is used by the path finding algorithm. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: Returns the type of blockage in the cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +MoveType InfantryClass::Can_Enter_Cell(CELL cell, FacingType ) const +{ + Validate(); + /* + ** If we are moving into an illegal cell, then we can't do that. + */ + if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO); + + /* + ** If moving off the edge of the map, then consider that an illegal move. + */ + if (IsLocked && !IsALoaner && !ScenarioInit && !Map.In_Radar(cell)) { + return(MOVE_NO); + } + CellClass * cellptr = &Map[cell]; + + /* + ** Walls are considered impassable for infantry UNLESS the wall has a hole + ** in it. + */ + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(cellptr->Overlay); + + if (otype.IsCrate && !House->IsHuman) { + return(MOVE_NO); + } + + if (otype.IsWall) { + if ((cellptr->OverlayData / 16) != otype.DamageLevels) { + return(MOVE_NO); + } + } + } + + /* + ** Loop through all of the objects in the square setting a bit + ** for how they affect movement. + */ + MoveType retval = MOVE_OK; + ObjectClass *obj = cellptr->Cell_Occupier(); + while (obj) { + + if (obj != this) { + + /* + ** Special case check so that a landed aircraft that is in radio contact, will not block + ** a capture attempt. It is presumed that this case happens when a helicopter is landed + ** at a helipad. + */ + if ((Mission != MISSION_CAPTURE && Mission != MISSION_SABOTAGE) || obj->What_Am_I() != RTTI_AIRCRAFT || !((AircraftClass *)obj)->In_Radio_Contact()) { + + /* + ** Special check to always allow entry into the building that this infantry + ** is trying to capture. + */ + if (obj->What_Am_I() == RTTI_BUILDING || obj->What_Am_I() == RTTI_AIRCRAFT) { + if ((Mission == MISSION_CAPTURE || Mission == MISSION_SABOTAGE) && (obj->As_Target() == NavCom || obj->As_Target() == TarCom)) { + return(MOVE_OK); + } + } + + /* + ** Special check to always allow entry into the building that this infantry + ** is trying to capture. + */ + if (Mission == MISSION_ENTER && obj->As_Target() == NavCom && IsTethered) { + return(MOVE_OK); + } + + /* + ** Allied objects block movement using different rules than for enemy + ** objects. + */ + if (House->Is_Ally(obj)) { + switch (obj->What_Am_I()) { + + /* + ** A unit blocks as either a moving blockage or a stationary temp blockage. + ** This depends on whether the unit is currently moving or not. + */ + case RTTI_UNIT: + if (((UnitClass *)obj)->IsDriving || Target_Legal(((UnitClass *)obj)->NavCom)) { + if (retval < MOVE_MOVING_BLOCK) retval = MOVE_MOVING_BLOCK; + } else { + if (retval < MOVE_TEMP) retval = MOVE_TEMP; + } + break; + + /* + ** Aircraft and buildings always block movement. If for some reason there is an + ** allied terrain object, that blocks movement as well. + */ + case RTTI_TERRAIN: + case RTTI_AIRCRAFT: + case RTTI_BUILDING: + return(MOVE_NO); + + default: + break; + } + + } else { + + /* + ** Cloaked enemy objects are not considered if this is a Find_Path() + ** call. + */ + if (!obj->Is_Techno() || ((TechnoClass *)obj)->Cloak != CLOAKED) { + + /* + ** Any non-allied blockage is considered impassible if the infantry + ** is not equipped with a weapon. + */ + if (Class->Primary == WEAPON_NONE) return(MOVE_NO); + + /* + ** Some kinds of terrain are considered destroyable if the infantry is equipped + ** with the weapon that can destroy it. Otherwise, the terrain is considered + ** impassable. + */ + switch (obj->What_Am_I()) { + case RTTI_TERRAIN: + if (((TerrainClass *)obj)->Class->IsFlammable && + BulletTypeClass::As_Reference(Weapons[Class->Primary].Fires).Warhead == WARHEAD_FIRE) { + + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; + } else { + return(MOVE_NO); + } + break; + + default: + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; + break; + } + } else { + if (retval < MOVE_CLOAK) retval = MOVE_CLOAK; + } + } + } + } + + /* + ** Move to next object in chain. + */ + obj = obj->Next; + } + + /* + ** If foot soldiers cannot travel on the cell -- consider it impassible. + */ + if (retval == MOVE_OK && !IsTethered && !Ground[cellptr->Land_Type()].Cost[SPEED_FOOT]) { + return(MOVE_NO); + } + + /* + ** if a unit has the cell reserved then we just can't go in there. + */ + if (retval == MOVE_OK && cellptr->Flag.Occupy.Vehicle) { + return(MOVE_NO); + } + + /* + ** if a block of infantry has the cell reserved then there are two + ** possibilities... + */ + if (cellptr->InfType != HOUSE_NONE) { + if (House->Is_Ally(cellptr->InfType)) { + if ((cellptr->Flag.Composite & 0x1F) == 0x1f) { + if (retval < MOVE_MOVING_BLOCK) retval = MOVE_MOVING_BLOCK; + } + } else { + if (Class->Primary != WEAPON_NONE) { + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; + } else { + return(MOVE_NO); + } + } + } + + /* + ** If it is still ok to move the infantry, then perform the last check + ** to see if the cell is already full of infantry. + */ + if (retval == MOVE_OK && (cellptr->Flag.Composite & 0x1F) == 0x1F) { + return(MOVE_NO); + } + + /* + ** Return with the most severe reason why this cell would be impassable. + */ + return(retval); +} + + +/*********************************************************************************************** + * InfantryClass::Overlap_List -- The list of cells that the infantry overlaps, but doesn't occ* + * * + * This is a rendering support routine that will return a pointer to a list of cell offsets * + * that specify the cells the infantry unit is currently overlapping (graphic wise) but * + * is not considered to occupy. This list is used to update the map display. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to an offset list for cells that the unit overlaps but doesn't * + * occupy. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +short const * InfantryClass::Overlap_List(void) const +{ + Validate(); + //return(Coord_Spillage_List(Coord, 24 + ((IsSelected || Doing > DO_WALK)?12:0))); + return(Coord_Spillage_List(Coord, 24 + ((Doing > DO_WALK || IsSelected)?12:0))); +// return(Coord_Spillage_List(Coord, (IsSelected ? 24 : 14))+1); +} + + +/*********************************************************************************************** + * InfantryClass::Can_Fire -- Can the infantry fire its weapon? * + * * + * Determines if the infantry unit can fire on the target. If it can't fire, then the * + * reason why is returned. * + * * + * INPUT: target -- The target to determine if the infantry can fire upon. * + * * + * OUTPUT: Returns the fire error type that indicates if the infantry can fire and if it * + * can't, why not. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + * 06/27/1995 JLB : Flame thrower can fire while prone now. * + *=============================================================================================*/ +FireErrorType InfantryClass::Can_Fire(TARGET target, int which) const +{ + Validate(); + WeaponTypeClass const * weapon = (which == 0) ? &Weapons[Class->Primary] : &Weapons[Class->Secondary]; + +#ifdef BOXING + /* + ** If in hand-to-hand, and we're currently playing a got-hit animation, + ** then we can't punch back yet. + */ + if (IsBoxing) { + if ( (Doing>=DO_PUNCH_HIT1 && Doing<=DO_KICK_DEATH) || (Doing == DO_ON_GUARD) ) return FIRE_BUSY; + if (Arm) return(FIRE_BUSY); // don't let fire if still re-arming + } +#endif + + /* + ** Don't allow firing if the turret is not ready. + */ + if (IsFiring) return(FIRE_REARM); + +#ifdef OBSOLETE + if (weapon->Fires == BULLET_FLAME && IsProne) return(FIRE_ILLEGAL); +#endif + + /* + ** The target must still be legal. + */ + if (!Target_Legal(target)) { + return(FIRE_ILLEGAL); + } + + /* + ** If this unit cannot fire while moving, then bail. + */ + if ((IsDriving && Special.IsDefenderAdvantage) || (Doing != DO_NOTHING && !MasterDoControls[Doing].Interrupt)) { + return(FIRE_MOVING); + } + + /* + ** If we're moving, but not facing the right direction, then exit. + */ + if (!Special.IsDefenderAdvantage && IsDriving) { + int diff = PrimaryFacing.Difference(Direction(TarCom)); + if (ABS(diff) >= 32) { + return(FIRE_MOVING); + } + } + + return(FootClass::Can_Fire(target, which)); +} + + +/*********************************************************************************************** + * InfantryClass::Enter_Idle_Mode -- The infantry unit enters idle mode by this routine. * + * * + * Use this routine when the infantry unit as accomplished its task and needs to find * + * something to do. The default behavior is to enter some idle state such as guarding. * + * * + * INPUT: initial -- Is this called when the unit just leaves a factory or is initially * + * or is initially placed on the map? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Enter_Idle_Mode(bool ) +{ + Validate(); + MissionType order; + + if (Target_Legal(TarCom)) { + order = MISSION_ATTACK; + } else { + if (Target_Legal(NavCom)) { + order = MISSION_MOVE; + } else { + if (GameToPlay == GAME_NORMAL || House->IsHuman) { + order = MISSION_GUARD; + } else { + order = MISSION_HUNT; + } + } + } + Assign_Mission(order); +} + + +/*********************************************************************************************** + * InfantryClass::Random_Animate -- Randomly animate the infantry (maybe) * + * * + * This routine is the random animator initiator for infantry units. This routine should * + * be called regularly. On occasion, it will cause the infantry to go into an idle * + * animation. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + * 12/13/1994 JLB : Does random facing change. * + * 07/02/1995 JLB : Nikoomba special effects. * + *=============================================================================================*/ +void InfantryClass::Random_Animate(void) +{ + Validate(); + if (!IsDriving && !IsProne && (Doing == DO_STAND_GUARD || Doing == DO_STAND_READY) && !IsFiring) { + + /* + ** Scared infantry will always follow the golden rule of civilians; + ** "When in darkness or in doubt, run in circles, scream, and shout!" + */ + if (Class->IsFraidyCat && !House->IsHuman && Fear > FEAR_ANXIOUS) { + Scatter(NULL, true); + return; + } + + /* + ** If Nikoomba is not scared, then he will be doing his thing with random animations. + */ + if (*this == INFANTRY_C10) { + switch (Random_Pick(0, 3)) { + case 0: + Do_Action(DO_IDLE2); + break; + + default: + break; + } + } + + switch (Random_Picky((int)0, (int)55, (char*)NULL, (int)0)) { + case 10: + Do_Action(DO_SALUTE1); + break; + + case 11: + Do_Action(DO_SALUTE2); + break; + + case 12: + Do_Action(DO_GESTURE1); + break; + + case 13: + Do_Action(DO_GESTURE2); + break; + + case 4: + case 3: + case 0: + PrimaryFacing.Set(Facing_Dir(Random_Pick(FACING_N, FACING_NW))); + Mark(MARK_CHANGE); + break; + + case 1: + Do_Action(DO_IDLE1); + PrimaryFacing.Set(Facing_Dir(Random_Pick(FACING_N, FACING_NW))); + if (Sim_Random_Pick(1,20) == 1 && !IsSelected && *this == INFANTRY_MOEBIUS && IsDiscoveredByPlayer) { + static VocType _response[] = { +// VOC_EXCELLENT1, +// VOC_EXCELLENT2, + VOC_EXCELLENT3, +// VOC_EXCELLENT4, +// VOC_EXCELLENT5, + VOC_QUIP1, +// VOC_QUIP2 + }; + Sound_Effect(_response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)], Coord); + } + break; + + case 2: + Do_Action(DO_IDLE2); + PrimaryFacing.Set(Facing_Dir(Random_Pick(FACING_N, FACING_NW))); + if (!IsSelected && IsOwnedByPlayer && *this == INFANTRY_RAMBO && Sim_Random_Pick(0, 2) == 0) { + Sound_Effect(VOC_RAMBO_CMON, Coord); + } + break; + + /* + ** On occasion, civilian types will wander about. + */ + case 5: + case 6: + case 7: + case 8: + case 9: + if (!House->IsHuman && Class->IsFraidyCat) { + Scatter(NULL, true); + } + break; + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Scatter -- Causes the infantry to scatter to nearby cell. * + * * + * This routine is used when the infantry should scatter to a nearby cell. Scattering * + * occurs as an occasional consequence of being fired upon. It is one of the features * + * that makes infantry so "charming". * + * * + * INPUT: threat -- The coordinate source of the threat that is causing the infantry to * + * scatter. If the threat isn't from a particular direction, then this * + * parameter will be NULL. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 12/12/1994 JLB : Flame thrower infantry always scatter. * + *=============================================================================================*/ +void InfantryClass::Scatter(COORDINATE threat, bool forced) +{ + Validate(); + + /* + ** A unit that is in the process of going somewhere will never scatter. + */ + if (IsDriving || Target_Legal(NavCom)) forced = false; + + /* + ** If the infantry is currently engaged in legitimate combat, then don't + ** scatter unless forced to. + */ + if (!Class->IsFraidyCat && Target_Legal(TarCom) && !forced) return; + + /* + ** Don't scatter if performing an action that can't be interrupted. + */ + if (Doing != DO_NOTHING && !MasterDoControls[Doing].Interrupt) return; + + /* + ** For human players, don't scatter the infantry, if the special + ** flag has not been enabled that allows infantry scatter. + */ + if (!Special.IsScatter && House->IsHuman && !forced && !Team) return; + + if (forced || Class->IsFraidyCat /*|| !(Random_Pick(1, 4) == 1)*/) { + FacingType toface; + FacingType newface; + CELL newcell; + + if (threat) { + toface = Dir_Facing(Direction8(threat, Coord)); + toface = toface + (Random_Pick(0, 4)-2); + } else { + COORDINATE coord = Coord & 0x00FF00FFL; + + if (coord != 0x00800080L) { + toface = Dir_Facing((DirType)Desired_Facing8(0x0080, 0x0080, Coord_X(coord), Coord_Y(coord))); + } else { + toface = Dir_Facing(PrimaryFacing.Current()); + } + toface = toface + (Random_Pick(0, 4)-2); + } + + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + newface = toface + face; + newcell = Adjacent_Cell(Coord_Cell(Coord), newface); + + if (Map.In_Radar(newcell) && Can_Enter_Cell(newcell) == MOVE_OK) { + Assign_Mission(MISSION_MOVE); + Assign_Destination(::As_Target(newcell)); + } + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Do_Action -- Launches the infantry into an animation sequence. * + * * + * This starts the infantry into a choreographed animation sequence. These sequences can * + * be as simple as standing up or lying down, but can also be complex, such as dying or * + * performing some idle animation. * + * * + * INPUT: todo -- The choreographed sequence to start. * + * * + * force -- Force starting this animation even if the current animation is flagged * + * as uninterruptible. This is necessary for death animations. * + * * + * OUTPUT: bool; Was the animation started? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Do_Action(DoType todo, bool force) +{ + Validate(); + if (todo != Doing && (Doing == DO_NOTHING || force || MasterDoControls[Doing].Interrupt)) { + Mark(MARK_CHANGE); + //Mark(MARK_OVERLAP_UP); + Doing = todo; + //Mark(MARK_OVERLAP_DOWN); + if (todo == DO_IDLE1 || todo == DO_IDLE2) { + Set_Rate(Options.Normalize_Delay(MasterDoControls[Doing].Rate)); + } else { + Set_Rate(MasterDoControls[Doing].Rate); + } + Set_Stage(0); + + /* + ** Kludge to make sure that if infantry is in the dying animation, it isn't still + ** moving as well. + */ + if (!Strength) { + Stop_Driver(); + } + + /* + ** Since the animation sequence might be interrupted. Set any flags + ** necessary so that if interrupted, the affect on the infantry is + ** still accomplished. + */ + switch (todo) { + case DO_LIE_DOWN: + IsProne = true; + break; + + case DO_GET_UP: + IsProne = false; + break; + + case DO_READY_WEAPON: + IsBoxing = false; + break; + + case DO_ON_GUARD: + IsBoxing = true; + PrimaryFacing.Set(Direction8(Center_Coord(), As_Coord(TarCom))); + Path[0] = FACING_NONE; + break; + + default: + break; + } + + return(true); + } + + return(false); +} + + +/*********************************************************************************************** + * InfantryClass::Stop_Driver -- Stops the infantry from moving any further. * + * * + * This is used to stop the infantry from animating in movement. This function will stop * + * the infantry moving and revert it to either a prone or standing. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the driving stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Stop_Driver(void) +{ + Validate(); + if (Head_To_Coord()) { + + /* + ** Remove the "reservation" bit in the destination location. + */ + Clear_Occupy_Bit(Head_To_Coord()); + } + + /* + ** Set the occupation bit at the current location. + */ + Set_Occupy_Bit(Coord); + + if (IsProne) { + Do_Action(DO_PRONE); + } else { + Do_Action(DO_STAND_READY); + } + + return(FootClass::Stop_Driver()); +} + + +/*********************************************************************************************** + * InfantryClass::Start_Driver -- Handles giving immediate destination and move orders. * + * * + * Use this routine to being the infantry moving toward the destination specified. The * + * destination is first checked to see if there is a free spot available. Then the infantry * + * reserves that spot and begins movement toward it. * + * * + * INPUT: headto -- The coordinate location desired for the infantry to head to. * + * * + * OUTPUT: bool; Was the infantry successfully started on its journey? Failure may be because * + * the specified destination could not contain the infantry unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/21/1994 JLB : Created. * + * 05/14/1995 JLB : Tries to move to closest spot possible. * + * 05/15/1995 JLB : Uses closest spot if moving onto transport. * + *=============================================================================================*/ +bool InfantryClass::Start_Driver(COORDINATE & headto) +{ + Validate(); + COORDINATE old = headto; + + /* + ** Convert the head to coordinate to a legal sub-position location. + */ + headto = Map[Coord_Cell(headto)].Closest_Free_Spot(Coord_Move(headto, Direction(headto)+DIR_S, 0x007C)); + if (!headto && Can_Enter_Cell(Coord_Cell(old)) == MOVE_OK) { + headto = Map[Coord_Cell(old)].Closest_Free_Spot(Coord_Move(old, Direction(headto)+DIR_S, 0x0080), true); + } + + /* + ** If the infantry started moving, then fixup the occupation bits. + */ + if (headto && FootClass::Start_Driver(headto)) { + /* + ** Remove the occupation bit from the infantry's current location. + */ + Clear_Occupy_Bit(Coord); + + /* + ** Set the occupation bit for the new headto location. + */ + Set_Occupy_Bit(headto); + return(true); + } + + return(false); +} + + +#ifdef NEVER +/*********************************************************************************************** + * InfantryClass::Set_Primary_Facing -- Change infantry primary facing -- always and instantly * + * * + * This routine ensures that when the infantry primary facing is changes, it is changed * + * instantly and always. There is no provision for infantry facing changing slowly over * + * time as the other vehicles usually do. * + * * + * INPUT: facing -- The desired facing for the infantry unit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/11/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Set_Primary_Facing(DirType facing, bool ) +{ + Validate(); + FootClass::Set_Primary_Facing(facing, true); +} +#endif + + +/*********************************************************************************************** + * InfantryClass::Limbo -- Performs cleanup operations needed when limboing. * + * * + * This routine will clean up the infantry occupation bits (as necessary) as well as stop * + * the infantry movement process when it gets limboed. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the infantry unit limboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Limbo(void) +{ + Validate(); + if (!IsInLimbo) { + Stop_Driver(); + + Clear_Occupy_Bit(Coord); + } + return(FootClass::Limbo()); +} + + +/*********************************************************************************************** + * InfantryClass::Fire_At -- Fires projectile from infantry unit. * + * * + * Use this routine when the infantry unit wishes to fire a projectile. This routine * + * will launch the projectile and perform any other necessary infantry specific operations. * + * * + * INPUT: target -- The target of the attack. * + * * + * which -- Which weapon to use for firing. 0=primary, 1=secondary. * + * * + * OUTPUT: Returns with pointer to the projectile launched. If none could be launched, then * + * NULL is returned. If there is already the maximum bullet objects in play, then * + * this could happen. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +BulletClass * InfantryClass::Fire_At(TARGET target, int which) +{ + Validate(); + BulletClass * bullet = NULL; + WeaponTypeClass const * weapon = (which == 0) ? &Weapons[Class->Primary] : &Weapons[Class->Secondary]; + + IsFiring = false; + +#ifdef BOXING + if (IsBoxing) { + RadioMessageType hitaction = (Doing == DO_KICK) ? RADIO_KICK : RADIO_PUNCH; + + /* + ** When fighting, verify that the target is legal to proceed. If there is some + ** error, then abort fightning mode. Otherwise, tell the target that it has + ** just been hit. + */ + if (In_Radio_Contact() && Target_Legal(target) && Transmit_Message(hitaction) == RADIO_ROGER) { + Arm = Rearm_Delay(true); + } else { + + /* + ** Fighting done for some reason, so pick up gun + */ + IsBoxing = false; + Do_Action(DO_READY_WEAPON,true); + } + } else { +#endif + + bullet = FootClass::Fire_At(target, which); + if (bullet) { + + /* + ** For fraidycat infantry that run out of ammo, always go into + ** a maximum fear state at that time. + */ + if (Class->IsFraidyCat && !Ammo) { + Fear = FEAR_MAXIMUM; + if (Mission == MISSION_ATTACK || Mission == MISSION_HUNT) { + Assign_Mission(MISSION_GUARD); + } + } + + /* + ** Create the projectile. Then process any special operations that + ** need to be performed according to the style of projectile + ** created. + */ + Sound_Effect(weapon->Sound, Coord); + } + +#ifdef BOXING + } +#endif + return(bullet); +} + + +/*********************************************************************************************** + * InfantryClass::Unlimbo -- Unlimbo infantry unit in legal sub-location. * + * * + * This will attempt to unlimbo the infantry unit at the designated coordinate, but will * + * ensure that the coordinate is a legal subposition. * + * * + * INPUT: coord -- The coordinate to unimbo the infantry at. * + * * + * facing -- The desired initial facing for the infantry unit. * + * * + * strength -- The desired initial strength for the infantry unit. * + * * + * mission -- The desired initial mission for the infantry unit. * + * * + * OUTPUT: bool; Was the infantry unlimboed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Unlimbo(COORDINATE coord, DirType facing) +{ + Validate(); + /* + ** Make sure that the infantry start in a legal position on the map. + */ + coord = Map[Coord_Cell(coord)].Closest_Free_Spot(coord, ScenarioInit); + if (coord == NULL) { + return(false); + } + + if (FootClass::Unlimbo(coord, facing)) { + + /* + ** Ensure that the owning house knows about the + ** new object. + */ + House->IScan |= (1L << Class->Type); + House->ActiveIScan |= (1L << Class->Type); + + /* + ** If there is no sight range, then this object isn't discovered by the player unless + ** it actually appears in a cell mapped by the player. + */ + if (Class->SightRange == 0) { + IsDiscoveredByPlayer = false; + } + + Set_Occupy_Bit(coord); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * InfantryClass::Greatest_Threat -- Determines greatest threat (target) for infantry unit. * + * * + * This routine intercepts the Greatest_Threat request and adds the appropriate target * + * types to search for. For regular infantry, this consists of all the ground types. For * + * rocket launching infantry, this also includes aircraft. * + * * + * INPUT: threat -- The basic threat control value. * + * * + * OUTPUT: Returns with the best target for this infantry unit to attack. If no suitable * + * target could be found, then TARGET_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + *=============================================================================================*/ +TARGET InfantryClass::Greatest_Threat(ThreatType threat) const +{ + Validate(); + /* + ** Engineers consider only buildings that can be captures as being a threat. All others + ** are ignored. + */ + if (Class->IsCapture && Class->Primary == WEAPON_NONE) { + threat = threat | THREAT_CAPTURE; + } + + switch (Class->Primary) { + case WEAPON_NONE: + if (*this != INFANTRY_E7) { + return(TARGET_NONE); + } + // fall into next case. + + /* + ** Dragon missile equiped soldiers are also assumed to carry a Stinger missile. As such, + ** they will consider aircraft a legal target. + */ + default: + if (BulletTypeClass::As_Reference(Weapons[Class->Primary].Fires).IsAntiAircraft) { + threat = threat | THREAT_AIR; + } + break; + + /* + ** The sniper rifle equipped soldier doesn't go hunting for targets + ** unless specifically in hunt mode. + */ + case WEAPON_RIFLE: + if (House->IsHuman && (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA)) { + return(TARGET_NONE); + } + return(TechnoClass::Greatest_Threat(threat | THREAT_INFANTRY|THREAT_BUILDINGS)); + } + return(FootClass::Greatest_Threat(threat)); +} + + +/*********************************************************************************************** + * InfantryClass::Response_Select -- Plays infantry audio response due to being selected. * + * * + * This routine handles playing an audio response as a result of the player selecting the * + * infantry unit. This occurs prior to giving it an order and may not be followed by any * + * order at all. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + * 05/05/1995 JLB : Rambo response types added. * + *=============================================================================================*/ +void InfantryClass::Response_Select(void) +{ + Validate(); + VocType response; + if (*this == INFANTRY_RAMBO) { + static VocType _response[] = { + VOC_RAMBO_YEA, + VOC_RAMBO_YES, + VOC_RAMBO_YO + }; + response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + } else { + if (Class->IsCivilian) { + if (*this == INFANTRY_MOEBIUS) { + static VocType _response[] = { + VOC_YES, + VOC_COMMANDER, + VOC_HELLO, + VOC_HMMM + }; + response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + } else { + if (Class->IsFemale) { + response = VOC_GIRL_YEAH; + } else { + response = VOC_GUY_YEAH; + } + } + } else { + static VocType _response[] = { + VOC_ACKNOWL, + VOC_REPORT, + VOC_REPORT, + VOC_YESSIR, + VOC_YESSIR, + VOC_READY, + VOC_AWAIT + }; + response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + } + } + if (AllowVoice) { + Sound_Effect(response, 0, Infantry.ID(this)+1); + } +} + + +/*********************************************************************************************** + * InfantryClass::Response_Move -- Plays infantry response to movement order. * + * * + * When the infantry is given the order to move, this routine handles the audio repsonse * + * generated by the infantry unit. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + * 05/05/1995 JLB : Rambo response types added. * + *=============================================================================================*/ +void InfantryClass::Response_Move(void) +{ + Validate(); + VocType response; + if (*this == INFANTRY_RAMBO) { + static VocType _response[] = { + VOC_RAMBO_UGOTIT, + VOC_RAMBO_ONIT, + VOC_RAMBO_NOPROB + }; + response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + } else { + if (Class->IsCivilian) { + if (*this == INFANTRY_MOEBIUS) { + static VocType _response[] = { + VOC_OF_COURSE, + VOC_YESYES + }; + response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + } else { + if (Class->IsFemale) { + response = VOC_GIRL_OKAY; + } else { + response = VOC_GUY_OKAY; + } + } + } else { + static VocType _response[] = { + VOC_MOVEOUT, + VOC_MOVEOUT, + VOC_MOVEOUT, + VOC_ROGER, + VOC_RIGHT_AWAY, + VOC_UGOTIT, + VOC_AFFIRM, + VOC_AFFIRM + }; + response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + } + } + if (AllowVoice) { + Sound_Effect(response, 0, Infantry.ID(this)+1); + } +} + + +/*********************************************************************************************** + * InfantryClass::Response_Attack -- Plays infantry audio response to attack order. * + * * + * When the player gives an infantry unit the order to attack, this routine handles * + * the audio response by that unit. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + * 05/05/1995 JLB : Rambo response types added. * + *=============================================================================================*/ +void InfantryClass::Response_Attack(void) +{ + Validate(); + VocType response; + if (*this == INFANTRY_RAMBO) { + static VocType _response[] = { + VOC_RAMBO_NOPROB, + VOC_RAMBO_UGOTIT, + VOC_RAMBO_NOPROB, + VOC_RAMBO_ONIT + }; + response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + } else { + if (Class->IsCivilian) { + if (Class->IsFemale) { + response = VOC_GIRL_OKAY; + } else { + response = VOC_GUY_OKAY; + } + } else { + static VocType _response[] = { + VOC_RIGHT_AWAY, + VOC_AFFIRM, + VOC_AFFIRM, + VOC_UGOTIT, + VOC_NO_PROB, + VOC_YESSIR, + VOC_YESSIR, + VOC_YESSIR + }; + response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + } + } + + if (AllowVoice) { + Sound_Effect(response, 0, Infantry.ID(this)+1); + } +} + + +/*********************************************************************************************** + * InfantryClass::Fire_Coord -- Calculates the origin point for projectiles fired. * + * * + * This routine will return with the point of origin for any firing projectiles. Typically, * + * this only includes the rocket launcher and the grenade thrower. The other infantry * + * have either invisible projectiles or special animations. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate where the projectile will appear as it is fired. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/04/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE InfantryClass::Fire_Coord(int) const +{ + Validate(); + if (Class->Type == INFANTRY_E4) { + return(Coord); // special case for flame thrower guy + } else { + return(Coord_Add(Coord, XYP_COORD(0, -5))); + } +} + + +/*************************************************************************** + * InfantryClass::Receive_Message -- Process radio messages * + * * + * If the infantry's boxing, it needs to return to a normal state when * + * his opponent moves away. Otherwise fall thru to FootClass processing* + * * + * INPUT: from - Pointer to the originator of this message. * + * * + * message - the message to process * + * * + * param -- Reference to an optional parameter that can be * + * used to transfer more information than is * + * possible with the simple radio message values. * + * * + * OUTPUT: an appropriate response message * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/19/1995 BWG : Created. * + * 05/14/1995 JLB : Handles loading maneuver messages. * + *=========================================================================*/ +RadioMessageType InfantryClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + Validate(); + int damage; + + switch (message) { + + case RADIO_OVER_OUT: +#ifdef BOXING + if (IsBoxing) Do_Action(DO_READY_WEAPON); +#endif + break; + + /* + ** Request a fisticuff fight. If this infantry is already involved in a fight, + ** then refuse the offer. + */ + case RADIO_PREPARE_TO_BOX: +#ifdef BOXING + if (IsBoxing) break; +#endif + if (Contact_With_Whom() == from) { + Do_Action(DO_ON_GUARD, true); + Assign_Target(Contact_With_Whom()->As_Target()); + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + + /* + ** Just received a kick! Take some damage. + */ + case RADIO_KICK: + damage = Infantry_Kick_Damage[Random_Pick(0, (int)(sizeof(Infantry_Kick_Damage) / sizeof(Infantry_Kick_Damage[0])))]; + if (Take_Damage(damage, 0, WARHEAD_FOOT, this) == RESULT_DESTROYED) return(RADIO_STATIC); + return(RADIO_ROGER); + + /* + ** Just recieved a punch! Take some damage. + */ + case RADIO_PUNCH: + damage = Infantry_Punch_Damage[Random_Pick(0, (int)(sizeof(Infantry_Punch_Damage) / sizeof(Infantry_Punch_Damage[0])))]; + if (Take_Damage(damage, 0, WARHEAD_FIST, this) == RESULT_DESTROYED) return(RADIO_STATIC); + return(RADIO_ROGER); + + } + return(FootClass::Receive_Message(from, message, param)); +} + + +/*************************************************************************** + * InfantryClass::Rearm_Delay -- Return Arming delay for infantry if boxing* + * * + * If the infantry's in a boxing mode, return an appropriate re-arming * + * delay. Otherwise return the default return val. * + * * + * INPUT: second -- bool; see TechnoClass... * + * * + * OUTPUT: Returns with the # of game frames to delay before shooting * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 BWG : Created. * + *=========================================================================*/ +int InfantryClass::Rearm_Delay(bool second) const +{ + Validate(); +#ifdef BOXING + if (IsBoxing) { + return(Random_Pick(5, 50)); + } +#endif + return(FootClass::Rearm_Delay(second)); +} + + +/*********************************************************************************************** + * InfantryClass::Assign_Mission -- Assign mission to infantry object. * + * * + * When a new mission is assigned, make sure he gets out of boxing mode. * + * * + * INPUT: order -- The new mission to assign to the unit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Assign_Mission(MissionType order) +{ + Validate(); + if (order == MISSION_SABOTAGE) { + Sound_Effect(VOC_RAMBO_PRESENT, Coord); + } + + IsBoxing = false; + FootClass::Assign_Mission(order); +} + + +/*********************************************************************************************** + * InfantryClass::What_Action -- Infantry units might be able to capture -- check. * + * * + * This routine checks to see if the infantry unit can capture the specified object rather * + * than merely attacking it. If this is the case, then ACTION_CAPTURE will be returned. * + * * + * INPUT: object -- The object that the mouse is currently over. * + * * + * OUTPUT: Returns the action that will be performed if the mouse were clicked over the * + * object specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/01/1995 JLB : Created. * + *=============================================================================================*/ +ActionType InfantryClass::What_Action(ObjectClass * object) const +{ + Validate(); + ActionType action = FootClass::What_Action(object); + + /* + ** First see if it's a commando, and if he's attacking a building, have him return ACTION_SABOTAGE instead + */ + if (*this == INFANTRY_RAMBO && action == ACTION_ATTACK && object->What_Am_I() == RTTI_BUILDING) { + return(ACTION_SABOTAGE); + } + + /* + ** There is no self-select action available for infantry types. + */ + if (action == ACTION_SELF) { + action = ACTION_NONE; + } + + /* + ** Check to see if it can enter a transporter. + */ + if ( + House->Is_Ally(object) && + IsOwnedByPlayer && + object->Is_Techno() && + IsOwnedByPlayer && + ((InfantryClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)object) == RADIO_ROGER) { +// if (object->Owner() == Owner() && object->What_Am_I() == RTTI_UNIT && ((UnitClass *)object)->Class->IsTransporter && ((UnitClass *)object)->How_Many() < 5) { + action = ACTION_ENTER; + } + + if (Class->IsCapture && action == ACTION_ATTACK) { + if (object->Owner() != Owner() && + ((object->What_Am_I() == RTTI_AIRCRAFT && ((AircraftClass *)object)->Pip_Count() == 0 && *((AircraftClass *)object) == AIRCRAFT_TRANSPORT) || + (object->What_Am_I() == RTTI_BUILDING && + ((BuildingClass *)object)->Class->IsCaptureable && (GameToPlay != GAME_NORMAL || *((BuildingClass *)object) != STRUCT_EYE || Scenario < 13) )) + ) { + + action = ACTION_CAPTURE; + } else { + if (Class->Primary == WEAPON_NONE) { + action = ACTION_NONE; + } + } + } + return(action); +} + + +/*********************************************************************************************** + * InfantryClass::Read_INI -- Reads units from scenario INI file. * + * * + * This routine is used to read all the starting units from the * + * scenario control INI file. The units are created and placed on the * + * map by this routine. * + * * + * INI entry format: * + * Housename, Typename, Strength, Cellnum, CellSublocation, Missionname, * + * Facingnum, Triggername * + * * + * INPUT: buffer -- Pointer to the loaded scenario INI file. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Read_INI(char *buffer) +{ + InfantryClass *infantry; // Working infantry pointer. + char *tbuffer; // Accumulation buffer of infantry IDs. + HousesType inhouse; // Infantry house. + InfantryType classid; // Infantry class. + int len; // Length of data in buffer. + char buf[128]; + + len = strlen(buffer) + 2; + tbuffer = buffer + len; + + /*------------------------------------------------------------------------ + Read the entire INFANTRY INI section into HIDBUF + ------------------------------------------------------------------------*/ + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); + + while (*tbuffer != '\0') { + + /* + ** Get an infantry entry + */ + WWGetPrivateProfileString(INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, buffer); + + /* + ** 1st token: house name. + */ + inhouse = HouseTypeClass::From_Name(strtok(buf, ",\n\r")); + if (inhouse != HOUSE_NONE) { + + /* + ** 2nd token: infantry type name. + */ + classid = InfantryTypeClass::From_Name(strtok(NULL, ",\n\r")); + + if (classid != INFANTRY_NONE) { + + infantry = new InfantryClass(classid, inhouse); + if (infantry) { + + /* + ** 3rd token: strength. + */ + int strength = atoi(strtok(NULL, ",\n\r")); + + /* + ** 4th token: cell #. + */ + COORDINATE coord = Cell_Coord((CELL)atoi(strtok(NULL, ",\n\r"))); + + /* + ** 5th token: cell sub-location. + */ + coord = Coord_Add(coord & 0xFF00FF00L, StoppingCoordAbs[atoi(strtok(NULL, ","))]); + + /* + ** Fetch the mission and facing. + */ + MissionType mission = MissionClass::Mission_From_Name(strtok(NULL, ",\n\r")); + DirType dir = (DirType)atoi(strtok(NULL,",\n\r")); + infantry->Trigger = TriggerClass::As_Pointer(strtok(NULL,",\n\r")); + if (infantry->Trigger) { + infantry->Trigger->AttachCount++; + } + + if (infantry->Unlimbo(coord, dir)) { + infantry->Strength = Fixed_To_Cardinal(infantry->Class_Of().MaxStrength, strength); + if (GameToPlay == GAME_NORMAL || infantry->House->IsHuman) { + infantry->Assign_Mission(mission); + infantry->Commence(); + } else { + infantry->Enter_Idle_Mode(); + } + } else { + + /* + ** If the infantry could not be unlimboed, then this is a big error. + ** Delete the infantry. + */ + delete infantry; + } + } + } + } + tbuffer += strlen(tbuffer)+1; + } +} + + +/*********************************************************************************************** + * InfantryClass::Write_INI -- Writes all the infantry out to an INI file. * + * * + * This routine writes all of the infantry in the game out to an INI file. This is used * + * in the scenario editor when the game needs to be saved. * + * * + * INI entry format: * + * Housename, Typename, Strength, Cellnum, CellSublocation, Missionname, * + * Facingnum, Triggername * + * * + * INPUT: buffer -- A pointer to the loaded INI file staging area. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Write_INI(char *buffer) +{ + int index; + char uname[10]; + char buf[128]; + char *tbuffer; // Accumulation buffer of infantry IDs. + + /* + ** First, clear out all existing infantry data from the ini file. + */ + tbuffer = buffer + strlen(buffer) + 2; + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-strlen(buffer), buffer); + while (*tbuffer != '\0') { + WWWritePrivateProfileString(INI_Name(), tbuffer, NULL, buffer); + tbuffer += strlen(tbuffer)+1; + } + + /* + ** Write the infantry data out. + */ + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * infantry; + + infantry = Infantry.Ptr(index); + if (!infantry->IsInLimbo) { + + sprintf(uname, "%03d", index); + sprintf(buf, "%s,%s,%d,%u,%d,%s,%d,%s", + infantry->House->Class->IniName, + infantry->Class->IniName, + infantry->Health_Ratio(), + Coord_Cell(infantry->Coord), + CellClass::Spot_Index(infantry->Coord), + MissionClass::Mission_Name((infantry->Mission == MISSION_NONE) ? + infantry->MissionQueue : infantry->Mission), + infantry->PrimaryFacing.Current(), + infantry->Trigger ? infantry->Trigger->Get_Name() : "None" + ); + WWWritePrivateProfileString(INI_Name(), uname, buf, buffer); + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Active_Click_With -- Handles action when clicking with infantry soldier. * + * * + * This routine is called when the player clicks over an object while this infantry soldier * + * is selected. Capture attempts are prohibited if the infantry cannot capture. The * + * command might respond if told to sabotage something. * + * * + * INPUT: action -- The action that is nominally to be performed. * + * * + * object -- The object over which the mouse was clicked. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + Validate(); + if (What_Action(object) != action) { + switch (action) { + case ACTION_SABOTAGE: + case ACTION_CAPTURE: + action = ACTION_ATTACK; + break; + + case ACTION_ENTER: + action = ACTION_MOVE; + break; + + default: + action = ACTION_NONE; + break; + } + } + + FootClass::Active_Click_With(action, object); +} + + +/*********************************************************************************************** + * InfantryClass::Made_A_Kill -- Marks a kill caused by this infantry soldier. * + * * + * When the infantry soldier is responsible for a kill, this routine is called. It checks * + * to see if the soldier should make some comment or perform some action. The commando * + * infantry is most likely to respond. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of kills this infantry soldier has made. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +int InfantryClass::Made_A_Kill(void) +{ + Validate(); + if (*this == INFANTRY_RAMBO || Random_Pick(0, 5) < Kills) { + IsStoked = true; + Comment = TICKS_PER_SECOND*2; + } + return(FootClass::Made_A_Kill()); +} + + +/*********************************************************************************************** + * InfantryClass::Set_Occupy_Bit -- Sets the occupy bit cell and bit pos * + * * + * INPUT: CELL - the cell we are setting the bit in * + * * + * int - the spot index we are setting the bit for * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/08/1995 PWG : Created. * + *=============================================================================================*/ +void InfantryClass::Set_Occupy_Bit(CELL cell, int spot_index) +{ + Validate(); + /* + ** Set the occupy postion for the spot that we passed in + */ + Map[cell].Flag.Composite |= (1 << spot_index); + + /* + ** Record the type of infantry that now owns the cell + */ + Map[cell].InfType = Owner(); +} + + +/*************************************************************************** + * InfantryClass::Clear_Occupy_Bit -- Clears occupy bit and given cell * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/08/1995 PWG : Created. * + *=========================================================================*/ +void InfantryClass::Clear_Occupy_Bit(CELL cell, int spot_index) +{ + Validate(); + /* + ** Clear the occupy bit for the infantry in that cell + */ + Map[cell].Flag.Composite &= ~(1 << spot_index); + + /* + ** If he was the last infantry recorded in the cell then + ** remove the infantry ownership flag. + */ + if (!(Map[cell].Flag.Composite & 0x1F)) { + Map[cell].InfType = HOUSE_NONE; + } +} + + +/*********************************************************************************************** + * InfantryClass::Full_Name -- Fetches the full name of the infantry unit. * + * * + * This routine will return with the full name (as a text number) for this infantry * + * unit. Typically, this is the normal name, but in cases of civilian type survivors from * + * a building explosion, it might be a technician instead. In such a case, the special * + * technician name number is returned instead. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the full name to use for this infantry unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + *=============================================================================================*/ +int InfantryClass::Full_Name(void) const +{ + Validate(); + if (IsTechnician) { + return(TXT_TECHNICIAN); + } + return(Class->Full_Name()); +} + + +/*********************************************************************************************** + * InfantryClass::Mission_Attack -- Intercept attack mission for special handling. * + * * + * This routine intercepts the normal attack mission and if an engineer is detected and the * + * target is a building, then the engineer will be automatically assigned the capture * + * mission. In other cases, the normal attack logic will proceed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/07/1995 JLB : Created. * + *=============================================================================================*/ +int InfantryClass::Mission_Attack(void) +{ + Validate(); + if (*this == INFANTRY_E7 && As_Building(TarCom)) { + Assign_Destination(TarCom); + Assign_Mission(MISSION_CAPTURE); + return(1); + } + return(FootClass::Mission_Attack()); +} + + +RTTIType InfantryClass::What_Am_I(void) const +{ + Validate(); + return(RTTI_INFANTRY); +} + +ActionType InfantryClass::What_Action(CELL cell) const +{ + Validate(); + return FootClass::What_Action(cell); +} + +ObjectTypeClass const & InfantryClass::Class_Of(void) const +{ + Validate(); + return(*Class); +} + +bool InfantryClass::Is_Infantry(void) const +{ + Validate(); + return(true); +} diff --git a/INFANTRY.H b/INFANTRY.H new file mode 100644 index 0000000..072069b --- /dev/null +++ b/INFANTRY.H @@ -0,0 +1,244 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\infantry.h_v 2.18 16 Oct 1995 16:48:08 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INFANTRY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 15, 1994 * + * * + * Last Update : August 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef INFANTRY_H +#define INFANTRY_H + +/********************************************************************** +** Infantry can be afraid. These defines are for the various infantry +** fear levels. When infantry be come scared enough they take cover and +** even run away in panic. +*/ +#define FEAR_ANXIOUS 10 // Something makes them scared. +#define FEAR_SCARED 100 // Scared enough to take cover. +#define FEAR_PANIC 200 // Run away! Run away! +#define FEAR_MAXIMUM 255 // Scared to death. + + +class InfantryClass : public FootClass +{ + public: + InfantryTypeClass const * const Class; + operator InfantryType(void) const {return Class->Type;}; + + /* + ** If the infantry is undergoing some choreographed animation sequence, then + ** this holds the particular sequence number. The frame of animation is kept + ** track of by the regular frame tracking system. When performing an animation + ** sequence, the infantry cannot perform anything else (even move). + */ + DoType Doing; + + /* + ** Certain infantry will either perform some comment or say something after an + ** amount of time has expired subsiquent to an significant event. This is the + ** timer the counts down. + */ + TCountDownTimerClass Comment; + + /* + ** If this civilian is actually a technician, then this flag will be true. + ** It should only be set for the civilian type infantry. Typically, the + ** technician appears after a building is destroyed. + */ + unsigned IsTechnician:1; + + /* + ** If the infantry just performed some feat, then it may respond with an action. + ** This flag will be true if an action is to be performed when the Comment timer + ** has expired. + */ + unsigned IsStoked:1; + + /* + ** This flag indicates if the infantry unit is prone. Prone infantry become that way + ** when they are fired upon. Infantry in the prone position are less vulnerable to + ** combat. + */ + unsigned IsProne:1; + + /* + ** This flag is set when the infantryman is engaged in hand-to-hand + ** combat. By setting this flag, it'll play the put-down-the-gun + ** sequence only once, and it'll know to pick up the gun when the + ** fight is over. + */ + unsigned IsBoxing:1; + + /* + ** The fear rating of this infantry unit. The more afraid the infantry, the more + ** likely it is to panic and seek cover. + */ + unsigned char Fear; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + static void * operator new(size_t size); + static void operator delete(void *ptr); + InfantryClass(void); + InfantryClass(InfantryType classid, HousesType house); + virtual ~InfantryClass(void); + virtual RTTIType What_Am_I(void) const; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + static void Init(void); + + virtual void Assign_Destination(TARGET); + + /* + ** Query functions. + */ + virtual bool Is_Infantry(void) const; + virtual ObjectTypeClass const & Class_Of(void) const; + virtual int Full_Name(void) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Fire_Coord(int which) const; + + /* + ** Object entry and exit from the game system. + */ + virtual bool Unlimbo(COORDINATE coord, DirType facing); + virtual bool Limbo(void); + virtual void Detach(TARGET target, bool all); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual short const * Overlap_List(void) const; + virtual void Draw_It(int x, int y, WindowNumberType window); + virtual void Look(bool incremental=false); + + /* + ** User I/O. + */ + virtual void Response_Select(void); + virtual void Response_Move(void); + virtual void Response_Attack(void); + virtual void Active_Click_With(ActionType action, ObjectClass * object); + + /* + ** Combat related. + */ + virtual int Made_A_Kill(void); + virtual ActionType What_Action(ObjectClass * object) const; + virtual ActionType What_Action(CELL cell) const; + virtual void Assign_Mission(MissionType order); + virtual BulletClass * Fire_At(TARGET target, int which); + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0); + virtual TARGET As_Target(void) const; + virtual FireErrorType Can_Fire(TARGET target, int which) const; + virtual void Assign_Target(TARGET); + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual int Rearm_Delay(bool second) const; + void Set_Occupy_Bit(COORDINATE coord) {Set_Occupy_Bit(Coord_Cell(coord), CellClass::Spot_Index(coord));}; + void Set_Occupy_Bit(CELL cell, int spot_index); + void Clear_Occupy_Bit(COORDINATE coord) {Clear_Occupy_Bit(Coord_Cell(coord), CellClass::Spot_Index(coord));}; + void Clear_Occupy_Bit(CELL cell, int spot_index); + + /* + ** Driver control support functions. These are used to control cell + ** occupation flags and driver instructions. + */ + virtual bool Stop_Driver(void); + virtual bool Start_Driver(COORDINATE & coord); + + /* + ** AI. + */ + virtual void AI(void); + virtual TARGET Greatest_Threat(ThreatType threat) const; + virtual int Mission_Attack(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** File I/O. + */ + static void Read_INI(char *buffer); + static void Write_INI(char *buffer); + static char *INI_Name(void) {return "INFANTRY";}; + bool Load(FileClass & file); + bool Save(FileClass & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Movement and animation. + */ + virtual bool Do_Action(DoType todo, bool force=false); + virtual void Random_Animate(void); + virtual MoveType Can_Enter_Cell(CELL , FacingType =FACING_NONE) const; + virtual void Per_Cell_Process(bool center); + virtual void Enter_Idle_Mode(bool initial=false); + virtual void Scatter(COORDINATE threat, bool forced =false); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + /* + ** Translation table to convert facing into infantry shape number. This special + ** table is needed since several facing stages are reused and flipped about the Y + ** axis. + */ + static int const HumanShape[32]; + + private: + + static DoStruct const MasterDoControls[DO_COUNT]; + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; +}; + +#endif diff --git a/INI.CPP b/INI.CPP new file mode 100644 index 0000000..02358d5 --- /dev/null +++ b/INI.CPP @@ -0,0 +1,1664 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\ini.cpv 2.18 16 Oct 1995 16:48:50 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INI.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : July 30, 1995 [BRR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Assign_Houses -- Assigns multiplayer houses to various players * + * Clear_Flag_Spots -- Clears flag overlays off the map * + * Clip_Move -- moves in given direction from given cell; clips to map * + * Clip_Scatter -- randomly scatters from given cell; won't fall off map * + * Create_Units -- Creates infantry & units, for non-base multiplayer * + * Furthest_Cell -- Finds cell furthest from a group of cells * + * Place_Flags -- Places flags for multiplayer games * + * Read_Scenario_Ini -- Read specified scenario INI file. * + * Remove_AI_Players -- Removes the computer AI houses & their units * + * Scan_Place_Object -- places an object >near< the given cell * + * Set_Scenario_Name -- Creates the INI scenario name string. * + * Sort_Cells -- sorts an array of cells by distance * + * Write_Scenario_Ini -- Write the scenario INI file. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/************************************* Prototypes *********************************************/ +static void Assign_Houses(void); +static void Remove_AI_Players(void); +static void Create_Units(void); +static void Sort_Cells(CELL *cells, int numcells, CELL *outcells); +static int Furthest_Cell(CELL *cells, int numcells, CELL *tcells, int numtcells); +static CELL Clip_Scatter(CELL cell, int maxdist); +static CELL Clip_Move(CELL cell, FacingType facing, int dist); + + +/*********************************************************************************************** + * Set_Scenario_Name -- Creates the INI scenario name string. * + * * + * This routine is used by the scenario loading and saving code. It generates the scenario * + * INI root file name for the specified scenario parameters. * + * * + * INPUT: * + * buf buffer to store filename in; must be long enough for root.ext * + * scenario scenario number * + * player player type for this game (GDI, NOD, multi-player, ...) * + * dir directional parameter for this game (East/West) * + * var variation of this game (Lose, A/B/C/D, etc) * + * * + * OUTPUT: none. * + * * + * WARNINGS: none. * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + * 05/01/1995 BRR : 2-player scenarios use same names as multiplayer * + *=============================================================================================*/ +void Set_Scenario_Name(char *buf, int scenario, ScenarioPlayerType player, ScenarioDirType dir, ScenarioVarType var) +{ + char c_player; // character representing player type + char c_dir; // character representing direction type + char c_var; // character representing variation type + ScenarioVarType i; + char fname[_MAX_FNAME+_MAX_EXT]; + + /* + ** Set the player-type value. + */ + switch (player) { + case SCEN_PLAYER_GDI: + c_player = HouseTypeClass::As_Reference(HOUSE_GOOD).Prefix; +// c_player = 'G'; + break; + + case SCEN_PLAYER_NOD: + c_player = HouseTypeClass::As_Reference(HOUSE_BAD).Prefix; +// c_player = 'B'; + break; + + case SCEN_PLAYER_JP: + c_player = HouseTypeClass::As_Reference(HOUSE_JP).Prefix; +// c_player = 'J'; + break; + + /* + ** Multi player scenario. + */ + default: + c_player = HouseTypeClass::As_Reference(HOUSE_MULTI1).Prefix; +// c_player = 'M'; + break; + } + + /* + ** Set the directional character value. + ** If SCEN_DIR_NONE is specified, randomly pick a direction; otherwise, use 'E' or 'W' + */ + switch (dir) { + case SCEN_DIR_EAST: + c_dir = 'E'; + break; + + case SCEN_DIR_WEST: + c_dir = 'W'; + break; + + default: + case SCEN_DIR_NONE: + c_dir = (Random_Pick(0, 1) == 0) ? 'W' : 'E'; + break; + } + + /* + ** Set the variation value. + */ + if (var == SCEN_VAR_NONE) { + + /* + ** Find which variations are available for this scenario + */ + for (i = SCEN_VAR_FIRST; i < SCEN_VAR_COUNT; i++) { + sprintf(fname, "SC%c%02d%c%c.INI", c_player, scenario, c_dir, 'A' + i); + if (!CCFileClass(fname).Is_Available()) { + break; + } + } + + if (i==SCEN_VAR_FIRST) { + c_var = 'X'; // indicates an error + } else { + c_var = 'A' + Random_Pick(0, i-1); + } + } else { + switch (var) { + case SCEN_VAR_A: + c_var = 'A'; + break; + + case SCEN_VAR_B: + c_var = 'B'; + break; + + case SCEN_VAR_C: + c_var = 'C'; + break; + + case SCEN_VAR_D: + c_var = 'D'; + break; + + default: + c_var = 'L'; + break; + + } + } + + /* + ** generate the filename + */ + sprintf(buf, "SC%c%02d%c%c", c_player, scenario, c_dir, c_var); +} + + +/*********************************************************************************************** + * Read_Scenario_Ini -- Read specified scenario INI file. * + * * + * Read in the scenario INI file. This routine only sets the game * + * globals with that data that is explicitly defined in the INI file. * + * The remaining necessary interpolated data is generated elsewhere. * + * * + * INPUT: * + * root root filename for scenario file to read * + * * + * fresh true = should the current scenario be cleared? * + * * + * OUTPUT: bool; Was the scenario read successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +bool Read_Scenario_Ini(char *root, bool fresh) +{ + char *buffer; // Scenario.ini staging buffer pointer. + char fname[_MAX_FNAME+_MAX_EXT]; // full INI filename + char buf[128]; // Working string staging buffer. + int rndmax; + int rndmin; + int len; + unsigned char val; + + ScenarioInit++; + + /* + ** Fetch working pointer to the INI staging buffer. Make sure that the buffer + ** is cleared out before proceeding. (Don't use the HidPage for this, since + ** the HidPage may be needed for various uncompressions during the INI + ** parsing.) + */ + buffer = (char *)_ShapeBuffer; + memset(buffer, '\0', _ShapeBufferSize); + + if (fresh) { + Clear_Scenario(); + } + + /* + ** If we are not dealing with scenario 1, or a multi player scenario + ** then make sure the correct disk is in the drive. + */ + if (RequiredCD != -2) { + if (Scenario >= 20 && Scenario <60 && GameToPlay == GAME_NORMAL) { + RequiredCD = 2; + } else { + if (Scenario != 1) { + if (Scenario >=60){ + RequiredCD = -1; + }else{ + switch (ScenPlayer) { + case SCEN_PLAYER_GDI: + RequiredCD = 0; + break; + case SCEN_PLAYER_NOD: + RequiredCD = 1; + break; + default: + RequiredCD = -1; + break; + } + } + } else { + RequiredCD = -1; + } + } + } + if (!Force_CD_Available(RequiredCD)) { + Prog_End(); + exit(EXIT_FAILURE); + } + + /* + ** Create scenario filename and read the file. + */ + + sprintf(fname,"%s.INI",root); + CCFileClass file(fname); + if (!file.Is_Available()) { + return(false); + } else { + file.Read(buffer, _ShapeBufferSize-1); + } + + /* + ** Init the Scenario CRC value + */ + ScenarioCRC = 0; + len = strlen(buffer); + for (int i = 0; i < len; i++) { + val = (unsigned char)buffer[i]; +#ifndef DEMO + Add_CRC(&ScenarioCRC, (unsigned long)val); +#endif + } + + /* + ** Fetch the appropriate movie names from the INI file. + */ + WWGetPrivateProfileString("Basic", "Intro", "x", IntroMovie, sizeof(IntroMovie), buffer); + WWGetPrivateProfileString("Basic", "Brief", "x", BriefMovie, sizeof(BriefMovie), buffer); + WWGetPrivateProfileString("Basic", "Win", "x", WinMovie, sizeof(WinMovie), buffer); + WWGetPrivateProfileString("Basic", "Lose", "x", LoseMovie, sizeof(LoseMovie), buffer); + WWGetPrivateProfileString("Basic", "Action", "x", ActionMovie, sizeof(ActionMovie), buffer); + + /* + ** For single-player scenarios, 'BuildLevel' is the scenario number. + ** This must be set before any buildings are created (if a factory is created, + ** it needs to know the BuildLevel for the sidebar.) + */ + if (GameToPlay == GAME_NORMAL) { +#ifdef NEWMENU + if (Scenario <= 15) { + BuildLevel = Scenario; + } else { + BuildLevel = WWGetPrivateProfileInt("Basic", "BuildLevel", Scenario, buffer); + } +#else + BuildLevel = Scenario; +#endif + } + + /* + ** Jurassic scenarios are allowed to build the full multiplayer set + ** of objects. + */ + if (Special.IsJurassic && AreThingiesEnabled) { + BuildLevel = 98; + } + + /* + ** Fetch the transition theme for this scenario. + */ + TransitTheme = THEME_NONE; + WWGetPrivateProfileString("Basic", "Theme", "No Theme", buf, sizeof(buf), buffer); + TransitTheme = Theme.From_Name(buf); + + /* + ** Read in the team-type data. The team types must be created before any + ** triggers can be created. + */ + TeamTypeClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in the specific information for each of the house types. This creates + ** the houses of different types. + */ + HouseClass::Read_INI(buffer); + Call_Back(); + + /* + ** Assign PlayerPtr by reading the player's house from the INI; + ** Must be done before any TechnoClass objects are created. + */ +// if (GameToPlay == GAME_NORMAL && (ScenPlayer == SCEN_PLAYER_GDI || ScenPlayer == SCEN_PLAYER_NOD)) { + if (GameToPlay == GAME_NORMAL) { + WWGetPrivateProfileString("Basic", "Player", "GoodGuy", buf, 127, buffer); + CarryOverPercent = WWGetPrivateProfileInt("Basic", "CarryOverMoney", 100, buffer); + CarryOverPercent = Cardinal_To_Fixed(100, CarryOverPercent); + CarryOverCap = WWGetPrivateProfileInt("Basic", "CarryOverCap", -1, buffer); + + PlayerPtr = HouseClass::As_Pointer(HouseTypeClass::From_Name(buf)); + PlayerPtr->IsHuman = true; + int carryover; + if (CarryOverCap != -1) { + carryover = MIN(Fixed_To_Cardinal(CarryOverMoney, CarryOverPercent), CarryOverCap); + } else { + carryover = Fixed_To_Cardinal(CarryOverMoney, CarryOverPercent); + } + PlayerPtr->Credits += carryover; + PlayerPtr->InitialCredits += carryover; + + if (Special.IsJurassic) { + PlayerPtr->ActLike = Whom; + } + } else { + +#ifdef OBSOLETE + if (GameToPlay==GAME_NORMAL && ScenPlayer==SCEN_PLAYER_JP) { + PlayerPtr = HouseClass::As_Pointer(HOUSE_MULTI4); + PlayerPtr->IsHuman = true; + PlayerPtr->Credits += CarryOverMoney; + PlayerPtr->InitialCredits += CarryOverMoney; + PlayerPtr->ActLike = Whom; + } else { + Assign_Houses(); + } +#endif + Assign_Houses(); + } + + /* + ** Read in the trigger data. The triggers must be created before any other + ** objects can be initialized. + */ + TriggerClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in the map control values. This includes dimensions + ** as well as theater information. + */ + Map.Read_INI(buffer); + Call_Back(); + + /* + ** Attempt to read the map's binary image file; if fails, read the + ** template data from the INI, for backward compatibility + */ + if (fresh) { + if (!Map.Read_Binary(root, &ScenarioCRC)) { + TemplateClass::Read_INI(buffer); + } + } + Call_Back(); + + /* + ** Read in and place the 3D terrain objects. + */ + TerrainClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in and place the units (all sides). + */ + UnitClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in and place the infantry units (all sides). + */ + InfantryClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in and place all the buildings on the map. + */ + BuildingClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in the AI's base information. + */ + Base.Read_INI(buffer); + Call_Back(); + + /* + ** Read in any normal overlay objects. + */ + OverlayClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in any smudge overlays. + */ + SmudgeClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in any briefing text. + */ + char * stage = &BriefingText[0]; + *stage = '\0'; + int index = 1; + + /* + ** Build the full text of the mission objective. + */ + for (;;) { + char buff[16]; + + sprintf(buff, "%d", index++); + *stage = '\0'; + WWGetPrivateProfileString("Briefing", buff, "", stage, (sizeof(BriefingText)-strlen(BriefingText))-1, buffer); + if (strlen(stage) == 0) break; + strcat(stage, " "); + stage += strlen(stage); + } + + /* + ** If the briefing text could not be found in the INI file, then search + ** the mission.ini file. + */ + if (BriefingText[0] == '\0') { + memset(_ShapeBuffer, '\0', _ShapeBufferSize); + CCFileClass("MISSION.INI").Read(_ShapeBuffer, _ShapeBufferSize); + + char * buffer = (char *)Add_Long_To_Pointer(_ShapeBuffer, strlen(_ShapeBuffer)); + char * work = &BriefingText[0]; + int index = 1; + + /* + ** Build the full text of the mission objective. + */ + for (;;) { + char buff[16]; + + sprintf(buff, "%d", index++); + *work = '\0'; + WWGetPrivateProfileString(root, buff, "", work, (sizeof(BriefingText)-strlen(BriefingText))-1, _ShapeBuffer); + if (strlen(work) == 0) break; + strcat(work, " "); + work += strlen(work); + } + } + + /* + ** Perform a final overpass of the map. This handles smoothing of certain + ** types of terrain (tiberium). + */ + Map.Overpass(); + Call_Back(); + + /* + ** Multi-player last-minute fixups: + ** - If computer players are disabled, remove all computer-owned houses + ** - Otherwise, set MPlayerBlitz to 0 or 1, randomly + ** - If bases are disabled, create the scenario dynamically + ** - Remove any flag spot overlays lying around + ** - If capture-the-flag is enabled, assign flags to cells. + */ + if (GameToPlay != GAME_NORMAL || ScenPlayer == SCEN_PLAYER_2PLAYER || + ScenPlayer == SCEN_PLAYER_MPLAYER) { + + /* + ** If Ghosts are disabled and we're not editing, remove computer players + ** (Must be done after all objects are read in from the INI) + */ + if (!MPlayerGhosts && !Debug_Map) { + Remove_AI_Players(); + } else { + + /* + ** If Ghosts are on, set up their houses for blitzing the humans + */ + MPlayerBlitz = IRandom (0,1); // 1 = computer will blitz + if (MPlayerBlitz) { + if (MPlayerBases) { + rndmax = 14000; + rndmin = 10000; + } else { + rndmax = 8000; + rndmin = 4000; + } + + for (i = 0; i < MPlayerMax; i++) { + HousesType house = (HousesType)(i + (int)HOUSE_MULTI1); + HouseClass *housep = HouseClass::As_Pointer (house); + housep->BlitzTime = IRandom (rndmin,rndmax); + } + + } + } + + /* + ** Units must be created for each house. If bases are ON, this routine + ** will create an MCV along with the units; otherwise, it will just create + ** a whole bunch of units. MPlayerUnitCount is the total # of units + ** to create. + */ + if (!Debug_Map) { + int save_init = ScenarioInit; // turn ScenarioInit off + ScenarioInit = 0; + Create_Units(); + ScenarioInit = save_init; // turn ScenarioInit back on + } + + /* + ** Place crates if MPlayerGoodies is on. + */ + if (MPlayerGoodies) { + for (int index = 0; index < MPlayerCount; index++) { + Map.Place_Random_Crate(); + } + } + + /* + ** Compute my starting location as the average Coord of all my stuff. + */ + Map.Compute_Start_Pos(); + } + + Call_Back(); + + /* + ** Return with flag saying that the scenario file was read. + */ + ScenarioInit--; + return(true); +} + + +/*********************************************************************************************** + * Write_Scenario_Ini -- Write the scenario INI file. * + * * + * INPUT: * + * root root filename for the scenario * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 05/11/1995 JLB : Updates movie data. * + *=============================================================================================*/ +void Write_Scenario_Ini(char *root) +{ +#ifndef CHEAT_KEYS + root = root; +#else + char * buffer; // Scenario.ini staging buffer pointer. + char fname[_MAX_FNAME+_MAX_EXT]; // full scenario name + HousesType house; + CCFileClass file; + + /* + ** Get a working pointer to the INI staging buffer. Make sure that the buffer + ** starts cleared out of any data. + */ + buffer = (char *)_ShapeBuffer; + memset(buffer, '\0', _ShapeBufferSize); + + switch (ScenPlayer) { + case SCEN_PLAYER_GDI: + house = HOUSE_GOOD; + break; + + case SCEN_PLAYER_NOD: + house = HOUSE_BAD; + break; + + case SCEN_PLAYER_JP: + house = HOUSE_JP; + break; + + default: + house = HOUSE_MULTI1; + break; + } + + /* + ** Create scenario filename and clear the buffer to empty. + */ + sprintf(fname,"%s.INI",root); + file.Set_Name(fname); + if (file.Is_Available()) { +// file.Open(READ); + file.Read(buffer, _ShapeBufferSize-1); +// file.Close(); + } else { + sprintf(buffer, "; Scenario %d control for house %s.\r\n", Scenario, HouseTypeClass::As_Reference(house).IniName); + } + + WWWritePrivateProfileString("Basic", "Intro", IntroMovie, buffer); + WWWritePrivateProfileString("Basic", "Brief", BriefMovie, buffer); + WWWritePrivateProfileString("Basic", "Win", WinMovie, buffer); + WWWritePrivateProfileString("Basic", "Lose", LoseMovie, buffer); + WWWritePrivateProfileString("Basic", "Action", ActionMovie, buffer); + WWWritePrivateProfileString("Basic", "Player", PlayerPtr->Class->IniName, buffer); + WWWritePrivateProfileString("Basic", "Theme", Theme.Base_Name(TransitTheme), buffer); + WWWritePrivateProfileInt("Basic", "BuildLevel", BuildLevel, buffer); + WWWritePrivateProfileInt("Basic", "CarryOverMoney", Fixed_To_Cardinal(100, CarryOverPercent), buffer); + WWWritePrivateProfileInt("Basic", "CarryOverCap", CarryOverCap, buffer); + + TeamTypeClass::Write_INI(buffer, true); + TriggerClass::Write_INI(buffer, true); + Map.Write_INI(buffer); + Map.Write_Binary(root); + HouseClass::Write_INI(buffer); + UnitClass::Write_INI(buffer); + InfantryClass::Write_INI(buffer); + BuildingClass::Write_INI(buffer); + TerrainClass::Write_INI(buffer); + OverlayClass::Write_INI(buffer); + SmudgeClass::Write_INI(buffer); + + Base.Write_INI(buffer); + + /* + ** Write the scenario data out to a file. + */ +// file.Open(WRITE); + file.Write(buffer, strlen(buffer)); +// file.Close(); + + /* + ** Now update the Master INI file, containing the master list of triggers & teams + */ + memset(buffer, '\0', _ShapeBufferSize); + + file.Set_Name("MASTER.INI"); + if (file.Is_Available()) { +// file.Open(READ); + file.Read(buffer, _ShapeBufferSize-1); +// file.Close(); + } else { + sprintf(buffer, "; Master Trigger & Team List.\r\n"); + } + + TeamTypeClass::Write_INI(buffer, false); + TriggerClass::Write_INI(buffer, false); + +// file.Open(WRITE); + file.Write(buffer,strlen(buffer)); +// file.Close(); +#endif +} + + +/*********************************************************************************************** + * Assign_Houses -- Assigns multiplayer houses to various players * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/09/1995 BRR : Created. * + * 07/14/1995 JLB : Records name of player in house structure. * + *=============================================================================================*/ +static void Assign_Houses(void) +{ + HousesType house; + HousesType pref_house; + HouseClass *housep; + bool house_used[MAX_PLAYERS]; // true = this house is in use + bool color_used[6]; // true = this color is in use + int i,j; + PlayerColorType color; + HousesType house2; + HouseClass *housep2; + +char wibble [256]; +sprintf (wibble, "C&C95 - In 'Assign_Houses'. Number of players:%d\n",MPlayerCount); +CCDebugString (wibble); + + /* + ** Init the 'used' flag for all houses & colors to 0 + */ + for (i = 0; i < MAX_PLAYERS; i++) { + house_used[i] = false; + } + for (i = 0; i < 6; i++) { + color_used[i] = false; + } + + /* + ** For each player, randomly pick a house + */ + for (i = 0; i < MPlayerCount; i++) { + j = Random_Pick(0, MPlayerMax-1); + + /* + ** If this house was already selected, decrement 'i' & keep looping. + */ + if (house_used[j]) { + i--; + continue; + } + + /* + ** Set the house, preferred house (GDI/NOD), color, and actual house; + ** get a pointer to the house instance + */ + house = (HousesType)(j + (int)HOUSE_MULTI1); + pref_house = MPlayerID_To_HousesType(MPlayerID[i]); + color = MPlayerID_To_ColorIndex(MPlayerID[i]); + housep = HouseClass::As_Pointer(house); + MPlayerHouses[i] = house; + + /* + ** Mark this house & color as used + */ + house_used[j] = true; + color_used[color] = true; + + /* + ** Set the house's IsHuman, Credits, ActLike, & RemapTable + */ + memset((char *)housep->Name, 0, MPLAYER_NAME_MAX); + strncpy((char *)housep->Name, MPlayerNames[i], MPLAYER_NAME_MAX-1); + housep->IsHuman = true; + housep->Init_Data(color, pref_house, MPlayerCredits); + + /* + ** If this ID is for myself, set up PlayerPtr + */ + if (MPlayerID[i] == MPlayerLocalID) { + PlayerPtr = housep; + } + } + + /* + ** For all houses not assigned to a player, set them up for computer use + */ + for (i = 0; i < MPlayerMax; i++) { + if (house_used[i] == false) { + + /* + ** Set the house, preferred house (GDI/NOD), and color; get a pointer + ** to the house instance + */ + house = (HousesType)(i + (int)HOUSE_MULTI1); + pref_house = (HousesType)(IRandom(0, 1) + (int)HOUSE_GOOD); + for (;;) { + color = Random_Pick(REMAP_FIRST, REMAP_LAST); + if (color_used[color] == false) { + break; + } + } + housep = HouseClass::As_Pointer (house); + + /* + ** Mark this house & color as used + */ + house_used[i] = true; + color_used[color] = true; + + /* + ** Set the house's IsHuman, Credits, ActLike, & RemapTable + */ + housep->IsHuman = false; + housep->Init_Data(color, pref_house, MPlayerCredits); + } + } + + /* + ** Now make all computer-owned houses allies of each other. + */ + for (house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + MPlayerMax); house++) { + housep = HouseClass::As_Pointer(house); + if (housep->IsHuman) + continue; + + for (house2 = HOUSE_MULTI1; house2 < (HOUSE_MULTI1 + MPlayerMax); house2++) { + housep2 = HouseClass::As_Pointer (house2); + if (housep2->IsHuman) + continue; + housep->Make_Ally(house2); + } + } +} + + +/*********************************************************************************************** + * Remove_AI_Players -- Removes the computer AI houses & their units * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/09/1995 BRR : Created. * + *=============================================================================================*/ +static void Remove_AI_Players(void) +{ + int i; + HousesType house; + HouseClass *housep; + + for (i = 0; i < MAX_PLAYERS; i++) { + house = (HousesType)(i + (int)HOUSE_MULTI1); + housep = HouseClass::As_Pointer (house); + if (housep->IsHuman == false) { + housep->Clobber_All(); + } + } +} + + +/*********************************************************************************************** + * Create_Units -- Creates infantry & units, for non-base multiplayer * + * * + * This routine uses data tables to determine which units to create for either * + * a GDI or NOD house, and how many of each. * + * * + * It also sets each house's FlagHome & FlagLocation to the Waypoint selected * + * as that house's "home" cell. * + * * + * ------------------ Unit Summary: ------------------------------- * + * UNIT_MTANK Medium tank (M1). GDI 7 * + * UNIT_JEEP 4x4 jeep replacement. GDI 5 * + * UNIT_MLRS MLRS rocket launcher. GDI 99 * + * UNIT_APC APC. GDI 10 * + * UNIT_HTANK Heavy tank (Mammoth). GDI 13 * + * * + * UNIT_LTANK Light tank ('Bradly'). NOD 5 * + * UNIT_BUGGY Rat patrol dune buggy type NOD 5 * + * UNIT_ARTY Artillery unit. NOD 10 * + * UNIT_FTANK Flame thrower tank. NOD 11 * + * UNIT_STANK Stealth tank (Romulan). NOD 13 * + * UNIT_BIKE Nod recon motor-bike. NOD 99 * + * * + * ~1/3 chance of getting: {UNIT_MHQ, Mobile Head Quarters. * + * * + * ------------------ Infantry Summary: ------------------------------- * + * INFANTRY_E1, Mini-gun armed. GDI/NOD * + * INFANTRY_E2, Grenade thrower. GDI * + * INFANTRY_E3, Rocket launcher. NOD * + * INFANTRY_E6, Rocket launcher GDI * + * INFANTRY_E4, Flame thrower equipped. NOD * + * INFANTRY_RAMBO, Commando. GDI/NOD * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/09/1995 BRR : Created. * + *=============================================================================================*/ +static void Create_Units(void) +{ + enum { + NUM_UNIT_CATEGORIES = 8, + NUM_INFANTRY_CATEGORIES = 5, + }; + + static struct { + int MinLevel; + int GDICount; + UnitType GDIType; + int NODCount; + UnitType NODType; + } utable[] = { + {0, 1,UNIT_MTANK, 2,UNIT_LTANK}, + {2, 1,UNIT_JEEP, 1,UNIT_BUGGY}, + {3, 1,UNIT_MLRS, 1,UNIT_ARTY}, + {4, 1,UNIT_APC, 2,UNIT_BUGGY}, + {5, 1,UNIT_JEEP, 1,UNIT_BIKE}, + {5, 2,UNIT_JEEP, 1,UNIT_FTANK}, + {6, 1,UNIT_MSAM, 1,UNIT_MSAM}, + {7, 1,UNIT_HTANK, 2,UNIT_STANK}, + }; + static int num_units[NUM_UNIT_CATEGORIES]; // # of each type of unit to create + int tot_units; // total # units to create + + static struct { + int MinLevel; + int GDICount; + InfantryType GDIType; + int NODCount; + InfantryType NODType; + } itable[] = { + {0, 1,INFANTRY_E1, 1,INFANTRY_E1}, + {1, 1,INFANTRY_E2, 1,INFANTRY_E3}, + {3, 1,INFANTRY_E3, 1,INFANTRY_E3}, + {5, 1,INFANTRY_E3, 1,INFANTRY_E4}, + {7, 1,INFANTRY_RAMBO, 1,INFANTRY_RAMBO}, + }; + static int num_infantry[NUM_INFANTRY_CATEGORIES];// # of each type of infantry to create + int tot_infantry; // total # infantry to create + + CELL waypts[26]; + CELL sorted_waypts[26]; + int num_waypts; + + HousesType h; // house loop counter + HouseClass *hptr; // ptr to house being processed + + CELL centroid; // centroid of this house's stuff + int try_count; // # times we've tried to select a centroid + CELL centerpt; // centroid for a category of objects, as a CELL + + int u_limit; // last allowable index of units for this BuildLevel + int i_limit; // last allowable index of infantry for this BuildLevel + TechnoClass *obj; // newly-created object + int i,j,k; // loop counters + int scaleval; // value to scale # units or infantry + + /*------------------------------------------------------------------------ + For the current BuildLevel, find the max allowable index into the tables + ------------------------------------------------------------------------*/ + for (i = 0; i < NUM_UNIT_CATEGORIES; i++) { + if (BuildLevel >= utable[i].MinLevel) + u_limit = i; + } + for (i = 0; i < NUM_INFANTRY_CATEGORIES; i++) { + if (BuildLevel >= utable[i].MinLevel) + i_limit = i; + } + + /*------------------------------------------------------------------------ + Compute how many of each buildable category to create + ------------------------------------------------------------------------*/ + /*........................................................................ + Compute allowed # units + ........................................................................*/ + tot_units = (MPlayerUnitCount * 2) / 3; +// tot_units = MAX(tot_units, 1); + + /*........................................................................ + Init # of each category to 0 + ........................................................................*/ + for (i = 0; i <= u_limit; i++) + num_units[i] = 0; + + /*........................................................................ + Increment # of each category, until we've used up all units + ........................................................................*/ + j = 0; + for (i = 0; i < tot_units; i++) { + num_units[j]++; + j++; + if (j > u_limit) + j = 0; + } + + /*........................................................................ + Compute allowed # infantry + ........................................................................*/ + tot_infantry = MPlayerUnitCount - tot_units; + + /*........................................................................ + Init # of each category to 0 + ........................................................................*/ + for (i = 0; i <= i_limit; i++) + num_infantry[i] = 0; + + /*........................................................................ + Increment # of each category, until we've used up all infantry + ........................................................................*/ + j = 0; + for (i = 0; i < tot_infantry; i++) { + num_infantry[j]++; + j++; + if (j > i_limit) + j = 0; + } + + /*------------------------------------------------------------------------ + Now sort all the Waypoints on the map by distance. + ------------------------------------------------------------------------*/ + num_waypts = 0; // counts # waypoints + + /*........................................................................ + First, copy all valid waytpoints into my 'waypts' array + ........................................................................*/ + for (i = 0; i < 26; i++) { + if (Waypoint[i] != -1) { + waypts[num_waypts] = Waypoint[i]; + num_waypts++; + } + } + + /*........................................................................ + Now sort the 'waypts' array + ........................................................................*/ + Sort_Cells (waypts, num_waypts, sorted_waypts); + + /*------------------------------------------------------------------------ + Loop through all houses. Computer-controlled houses, with MPlayerBases + ON, are treated as though bases are OFF (since we have no base-building + AI logic.) + ------------------------------------------------------------------------*/ + for (h = HOUSE_MULTI1; h < (HOUSE_MULTI1 + MPlayerMax); h++) { + + /*..................................................................... + Get a pointer to this house; if there is none, go to the next house + .....................................................................*/ + hptr = HouseClass::As_Pointer(h); + if (!hptr) + continue; + + /*..................................................................... + Pick a random waypoint; if the chosen waypoint isn't valid, try again. + 'centroid' will be the centroid of all this house's stuff. + .....................................................................*/ + try_count = 0; + while (1) { + j = IRandom(0,MPlayerMax - 1); + if (sorted_waypts[j] != -1) { + centroid = sorted_waypts[j]; + sorted_waypts[j] = -1; + break; + } + try_count++; + + /*.................................................................. + OK, we've tried enough; just pick any old cell at random, as long + as it's mappable. + ..................................................................*/ + if (try_count > 200) { + while (1) { + centroid = IRandom(0,MAP_CELL_TOTAL - 1); + if (Map.In_Radar(centroid)) + break; + } + break; + } + } + + /*--------------------------------------------------------------------- + If Bases are ON, human & computer houses are treated differently + ---------------------------------------------------------------------*/ + if (MPlayerBases) { + /*.................................................................. + - For a human-controlled house: + - Set 'scaleval' to 1 + - Create an MCV + - Attach a flag to it for capture-the-flag mode + ..................................................................*/ + if (hptr->IsHuman) { + scaleval = 1; + obj = new UnitClass (UNIT_MCV, h); + if (!obj->Unlimbo(Cell_Coord(centroid),DIR_N)) { + if (!Scan_Place_Object(obj, centroid)) { + delete obj; + obj = NULL; + } + } + if (obj) { + hptr->FlagHome = 0; + hptr->FlagLocation = 0; + if (Special.IsCaptureTheFlag) { + hptr->Flag_Attach((UnitClass *)obj,true); + } + } + } else { + + /*.................................................................. + - For computer-controlled house: + - Set 'scaleval' to 3 + - Create a Mobile HQ for capture-the-flag mode + ..................................................................*/ + scaleval = 3 / (MPlayerMax - MPlayerCount); + if (scaleval==0) { + scaleval = 1; + } + + if (Special.IsCaptureTheFlag) { + obj = new UnitClass (UNIT_MHQ, h); + if (!obj->Unlimbo(Cell_Coord(centroid),DIR_N)) { + if (!Scan_Place_Object(obj, centroid)) { + delete obj; + obj = NULL; + } + } + hptr->FlagHome = 0; // turn house's flag off + hptr->FlagLocation = 0; + } + } + } else { + + /*--------------------------------------------------------------------- + If bases are OFF, set 'scaleval' to 1 & create a Mobile HQ for + capture-the-flag mode. + ---------------------------------------------------------------------*/ + scaleval = 1; + if (Special.IsCaptureTheFlag) { + obj = new UnitClass (UNIT_MHQ, h); + obj->Unlimbo(Cell_Coord(centroid),DIR_N); + hptr->FlagHome = 0; // turn house's flag off + hptr->FlagLocation = 0; + } + } + + /*--------------------------------------------------------------------- + Set the house's max # units (this is used in the Mission_Timed_Hunt()) + ---------------------------------------------------------------------*/ + hptr->MaxUnit = MPlayerUnitCount * scaleval; + + /*--------------------------------------------------------------------- + Create units for this house + ---------------------------------------------------------------------*/ + for (i = 0; i <= u_limit; i++) { + /*.................................................................. + Find the center point for this category. + ..................................................................*/ + centerpt = Clip_Scatter(centroid,4); + + /*.................................................................. + Place objects; loop through all unit in this category + ..................................................................*/ + for (j = 0; j < num_units[i] * scaleval; j++) { + /*............................................................... + Create a GDI unit + ...............................................................*/ + if (hptr->ActLike == HOUSE_GOOD) { + for (k = 0; k < utable[i].GDICount; k++) { + obj = new UnitClass (utable[i].GDIType, h); + if (!Scan_Place_Object(obj, centerpt)) { + delete obj; + } else { + if (!hptr->IsHuman) { + obj->Set_Mission(MISSION_TIMED_HUNT); + } + } + } + } else { + + /*............................................................... + Create a NOD unit + ...............................................................*/ + for (k = 0; k < utable[i].NODCount; k++) { + obj = new UnitClass (utable[i].NODType, h); + if (!Scan_Place_Object(obj, centerpt)) { + delete obj; + } else { + if (!hptr->IsHuman) { + obj->Set_Mission(MISSION_TIMED_HUNT); + } + } + } + } + } + } + + /*--------------------------------------------------------------------- + Create infantry + ---------------------------------------------------------------------*/ + for (i = 0; i <= i_limit; i++) { + /*.................................................................. + Find the center point for this category. + ..................................................................*/ + centerpt = Clip_Scatter(centroid,4); + + /*.................................................................. + Place objects; loop through all unit in this category + ..................................................................*/ + for (j = 0; j < num_infantry[i] * scaleval; j++) { + /*............................................................... + Create GDI infantry (Note: Unlimbo calls Enter_Idle_Mode(), which + assigns the infantry to HUNT; we must use Set_Mission() to override + this state.) + ...............................................................*/ + if (hptr->ActLike == HOUSE_GOOD) { + for (k = 0; k < itable[i].GDICount; k++) { + obj = new InfantryClass (itable[i].GDIType, h); + if (!Scan_Place_Object(obj, centerpt)) { + delete obj; + } else { + if (!hptr->IsHuman) { + obj->Set_Mission(MISSION_TIMED_HUNT); + } + } + } + } else { + + /*............................................................... + Create NOD infantry + ...............................................................*/ + for (k = 0; k < itable[i].NODCount; k++) { + obj = new InfantryClass (itable[i].NODType, h); + if (!Scan_Place_Object(obj, centerpt)) { + delete obj; + } else { + if (!hptr->IsHuman) { + obj->Set_Mission(MISSION_TIMED_HUNT); + } + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * Scan_Place_Object -- places an object >near< the given cell * + * * + * INPUT: * + * obj ptr to object to Unlimbo * + * cell center of search area * + * * + * OUTPUT: * + * true = object was placed; false = it wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/09/1995 BRR : Created. * + *=============================================================================================*/ +int Scan_Place_Object(ObjectClass *obj, CELL cell) +{ + int dist; // for object placement + FacingType rot; // for object placement + FacingType fcounter; // for object placement + int tryval; + CELL newcell; + TechnoClass *techno; + int skipit; + + /*------------------------------------------------------------------------ + First try to unlimbo the object in the given cell. + ------------------------------------------------------------------------*/ + if (Map.In_Radar(cell)) { + techno = Map[cell].Cell_Techno(); + if (!techno || (techno->What_Am_I()==RTTI_INFANTRY && + obj->What_Am_I()==RTTI_INFANTRY)) { + if (obj->Unlimbo(Cell_Coord(cell),DIR_N)) { + return(true); + } + } + } + + /*------------------------------------------------------------------------ + Loop through distances from the given center cell; skip the center cell. + For each distance, try placing the object along each rotational direction; + if none are available, try each direction with a random scatter value. + If that fails, go to the next distance. + This ensures that the closest coordinates are filled first. + ------------------------------------------------------------------------*/ + for (dist = 1; dist < 32; dist++) { + /*..................................................................... + Pick a random starting direction + .....................................................................*/ + rot = (FacingType)IRandom (FACING_N, FACING_NW); + /*..................................................................... + Try all directions twice + .....................................................................*/ + for (tryval = 0 ; tryval < 2; tryval++) { + /*.................................................................. + Loop through all directions, at this distance. + ..................................................................*/ + for (fcounter = FACING_N; fcounter <= FACING_NW; fcounter++) { + + skipit = false; + + /*............................................................... + Pick a coordinate along this directional axis + ...............................................................*/ + newcell = Clip_Move(cell, rot, dist); + + /*............................................................... + If this is our second try at this distance, add a random scatter + to the desired cell, so our units aren't all aligned along spokes. + ...............................................................*/ + if (tryval > 0) + newcell = Clip_Scatter (newcell, 1); + + /*............................................................... + If, by randomly scattering, we've chosen the exact center, skip + it & try another direction. + ...............................................................*/ + if (newcell==cell) + skipit = true; + + if (!skipit) { + /*............................................................ + Only attempt to Unlimbo the object if: + - there is no techno in the cell + - the techno in the cell & the object are both infantry + ............................................................*/ + techno = Map[newcell].Cell_Techno(); + if (!techno || (techno->What_Am_I()==RTTI_INFANTRY && + obj->What_Am_I()==RTTI_INFANTRY)) { + if (obj->Unlimbo(Cell_Coord(newcell),DIR_N)) { + return(true); + } + } + } + + rot++; + if (rot > FACING_NW) + rot = FACING_N; + } + } + } + + return(false); +} + + +/*********************************************************************************************** + * Sort_Cells -- sorts an array of cells by distance * + * * + * INPUT: * + * cells array to sort * + * numcells # entries in 'cells' * + * outcells array to store sorted values in * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/19/1995 BRR : Created. * + *=============================================================================================*/ +static void Sort_Cells(CELL *cells, int numcells, CELL *outcells) +{ + int i,j,k; + int num_sorted = 0; + int num_unsorted = numcells; + + /*------------------------------------------------------------------------ + Pick the first cell at random + ------------------------------------------------------------------------*/ + j = Random_Pick(0,numcells - 1); + outcells[0] = cells[j]; + num_sorted++; + + for (k = j; k < num_unsorted - 1; k++) { + cells[k] = cells[k + 1]; + } + num_unsorted--; + + /*------------------------------------------------------------------------ + After the first cell, assign the other cells based on who's furthest away + from the chosen ones. + ------------------------------------------------------------------------*/ + for (i = 0; i < numcells; i++) { + j = Furthest_Cell (outcells, num_sorted, cells, num_unsorted); + outcells[num_sorted] = cells[j]; + num_sorted++; + + for (k = j; k < num_unsorted - 1; k++) { + cells[k] = cells[k + 1]; + } + num_unsorted--; + } +} + + +/*********************************************************************************************** + * Furthest_Cell -- Finds cell furthest from a group of cells * + * * + * INPUT: * + * cells array of cells to find furthest-cell-away-from * + * numcells # entries in 'cells' * + * tcells array of cells to test; one of these will be selected as being * + * "furthest" from all the cells in 'cells' * + * numtcells # entries in 'tcells' * + * * + * OUTPUT: * + * index of 'tcell' that's furthest away from 'cells' * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/19/1995 BRR : Created. * + *=============================================================================================*/ +static int Furthest_Cell(CELL *cells, int numcells, CELL *tcells, int numtcells) +{ + int i; + int j; + int mindist; // minimum distance a 'tcell' is from a 'cell' + int maxmindist; // the highest mindist value of all tcells + int maxmin_idx; // index of the tcell with largest mindist + int dist; // working distance measure + + /*------------------------------------------------------------------------ + Initialize + ------------------------------------------------------------------------*/ + maxmindist = 0; + maxmin_idx = 0; + + /*------------------------------------------------------------------------ + Loop through all test cells, finding the furthest one from all entries in + the 'cells' array + ------------------------------------------------------------------------*/ + for (i = 0; i < numtcells; i++) { + + /*..................................................................... + Find the 'cell' closest to this 'tcell' + .....................................................................*/ + mindist = 0xffff; + for (j = 0; j < numcells; j++) { + dist = Distance (tcells[i],cells[j]); + if (dist <= mindist) { + mindist = dist; + } + } + + /*..................................................................... + If this tcell is further away than the others, save its distance & + index value + .....................................................................*/ + if (mindist >= maxmindist) { + maxmindist = mindist; + maxmin_idx = i; + } + } + + return (maxmin_idx); +} + + +/*********************************************************************************************** + * Clip_Scatter -- randomly scatters from given cell; won't fall off map * + * * + * INPUT: * + * cell cell to scatter from * + * maxdist max distance to scatter * + * * + * OUTPUT: * + * new cell number * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/30/1995 BRR : Created. * + *=============================================================================================*/ +static CELL Clip_Scatter(CELL cell, int maxdist) +{ + int x,y; + int xdist; + int ydist; + int xmin,xmax; + int ymin,ymax; + + /*------------------------------------------------------------------------ + Get X & Y coords of given starting cell + ------------------------------------------------------------------------*/ + x = Cell_X(cell); + y = Cell_Y(cell); + + /*------------------------------------------------------------------------ + Compute our x & y limits + ------------------------------------------------------------------------*/ + xmin = Map.MapCellX; + xmax = xmin + Map.MapCellWidth - 1; + ymin = Map.MapCellY; + ymax = ymin + Map.MapCellHeight - 1; + + /*------------------------------------------------------------------------ + Adjust the x-coordinate + ------------------------------------------------------------------------*/ + xdist = IRandom(0,maxdist); + if (IRandom(0,1)==0) { + x += xdist; + if (x > xmax) { + x = xmax; + } + } else { + x -= xdist; + if (x < xmin) { + x = xmin; + } + } + + /*------------------------------------------------------------------------ + Adjust the y-coordinate + ------------------------------------------------------------------------*/ + ydist = IRandom(0,maxdist); + if (IRandom(0,1)==0) { + y += ydist; + if (y > ymax) { + y = ymax; + } + } else { + y -= ydist; + if (y < ymin) { + y = ymin; + } + } + + return (XY_Cell(x,y)); +} + + +/*********************************************************************************************** + * Clip_Move -- moves in given direction from given cell; clips to map * + * * + * INPUT: * + * cell cell to start from * + * facing direction to move * + * dist distance to move * + * * + * OUTPUT: * + * new cell number * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/30/1995 BRR : Created. * + *=============================================================================================*/ +static CELL Clip_Move(CELL cell, FacingType facing, int dist) +{ + int x,y; + int xmin,xmax; + int ymin,ymax; + + /*------------------------------------------------------------------------ + Get X & Y coords of given starting cell + ------------------------------------------------------------------------*/ + x = Cell_X(cell); + y = Cell_Y(cell); + + /*------------------------------------------------------------------------ + Compute our x & y limits + ------------------------------------------------------------------------*/ + xmin = Map.MapCellX; + xmax = xmin + Map.MapCellWidth - 1; + ymin = Map.MapCellY; + ymax = ymin + Map.MapCellHeight - 1; + + /*------------------------------------------------------------------------ + Adjust the x-coordinate + ------------------------------------------------------------------------*/ + switch (facing) { + case FACING_N: + y -= dist; + break; + + case FACING_NE: + x += dist; + y -= dist; + break; + + case FACING_E: + x += dist; + break; + + case FACING_SE: + x += dist; + y += dist; + break; + + case FACING_S: + y += dist; + break; + + case FACING_SW: + x -= dist; + y += dist; + break; + + case FACING_W: + x -= dist; + break; + + case FACING_NW: + x -= dist; + y -= dist; + break; + } + + /*------------------------------------------------------------------------ + Clip to the map + ------------------------------------------------------------------------*/ + if (x > xmax) + x = xmax; + if (x < xmin) + x = xmin; + + if (y > ymax) + y = ymax; + if (y < ymin) + y = ymin; + + return (XY_Cell(x,y)); +} diff --git a/INIT.CPP b/INIT.CPP new file mode 100644 index 0000000..87ccc4a --- /dev/null +++ b/INIT.CPP @@ -0,0 +1,2958 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\init.cpv 2.18 16 Oct 1995 16:50:16 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INIT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : January 20, 1992 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Anim_Init -- Initialize the VQ animation control structure. * + * Init_Game -- Main game initialization routine. * + * Load_Recording_Values -- Loads recording values from recording file * + * Obfuscate -- Sufficiently transform parameter to thwart casual hackers. * + * Parse_Command_Line -- Parses the command line parameters. * + * Parse_INI_File -- Parses CONQUER.INI for special options * + * Play_Intro -- plays the introduction & logo movies * + * Save_Recording_Values -- Saves recording values to a recording file * + * Select_Game -- The game's main menu * + * Version_Number -- Determines the version number. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "loaddlg.h" +#include "tcpip.h" +#include +#include +#include "ccdde.h" + +static HANDLE hCCLibrary; + +/**************************************** +** Function prototypes for this module ** +*****************************************/ +static void Play_Intro(bool for_real = false); + +extern "C" { +extern long RandNumb; +} + +extern int SimRandIndex; + +#define ATTRACT_MODE_TIMEOUT 3600 // timeout for attract mode +#if(0) + +long FAR PASCAL _export Start_Game_Proc(HWND hwnd, UINT message, UINT wParam, LONG lParam) +{ + switch (message) { + case WM_CREATE: + break; + + case WM_COMMAND: + EndDialog(hwnd, TRUE); + AllDone = TRUE; + break; + + case WM_DESTROY: + EndDialog(hwnd, TRUE); + break; + } + return(DefWindowProc(hwnd, message, wParam, lParam)); +} +#endif + + +extern bool Server_Remote_Connect(void); +extern bool Client_Remote_Connect(void); +extern bool SpawnedFromWChat; + + +/*********************************************************************************************** + * Init_Game -- Main game initialization routine. * + * * + * Perform all one-time game initializations here. This includes all * + * allocations and table setups. The intro and other one-time startup * + * tasks are also performed here. * + * * + * INPUT: argc,argv -- Command line arguments. * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this ONCE! * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +bool Init_Game(int , char *[]) +{ + void const *temp_mouse_shapes; + + CCDebugString ("C&C95 - About to load reslib.dll\n"); + hCCLibrary = LoadLibrary("reslib.dll"); + + /* + ** Initialize the game object heaps. + */ + CCDebugString ("C&C95 - About to enter Units.Set_Heap\n"); + Units.Set_Heap(UNIT_MAX); + CCDebugString ("C&C95 - About to enter Factories.Set_Heap\n"); + Factories.Set_Heap(FACTORY_MAX); + CCDebugString ("C&C95 - About to enter Terrains.Set_Heap\n"); + Terrains.Set_Heap(TERRAIN_MAX); + CCDebugString ("C&C95 - About to enter Templates.Set_Heap\n"); + Templates.Set_Heap(TEMPLATE_MAX); + CCDebugString ("C&C95 - About to enter Smudges.Set_Heap\n"); + Smudges.Set_Heap(SMUDGE_MAX); + CCDebugString ("C&C95 - About to enter Overlays.Set_Heap\n"); + Overlays.Set_Heap(OVERLAY_MAX); + CCDebugString ("C&C95 - About to enter Infantry.Set_Heap\n"); + Infantry.Set_Heap(INFANTRY_MAX); + CCDebugString ("C&C95 - About to enter Bullets.Set_Heap\n"); + Bullets.Set_Heap(BULLET_MAX); + CCDebugString ("C&C95 - About to enter Buildings.Set_Heap\n"); + Buildings.Set_Heap(BUILDING_MAX); + CCDebugString ("C&C95 - About to enter Anims.Set_Heap\n"); + Anims.Set_Heap(ANIM_MAX); + CCDebugString ("C&C95 - About to enter Aircraft.Set_Heap\n"); + Aircraft.Set_Heap(AIRCRAFT_MAX); + CCDebugString ("C&C95 - About to enter Triggers.Set_Heap\n"); + Triggers.Set_Heap(TRIGGER_MAX); + CCDebugString ("C&C95 - About to enter TeamTypes.Set_Heap\n"); + TeamTypes.Set_Heap(TEAMTYPE_MAX); + CCDebugString ("C&C95 - About to enter Teams.Set_Heap\n"); + Teams.Set_Heap(TEAM_MAX); + CCDebugString ("C&C95 - About to enter Houses.Set_Heap\n"); + Houses.Set_Heap(HOUSE_MAX); + + /* + ** Initialize all the waypoints to invalid values. + */ + CCDebugString ("C&C95 - About to clear waypoints\n"); + memset(Waypoint, 0xFF, sizeof(Waypoint)); + + /* + ** Setup the keyboard processor in preparation for the game. + */ + CCDebugString ("C&C95 - About to do various keyboard inits\n"); +#ifdef FIX_ME_LATER + Keyboard_Attributes_Off(TRACKEXT | PAUSEON | BREAKON | SCROLLLOCKON | CTRLSON | CTRLCON | PASSBREAKS | FILTERONLY | TASKSWITCHABLE); +#endif //FIX_ME_LATER + Keyboard::Clear(); + Kbd.Clear(); + + /* + ** This is the shape staging buffer. It must always be available, so it is + ** allocated here and never freed. The library sets the globals ShapeBuffer + ** and ShapeBufferSize to these values, so it can be accessed for other + ** purposes. + */ + CCDebugString ("C&C95 - About to call Set_Shape_Buffer\n"); + Set_Shape_Buffer(new unsigned char[SHAPE_BUFFER_SIZE], SHAPE_BUFFER_SIZE); + + /* + ** Bootstrap enough of the system so that the error dialog box can sucessfully + ** be displayed. + */ + CCDebugString ("C&C95 - About to register CCLOCAL.MIX\n"); +#ifdef DEMO + new MixFileClass("DEMOL.MIX"); + MixFileClass::Cache("DEMOL.MIX"); +#else + int temp = RequiredCD; + RequiredCD = -2; + new MixFileClass("CCLOCAL.MIX"); // Cached. + MixFileClass::Cache("CCLOCAL.MIX"); + CCDebugString ("C&C95 - About to register UPDATE.MIX\n"); + new MixFileClass("UPDATE.MIX"); // Cached. + CCDebugString ("C&C95 - About to register UPDATEC.MIX\n"); + new MixFileClass("UPDATEC.MIX"); // Cached. + MixFileClass::Cache("UPDATEC.MIX"); +#ifdef JAPANESE + CCDebugString ("C&C95 - About to register LANGUAGE.MIX\n"); + new MixFileClass("LANGUAGE.MIX"); +#endif //JAPANESE + + RequiredCD = temp; + +#endif + CCDebugString ("C&C95 - About to load fonts\n"); + Green12FontPtr = Load_Alloc_Data(CCFileClass("12GREEN.FNT")); + Green12GradFontPtr = Load_Alloc_Data(CCFileClass("12GRNGRD.FNT")); + MapFontPtr = Load_Alloc_Data(CCFileClass("8FAT.FNT")); + Font8Ptr = MixFileClass::Retrieve(FONT8); + FontPtr = (char *)Font8Ptr; + Set_Font(FontPtr); + Font3Ptr = MixFileClass::Retrieve(FONT3); +// Font6Ptr = MixFileClass::Retrieve(FONT6); + Font6Ptr = Load_Alloc_Data(CCFileClass("6POINT.FNT")); + //ScoreFontPtr = MixFileClass::Retrieve("12GRNGRD.FNT"); //GRAD12FN"); //("SCOREFNT.FNT"); + ScoreFontPtr = Load_Alloc_Data(CCFileClass("12GRNGRD.FNT")); + FontLEDPtr = MixFileClass::Retrieve("LED.FNT"); + VCRFontPtr = MixFileClass::Retrieve("VCR.FNT"); +// GradFont6Ptr = MixFileClass::Retrieve("GRAD6FNT.FNT"); + GradFont6Ptr = Load_Alloc_Data(CCFileClass("GRAD6FNT.FNT")); + BlackPalette = new(MEM_CLEAR|MEM_REAL) unsigned char[768]; + GamePalette = new(MEM_CLEAR|MEM_REAL) unsigned char[768]; + OriginalPalette = new(MEM_CLEAR|MEM_REAL) unsigned char[768]; + WhitePalette = new(MEM_CLEAR|MEM_REAL) unsigned char[768]; + memset(WhitePalette, 63, 768); + + CCDebugString ("C&C95 - About to set palette\n"); + memset(BlackPalette, 0x01, 768); + if (!Special.IsFromInstall) Set_Palette(BlackPalette); + memset(BlackPalette, 0, 768); + if (!Special.IsFromInstall) { + Set_Palette(BlackPalette); + CCDebugString ("C&C95 - About to clear visible page\n"); + VisiblePage.Clear(); + } + + Set_Palette(GamePalette); + + CCDebugString ("C&C95 - About to set the mouse shape\n"); + /* + ** Since there is no mouse shape currently available we need' + ** to set one of our own. + */ + ShowCursor (FALSE); + if (MouseInstalled) { + temp_mouse_shapes = MixFileClass::Retrieve("MOUSE.SHP"); + if (temp_mouse_shapes) { + Set_Mouse_Cursor(0, 0, Extract_Shape(temp_mouse_shapes,0)); + while (Get_Mouse_State() > 1) { + Show_Mouse(); + } + } + } + + CCDebugString ("C&C95 - About to enter wait for focus loop\n"); + /* + ** Process the message loop until we are in focus. + */ + do { + CCDebugString ("C&C95 - About to call Keyboard::Check\n"); + Keyboard::Check(); + }while (!GameInFocus); + AllSurfaces.SurfacesRestored=FALSE; + + CCDebugString ("C&C95 - About to load the language file\n"); + /* + ** Fetch the language text from the hard drive first. If it cannot be + ** found on the hard drive, then look for it in the mixfile. + */ + if (RawFileClass(Language_Name("CONQUER")).Is_Available()) { + SystemStrings = (char const *)Load_Alloc_Data(RawFileClass(Language_Name("CONQUER"))); + } else { + SystemStrings = (char const *)MixFileClass::Retrieve(Language_Name("CONQUER")); + } + + /* + ** Default palette initialization. Uses the desert palette for convenience, + ** but only the non terrain specific colors matter. + */ + //Mem_Copy((void *)MixFileClass::Retrieve("TEMPERAT.PAL"), GamePalette, 768L); + CCFileClass palfile ("TEMPERAT.PAL"); + palfile.Read (GamePalette, 768L); + + if (!MouseInstalled) { + char buffer[255]; + Set_Palette(GamePalette); +#ifdef GERMAN + sprintf(buffer, "Command & Conquer kann Ihren Maustreiber nicht finden.."); +#else +#ifdef FRENCH + sprintf(buffer, "Command & Conquer ne peut pas d‚tecter votre gestionnaire de souris."); +#else + sprintf(buffer, "Command & Conquer is unable to detect your mouse driver."); +#endif +#endif + CCMessageBox().Process(buffer, TXT_OK); + Prog_End(); + exit(1); + } + +#ifdef DEMO + /* + ** Add in any override path specified in the conquer.ini file. + */ + if (strlen(OverridePath)) { + CCFileClass::Set_Search_Drives(OverridePath); + } +#endif + + CCDebugString ("C&C95 - About to search for CD drives\n"); + /* + ** Always try to look at the CD-ROM for data files. + */ + if (!CCFileClass::Is_There_Search_Drives()) { + + /* + ** If there are no search drives specified then we must be playing + ** off cd, so read files from there. + */ + int error; + + do { + if (!CDList.Get_Number_Of_Drives()){ + Set_Palette(GamePalette); + Show_Mouse(); + CCMessageBox().Process(TXT_CD_ERROR1, TXT_OK); + Prog_End(); + exit(EXIT_FAILURE); + } + CCFileClass::Set_CD_Drive( CDList.Get_First_CD_Drive() ); + + error = CCFileClass::Set_Search_Drives("?:\\"); + switch(error) { + case 1: + Set_Palette(GamePalette); + Show_Mouse(); + CCMessageBox().Process(TXT_CD_ERROR1, TXT_OK); + Prog_End(); + exit(EXIT_FAILURE); + + case 2: + Set_Palette(GamePalette); + Show_Mouse(); + if (CCMessageBox().Process(TXT_CD_DIALOG_1, TXT_OK, TXT_CANCEL) == 1) { + Prog_End(); + exit(EXIT_FAILURE); + } + Hide_Mouse(); + break; + + default: + Show_Mouse(); + if (!Force_CD_Available(RequiredCD)) { + Prog_End(); + exit(EXIT_FAILURE); + } + Hide_Mouse(); + break; + } + } while (error); + +#ifdef DEMO + RequiredCD = -2; +#else + RequiredCD = -1; +#endif + } else { + + /* + ** If there are search drives specified then all files are to be + ** considered local. + */ + RequiredCD = -2; + } +#ifndef DEMO + CCDebugString ("C&C95 - About to register addon mixfiles\n"); + /* + ** Before all else, cache any additional mixfiles. + */ + struct find_t ff; // for _dos_findfirst + if (!_dos_findfirst("SC*.MIX", _A_NORMAL, &ff)) { + char * ptr; + do { + ptr = strdup(ff.name); + new MixFileClass(ptr); + MixFileClass::Cache(ptr); +// free(ptr); + } while(!_dos_findnext(&ff)); + } + if (!_dos_findfirst("SS*.MIX", _A_NORMAL, &ff)) { + char * ptr; + do { + ptr = strdup(ff.name); + new MixFileClass(ptr); +// free(ptr); + } while(!_dos_findnext(&ff)); + } +#endif //DEMO + + CCDebugString ("C&C95 - About to register GENERAL.MIX\n"); + if (GeneralMix) delete GeneralMix; + GeneralMix = new MixFileClass("GENERAL.MIX"); + +// if (!_dos_findfirst("SC*.MIX", _A_NORMAL, &ff)) { +// do { +// new MixFileClass(ff.name); +// MixFileClass::Cache(ff.name); +// } while(!_dos_findnext(&ff)); +// } + + /* + ** Inform the file system of the various MIX files. + */ +#ifdef DEMO + new MixFileClass("DEMO.MIX"); + if (CCFileClass("DEMOM.MIX").Is_Available()) { + if (!MoviesMix) MoviesMix = new MixFileClass("DEMOM.MIX"); + ScoresPresent = true; + ThemeClass::Scan(); + } + +#else + CCDebugString ("C&C95 - About to register CONQUER.MIX\n"); + new MixFileClass("CONQUER.MIX"); // Cached. + CCDebugString ("C&C95 - About to register TRANSIT.MIX\n"); + new MixFileClass("TRANSIT.MIX"); + + CCDebugString ("C&C95 - About to register GENERAL.MIX\n"); + if (!GeneralMix) GeneralMix = new MixFileClass("GENERAL.MIX"); // Never cached. + +// if (CCFileClass("MOVIES.MIX").Is_Available()) { + CCDebugString ("C&C95 - About to register MOVIES.MIX\n"); + if (!MoviesMix) MoviesMix = new MixFileClass("MOVIES.MIX"); // Never cached. +// } + + + +#if (0) + + /* + ** Extract a movie from a mixfile. + */ + char *file_ptr = (char*)Alloc (32 * 1024 * 1024, MEM_NORMAL); + CCFileClass whatever ("PINTLE.VQA"); + + int len = whatever.Size(); + + whatever.Open(); + + DWORD actual; + HANDLE sfile = CreateFile("c:\\temp\\PINTLE.VQA", GENERIC_WRITE, 0, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (sfile != INVALID_HANDLE_VALUE){ + SetFilePointer (sfile, 0, NULL, FILE_END); + + do{ + whatever.Read (file_ptr, MIN (len, 1024*64)); + WriteFile(sfile, file_ptr, MIN (len, 1024*64), &actual, NULL); + len -= MIN (len, 1024*64); + }while ( len >0 ); + + CloseHandle (sfile); + } + + whatever.Close(); + + Free (file_ptr); + +#endif //(0) + + + + /* + ** Register the score mixfile. + */ + CCDebugString ("C&C95 - About to register SCORES.MIX\n"); + ScoresPresent = false; +// if (CCFileClass("SCORES.MIX").Is_Available()) { + ScoresPresent = true; + if (!ScoreMix) { + ScoreMix = new MixFileClass("SCORES.MIX"); + ThemeClass::Scan(); + } +// } +#endif + + /* + ** These are sound card specific, but the install program would have + ** copied the coorect versions to the hard drive. + */ + CCDebugString ("C&C95 - About to register SPEECH.MIX\n"); + if (CCFileClass("SPEECH.MIX").Is_Available()) { + new MixFileClass("SPEECH.MIX"); // Never cached. + } + CCDebugString ("C&C95 - About to register SOUNDS.MIX\n"); + new MixFileClass("SOUNDS.MIX"); // Cached. + + /* + ** Initialize the animation system. + */ + CCDebugString ("C&C95 - About to initialise the animation system\n"); + Anim_Init(); + + if (SpawnedFromWChat){ + Special.IsFromWChat = true; + } + + /* + ** Play the introduction movies. + */ + CCDebugString ("C&C95 - About to play the intro movie\n"); + if (!Special.IsFromInstall && !Special.IsFromWChat) Play_Intro(true); + + /* + ** Wait for a VSync; during the vertical blank, set the game palette & blit + ** the title screen. We must ensure no RGB values in the game palette match + ** those in the WWLIB's 'CurrentPalette', or the WWLIB palette-set routine + ** will skip that color; the VQ player will have changed that color (behind + ** WWLIB's back), so it will be incorrect. + */ + memset(CurrentPalette, 0x01, 768); + + if (!Special.IsFromInstall) { + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + } + + Hide_Mouse(); + Wait_Vert_Blank(); + if (!Special.IsFromInstall) { + Set_Palette(Palette); + HidPage.Blit(SeenBuff); + Show_Mouse(); + } + Call_Back(); +// Window_Dialog_Box(hCCLibrary, "DIALOG_1", MainWindow, MakeProcInstance((FARPROC)Start_Game_Proc, hInstance)); +// if (hCCLibrary) FreeLibrary(hCCLibrary); + +#ifdef DEMO + MixFileClass::Cache("DEMO.MIX"); + MixFileClass::Cache("SOUNDS.MIX"); +#else + /* + ** Cache the main game data. This operation can take a very long time. + */ + MixFileClass::Cache("CONQUER.MIX"); + if (SampleType != 0 && !Debug_Quiet) { + MixFileClass::Cache("SOUNDS.MIX"); + if (Special.IsJuvenile) { + new MixFileClass("ZOUNDS.MIX"); // Cached. + MixFileClass::Cache("ZOUNDS.MIX"); + } + } + Call_Back(); +#endif + +// malloc(2); + + /* + ** Perform any special debug-only processing. This includes preparing the + ** monochrome screen. + */ + Mono_Clear_Screen(); + +#ifdef ONHOLD + /* + ** Check for addition options not specified on the command-line. This must + ** be done before the One_Time calls, but after the shape buffer is set up. + */ + Parse_INI_File(); +#endif + + /* + ** Perform one-time game system initializations. + */ + Call_Back(); +// malloc(3); + Map.One_Time(); +// malloc(4); + Logic.One_Time(); +// malloc(5); + Options.One_Time(); + +// malloc(6); + + ObjectTypeClass::One_Time(); + BuildingTypeClass::One_Time(); + BulletTypeClass::One_Time(); + HouseTypeClass::One_Time(); + + TemplateTypeClass::One_Time(); + OverlayTypeClass::One_Time(); + SmudgeTypeClass::One_Time(); + TerrainTypeClass::One_Time(); + UnitTypeClass::One_Time(); + + InfantryTypeClass::One_Time(); + AnimTypeClass::One_Time(); + AircraftTypeClass::One_Time(); + HouseClass::One_Time(); + + /* + ** Speech holding tank buffer. Since speech does not mix, it can be placed + ** into a custom holding tank only as large as the largest speech file to + ** be played. + */ + SpeechBuffer = new char [SPEECH_BUFFER_SIZE]; + Call_Back(); + + /* + ** WWLIB bug: MouseState is in some undefined state; show the mouse until + ** it really shows. + */ + Map.Set_Default_Mouse(MOUSE_NORMAL, false); + Show_Mouse(); +//#ifdef FIX_ME_LATER + while (Get_Mouse_State() > 0) Show_Mouse(); +//#endif //FIX_ME_LATER + Call_Back(); + +#ifndef DEMO + /* + ** Load multiplayer scenario descriptions + */ + Read_Scenario_Descriptions(); +#endif + + /* + ** Initialize the multiplayer score values + */ + MPlayerGamesPlayed = 0; + MPlayerNumScores = 0; + MPlayerCurGame = 0; + for (int i = 0; i < MAX_MULTI_NAMES; i++) { + MPlayerScore[i].Name[0] = '\0'; + MPlayerScore[i].Wins = 0; + for (int j = 0; j < MAX_MULTI_GAMES; j++) { + MPlayerScore[i].Kills[j] = -1; // -1 = this player didn't play this round + } + } + + /* + ** Copy the title screen's palette into the GamePalette & OriginalPalette, + ** because the options Load routine uses these palettes to set the brightness, etc. + */ + memcpy (GamePalette, Palette, 768); + memcpy (OriginalPalette, Palette, 768); + + /* + ** Read game options, so the GameSpeed is initialized when multiplayer + ** dialogs are invoked. (GameSpeed must be synchronized between systems.) + */ + Options.Load_Settings(); + + return(true); +} + + +#ifndef NOMEMCHECK +void Uninit_Game(void) +{ + delete Map.ShadowPage; + Map.ShadowPage = NULL; + Map.Free_Cells(); + + delete [] SpeechBuffer; + + CCFileClass::Clear_Search_Drives(); + MixFileClass::Free_All(); + + Units.Set_Heap(0); + Factories.Set_Heap(0); + Terrains.Set_Heap(0); + Templates.Set_Heap(0); + Smudges.Set_Heap(0); + Overlays.Set_Heap(0); + Infantry.Set_Heap(0); + Bullets.Set_Heap(0); + Buildings.Set_Heap(0); + Anims.Set_Heap(0); + Aircraft.Set_Heap(0); + Triggers.Set_Heap(0); + TeamTypes.Set_Heap(0); + Teams.Set_Heap(0); + Houses.Set_Heap(0); + + delete [] _ShapeBuffer; + Set_Shape_Buffer(NULL, 0); + delete [] BlackPalette; + delete [] GamePalette; + delete [] OriginalPalette; + delete [] WhitePalette; + + WWDOS_Shutdown(); + delete [] Palette; +} +#endif + +extern bool Do_The_Internet_Menu_Thang(void); +extern int ShowCommand; + +/*********************************************************************************************** + * Select_Game -- The game's main menu * + * * + * INPUT: * + * fade if true, will fade the palette in gradually * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/05/1995 BRR : Created. * + *=============================================================================================*/ +extern int Com_Fake_Scenario_Dialog(void); +extern int Com_Show_Fake_Scenario_Dialog(void); +extern int WChatMaxAhead; +extern int WChatSendRate; +void Check_From_WChat(char *wchat_name); + +bool Select_Game(bool fade) +{ + enum { + SEL_TIMEOUT = -1, // main menu timeout--go into attract mode +#ifdef NEWMENU + SEL_NEW_SCENARIO, // Expansion scenario to play. +#endif + SEL_START_NEW_GAME, // start a new game +#ifdef BONUS_MISSIONS + SEL_BONUS_MISSIONS, +#endif //BONUS_MISSIONS + SEL_INTERNET, + SEL_LOAD_MISSION, // load a saved game + SEL_MULTIPLAYER_GAME, // play modem/null-modem/network game + SEL_INTRO, // replay the intro + SEL_EXIT, // exit to DOS + SEL_FAME, // view the hall of fame + SEL_NONE, // placeholder default value + }; + bool gameloaded=false; // Has the game been loaded from the menu? + int selection; // the default selection + bool process = true; // false = break out of while loop + bool display = true; + CountDownTimerClass count; + int cd_index; + + MEMORYSTATUS mem_info; + mem_info.dwLength=sizeof(mem_info); + GlobalMemoryStatus(&mem_info); + + /* + ** Enable the DDE Server so we can get internet start game packets from WChat + */ + DDEServer.Enable(); + + if (Special.IsFromInstall) { + /* + ** Special case for machines with 12 megs or less - just play intro, no choose side screen + */ + if (mem_info.dwTotalPhys < 12*1024*1024){ + VisiblePage.Clear(); + Play_Movie("INTRO2", THEME_NONE, false); + BreakoutAllowed = true; + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + fade = true; + VisiblePage.Clear(); + }else{ + display = false; + Show_Mouse(); + } + } + + /* + ** [Re]set any globals that need it, in preparation for a new scenario + */ + GameActive = true; + DoList.Init(); + OutList.Init(); + Frame = 0; + PlayerWins = false; + PlayerLoses = false; + MPlayerObiWan = false; + Debug_Unshroud = false; + Map.Set_Cursor_Shape(0); + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + + /* + ** Initialize multiplayer-protocol-specific variables: + ** If CommProtocol MULTI_E_COMP is used, you must: + ** Init FrameSendRate to a sensible value (3 is good) + ** Init MPlayerMaxAhead to an even multiple of FrameSendRate, and it must + ** be at least 2 * MPlayerMaxAhead + */ + CommProtocol = COMM_PROTOCOL_SINGLE_NO_COMP; + if (!Special.IsFromWChat){ + FrameSendRate = 3; + } + + ProcessTicks = 0; + ProcessFrames = 0; + DesiredFrameRate = 30; +//#if(TIMING_FIX) + NewMaxAheadFrame1 = 0; + NewMaxAheadFrame2 = 0; +//#endif + + /* + ** Init multiplayer game scores. Let Wins accumulate; just init the current + ** Kills for this game. Kills of -1 means this player didn't play this round. + */ + for (int i = 0 ; i < MAX_MULTI_GAMES; i++) { + MPlayerScore[i].Kills[MPlayerCurGame] = -1; + } + + /* + ** Set default mouse shape + */ + Map.Set_Default_Mouse(MOUSE_NORMAL, false); + + /* + ** If the last game we played was a multiplayer game, jump right to that + ** menu by pre-setting 'selection'. + */ + if (GameToPlay == GAME_NORMAL) { + selection = SEL_NONE; + } else { + selection = SEL_MULTIPLAYER_GAME; + } + + /* + ** Main menu processing; only do this if we're not in editor mode. + */ + if (!Debug_Map) { + + /* + ** Menu selection processing loop + */ + ScenarioInit++; + Theme.Queue_Song(THEME_MAP1); + ScenarioInit--; + + /* + ** If we're playing back a recording, load all pertinant values & skip + ** the menu loop. Hide the now-useless mouse pointer. + */ + if (PlaybackGame && RecordFile.Is_Available()) { + if (RecordFile.Open(READ)) { + Load_Recording_Values(); + process = false; + Theme.Fade_Out(); + } else + PlaybackGame = false; + } + + + /* + ** Handle case where we were spawned from Wchat + */ + if (SpawnedFromWChat){ + Special.IsFromInstall = false; //Dont play intro if we were spawned from wchat + selection = SEL_INTERNET; + Theme.Queue_Song(THEME_NONE); + GameToPlay = GAME_INTERNET; + display = false; + Set_Logic_Page(SeenBuff); + } + + + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=TRUE; + } + + /* + ** Redraw the title page if needed + */ + if (display) { + Hide_Mouse(); + + /* + ** Display the title page; fade it in if this is the first time + ** through the loop, and the 'fade' flag is true + */ + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + memcpy (GamePalette, Palette, 768); + HidPage.Blit(SeenBuff); + + if (fade) { + Fade_Palette_To(Palette, FADE_PALETTE_SLOW, Call_Back); + fade = false; + } + + Set_Logic_Page(SeenBuff); +#ifdef VIRGIN_CHEAT_KEYS + Fancy_Text_Print("V.%d%s", SeenBuff.Get_Width() - 1, SeenBuff.Get_Height() - 10, DKGREY, TBLACK, TPF_6POINT|TPF_FULLSHADOW|TPF_RIGHT, Version_Number(), VersionText, FOREIGN_VERSION_NUMBER); +// Fancy_Text_Print("V.%d%s%02d", 319, 190, DKGREY, TBLACK, TPF_6POINT|TPF_FULLSHADOW|TPF_RIGHT, Version_Number(), VersionText, FOREIGN_VERSION_NUMBER); +#else + +#ifdef DEMO + Version_Number(); + Fancy_Text_Print("DEMO V%s", SeenBuff.Get_Width() - 1, SeenBuff.Get_Height() - 10, DKGREY, TBLACK, TPF_6POINT|TPF_FULLSHADOW|TPF_RIGHT, VersionText); +#else + Fancy_Text_Print("V.%d%s", SeenBuff.Get_Width() - 1, SeenBuff.Get_Height() - 10, DKGREY, TBLACK, TPF_6POINT|TPF_FULLSHADOW|TPF_RIGHT, Version_Number(), VersionText); +#endif +#endif + display = false; + Show_Mouse(); + } + + /* + ** Display menu and fetch selection from player. + */ + if (Special.IsFromInstall && mem_info.dwTotalPhys >= 12*1024*1024){ + selection = SEL_START_NEW_GAME; + Theme.Queue_Song(THEME_NONE); + } + + /* + ** Handle case where we were spawned from Wchat + */ + if (Special.IsFromWChat && DDEServer.Get_MPlayer_Game_Info()){ + Check_From_WChat(NULL); + selection = SEL_MULTIPLAYER_GAME; + Theme.Queue_Song(THEME_NONE); + GameToPlay = GAME_INTERNET; + }else{ + /* + ** We werent spawned but we could still receive a DDE packet from wchat + */ + if (DDEServer.Get_MPlayer_Game_Info()){ + Check_From_WChat(NULL); + /* + ** Make sure top and bottom of screen are clear in 640x480 mode + */ + if (ScreenHeight == 480){ + VisiblePage.Fill_Rect (0, 0, 639, 40, 0); + VisiblePage.Fill_Rect (0, 440, 639, 479, 0); + } + } + } + + if (selection == SEL_NONE) { +// selection = Main_Menu(0); + selection = Main_Menu(ATTRACT_MODE_TIMEOUT); + } + Call_Back(); + + switch (selection) { + +#ifdef NEWMENU + + case SEL_INTERNET: + /* + ** Only call up the internet menu code if we dont already have connect info from WChat + */ + if (!DDEServer.Get_MPlayer_Game_Info()){ + CCDebugString ("C&C95 - About to call Internet Menu.\n"); + if (Do_The_Internet_Menu_Thang() && DDEServer.Get_MPlayer_Game_Info()){ + CCDebugString ("C&C95 - About to call Check_From_WChat.\n"); + Check_From_WChat(NULL); + selection = SEL_MULTIPLAYER_GAME; + display = false; + GameToPlay = GAME_INTERNET; + }else{ + selection = SEL_NONE; + display = true; + } + }else{ + CCDebugString ("C&C95 - About to call Check_From_WChat.\n"); + Check_From_WChat(NULL); + display = false; + GameToPlay = GAME_INTERNET; + selection = SEL_MULTIPLAYER_GAME; + } + break; + + + /* + ** Pick an expansion scenario. + */ + case SEL_NEW_SCENARIO: + CarryOverMoney = 0; + if (Expansion_Dialog()) { + Theme.Fade_Out(); +// Theme.Queue_Song(THEME_AOI); + GameToPlay = GAME_NORMAL; + process = false; + } else { + display = true; + selection = SEL_NONE; + } + break; + + +#ifdef BONUS_MISSIONS + + /* + ** User selected to play a bonus scenario. + */ + case SEL_BONUS_MISSIONS: + CarryOverMoney = 0; + + /* + ** Ensure that CD1 or CD2 is in the drive. These missions + ** are not on the covert CD. + */ + cd_index = Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60); + /* + ** If cd_index == 2 then its a covert CD + */ + if (cd_index == 2){ + RequiredCD = 0; + if (!Force_CD_Available (RequiredCD)){ + Prog_End(); + exit(EXIT_FAILURE); + } + } + + if (Bonus_Dialog()) { + Theme.Fade_Out(); + GameToPlay = GAME_NORMAL; + process = false; + } else { + display = true; + selection = SEL_NONE; + } + break; + + +#endif //BONUS_MISSIONS + +#endif + + /* + ** SEL_START_NEW_GAME: Play the game + */ + case SEL_START_NEW_GAME: + CarryOverMoney = 0; + +#ifdef DEMO + Hide_Mouse(); + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + Load_Title_Screen("PREPICK.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Fade_Palette_To(Palette, FADE_PALETTE_MEDIUM, Call_Back); + Clear_KeyBuffer(); + while (!Check_Key_Num()) { + Call_Back(); + } + Get_Key_Num(); + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + Show_Mouse(); + + Scenario = 1; + BuildLevel = 1; +#else + Scenario = 1; + BuildLevel = 1; +#endif + ScenPlayer = SCEN_PLAYER_GDI; + ScenDir = SCEN_DIR_EAST; + Whom = HOUSE_GOOD; + +#ifndef DEMO + Theme.Fade_Out(); + Choose_Side(); +#endif + + /* + ** If user is playing special mode, do NOT change Whom; leave it set to + ** GDI or NOD. Ini.cpp will set the player's ActLike to mirror the + ** Whom value. + */ + if (Special.IsJurassic && AreThingiesEnabled) { + ScenPlayer = SCEN_PLAYER_JP; + ScenDir = SCEN_DIR_EAST; + } + + GameToPlay = GAME_NORMAL; + process = false; + break; + + /* + ** Load a saved game. + */ + case SEL_LOAD_MISSION: + if (LoadOptionsClass(LoadOptionsClass::LOAD).Process()) { + //Theme.Fade_Out(); + Theme.Queue_Song(THEME_AOI); + process = false; + gameloaded = true; + } else { + display = true; + selection = SEL_NONE; + } + break; + + /* + ** SEL_MULTIPLAYER_GAME: set 'GameToPlay' to NULL-modem, modem, or + ** network play. + */ + case SEL_MULTIPLAYER_GAME: + +#ifdef DEMO + Hide_Mouse(); + Set_Palette(BlackPalette); + Load_Title_Screen("DEMOPIC.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Fade_Palette_To(Palette, FADE_PALETTE_MEDIUM, Call_Back); + Clear_KeyBuffer(); + while (!Check_Key()) { + Call_Back(); + } + Get_Key(); + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + Show_Mouse(); + display = true; + fade = true; + selection = SEL_NONE; +#else + switch (GameToPlay) { + + /* + ** If 'GameToPlay' isn't already set up for a multiplayer game, + ** we must prompt the user for which type of multiplayer game + ** they want. + */ + case GAME_NORMAL: + GameToPlay = Select_MPlayer_Game(); + if (GameToPlay == GAME_NORMAL) { // 'Cancel' + display = true; + selection = SEL_NONE; + } + break; + + case GAME_NULL_MODEM: + case GAME_MODEM: + if ( NullModem.Num_Connections() ) { + NullModem.Init_Send_Queue(); + + if ((GameToPlay == GAME_NULL_MODEM && + ModemGameToPlay == MODEM_NULL_HOST) || + (GameToPlay == GAME_MODEM && + ModemGameToPlay == MODEM_DIALER) ) { + + if ( !Com_Scenario_Dialog() ) { + GameToPlay = Select_Serial_Dialog(); + if (GameToPlay == GAME_NORMAL) { // user hit Cancel + display = true; + selection = SEL_NONE; + } + } + } else { + if ( !Com_Show_Scenario_Dialog() ) { + GameToPlay = Select_Serial_Dialog(); + if (GameToPlay == GAME_NORMAL) { // user hit Cancel + display = true; + selection = SEL_NONE; + } + } + } + } else { + GameToPlay = Select_MPlayer_Game(); + if (GameToPlay == GAME_NORMAL) { // 'Cancel' + display = true; + selection = SEL_NONE; + } + } + break; + + + +#ifdef FORCE_WINSOCK + /* + ** Handle being spawned from WChat. Intermnet play based on IPX code now. + */ + case GAME_INTERNET: + CCDebugString ("C&C95 - case GAME_INTERNET:\n"); + if (Special.IsFromWChat){ + //MessageBox (NULL, "About to restore focus to C&C95", "C&C95", MB_OK); + CCDebugString ("C&C95 - About to give myself focus.\n"); + SetForegroundWindow ( MainWindow ); + ShowWindow ( MainWindow, ShowCommand ); + + CCDebugString ("C&C95 - About to initialise Winsock.\n"); + if (Winsock.Init()){ + CCDebugString ("C&C95 - About to read multiplayer settings.\n"); + Read_MultiPlayer_Settings (); + Server = PlanetWestwoodIsHost; + + CCDebugString ("C&C95 - About to set addresses.\n"); + Winsock.Set_Host_Address(PlanetWestwoodIPAddress); + + CCDebugString ("C&C95 - About to call Start_Server or Start_Client.\n"); + if (Server){ + ModemGameToPlay = INTERNET_HOST; + Winsock.Start_Server(); + }else{ + ModemGameToPlay = INTERNET_JOIN; + Winsock.Start_Client(); + } + + +//#if (0) + /* + ** Flush out any pending packets from a previous game. + */ + CCDebugString ("C&C95 - About to flush packet queue.\n"); + CCDebugString ("C&C95 - Allocating scrap memory.\n"); + char *temp_buffer = new char[1024]; + + CCDebugString ("C&C95 - Creating timer class instance.\n"); + CountDownTimerClass ptimer; + + CCDebugString ("C&C95 - Entering read loop.\n"); + while (Winsock.Read(temp_buffer, 1024)){ + + CCDebugString ("C&C95 - Discarding a packet.\n"); + ptimer.Set (30, true); + while (ptimer.Time()){}; + CCDebugString ("C&C95 - Ready to check for more packets.\n"); + + } + CCDebugString ("C&C95 - About to delete scrap memory.\n"); + delete temp_buffer; +//#endif //(0) + + + + }else{ + CCDebugString ("C&C95 - Winsock failed to initialise.\n"); + GameToPlay = GAME_NORMAL; + selection = SEL_EXIT; + Special.IsFromWChat = false; + break; + } + + CCDebugString ("C&C95 - About to call Init_Network.\n"); + Init_Network(); + + if (DDEServer.Get_MPlayer_Game_Info()){ + CCDebugString ("C&C95 - About to call Read_Game_Options.\n"); + Read_Game_Options( NULL ); + }else{ + Read_Game_Options( "C&CSPAWN.INI" ); + } + + if (Server){ + + CCDebugString ("C&C95 - About to call Server_Remote_Connect.\n"); + if (Server_Remote_Connect()){ + CCDebugString ("C&C95 - Server_Remote_Connect returned success.\n"); + break; + }else{ + /* + ** We failed to connect to the other player + * + * SEND FAILURE PACKET TO WCHAT HERE !!!!! + * + */ + Winsock.Close(); + GameToPlay = GAME_NORMAL; + selection = SEL_NONE; + DDEServer.Delete_MPlayer_Game_Info(); // Make sure we dont go round in an infinite loop + //Special.IsFromWChat = false; + break; + } + }else{ + CCDebugString ("C&C95 - About to call Client_Remote_Connect.\n"); + if (Client_Remote_Connect()){ + CCDebugString ("C&C95 - Client_Remote_Connect returned success.\n"); + break; + }else{ + /* + ** We failed to connect to the other player + * + * SEND FAILURE PACKET TO WCHAT HERE !!!!! + * + */ + Winsock.Close(); + GameToPlay = GAME_NORMAL; + selection = SEL_NONE; + DDEServer.Delete_MPlayer_Game_Info(); // Make sure we dont go round in an infinite loop + //Special.IsFromWChat = false; + break; + } + } + + }else{ + GameToPlay = Select_MPlayer_Game(); + if (GameToPlay == GAME_NORMAL) { // 'Cancel' + display = true; + selection = SEL_NONE; + } + } + break; + +#endif //FORCE_WINSOCK + + } + + + switch (GameToPlay) { + /* + ** Internet, Modem or Null-Modem + */ + case GAME_MODEM: + case GAME_NULL_MODEM: + case GAME_INTERNET: + Theme.Fade_Out(); + ScenPlayer = SCEN_PLAYER_2PLAYER; + ScenDir = SCEN_DIR_EAST; + process = false; + Options.ScoreVolume = 0; + break; + + + /* + ** Network (IPX): start a new network game. + */ + case GAME_IPX: + /* + ** Init network system & remote-connect + */ + if (Init_Network() && Remote_Connect()) { +#if (0) + char seed[80]; + sprintf (seed, "Seed: %08x\n", Seed); + CCDebugString (seed); + + sprintf (seed, "rand: %04x\n", rand()); + CCDebugString (seed); + + sprintf (seed, "rand: %04x\n", rand()); + CCDebugString (seed); + + sprintf (seed, "rand: %04x\n", rand()); + CCDebugString (seed); +#endif + Options.ScoreVolume = 0; + ScenPlayer = SCEN_PLAYER_MPLAYER; + ScenDir = SCEN_DIR_EAST; + process = false; + Theme.Fade_Out(); + } else { // user hit cancel, or init failed + GameToPlay = GAME_NORMAL; + display = true; + selection = SEL_NONE; + } + break; + } +#endif + break; + + /* + ** Play a VQ + */ + case SEL_INTRO: + Theme.Fade_Out(); + Theme.Stop(); + Call_Back(); + + Force_CD_Available(-1); + Play_Intro(false); + Hide_Mouse(); + +// verify existance of movie file before playing this sequence. + if (CCFileClass("TRAILER.VQA").Is_Available()) { + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + VisiblePage.Clear(); + if (CCFileClass("ATTRACT2.CPS").Is_Available()){ + Load_Uncompress(CCFileClass("ATTRACT2.CPS"), SysMemPage, SysMemPage, Palette); + SysMemPage.Scale(SeenBuff, 0, 0, 0, 0, 320, 199, 640, 398); + Fade_Palette_To(Palette, FADE_PALETTE_MEDIUM, Call_Back); + } + Clear_KeyBuffer(); + count.Set(TIMER_SECOND*3); + while (count.Time()) { + Call_Back(); + } + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + + Play_Movie("TRAILER"); // Red Alert teaser. + } + + if (CCFileClass("SIZZLE.VQA").Is_Available()) { + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + VisiblePage.Clear(); + if (CCFileClass("ATTRACT2.CPS").Is_Available()){ + Load_Uncompress(CCFileClass("ATTRACT2.CPS"), SysMemPage, SysMemPage, Palette); + SysMemPage.Scale(SeenBuff, 0, 0, 0, 0, 320, 199, 640, 398); + Fade_Palette_To(Palette, FADE_PALETTE_MEDIUM, Call_Back); + } + Clear_KeyBuffer(); + count.Set(TIMER_SECOND*3); + while (count.Time()) { + Call_Back(); + } + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + + Play_Movie("SIZZLE"); // Red Alert teaser. + } + + if (CCFileClass("SIZZLE2.VQA").Is_Available()) { + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + VisiblePage.Clear(); + if (CCFileClass("ATTRACT2.CPS").Is_Available()){ + Load_Uncompress(CCFileClass("ATTRACT2.CPS"), SysMemPage, SysMemPage, Palette); + SysMemPage.Scale(SeenBuff, 0, 0, 0, 0, 320, 199, 640, 398); + Fade_Palette_To(Palette, FADE_PALETTE_MEDIUM, Call_Back); + } + Clear_KeyBuffer(); + count.Set(TIMER_SECOND*3); + while (count.Time()) { + Call_Back(); + } + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + + Play_Movie("SIZZLE2"); // Red Alert teaser. + } + + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + VisiblePage.Clear(); + if (CCFileClass("ATTRACT2.CPS").Is_Available()){ + Load_Uncompress(CCFileClass("ATTRACT2.CPS"), SysMemPage, SysMemPage, Palette); + SysMemPage.Scale(SeenBuff, 0, 0, 0, 0, 320, 199, 640, 398); + Fade_Palette_To(Palette, FADE_PALETTE_MEDIUM, Call_Back); + } + Clear_KeyBuffer(); + count.Set(TIMER_SECOND*3); + while (count.Time()) { + Call_Back(); + } + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + + Play_Movie("CC2TEASE"); + Show_Mouse(); + + ScenarioInit++; + Theme.Play_Song (THEME_MAP1); + ScenarioInit--; + display = true; + fade = true; + selection = SEL_NONE; + break; + + /* + ** Exit to DOS. + */ + case SEL_EXIT: +#ifdef JAPANESE + Hide_Mouse(); +#endif + Theme.Fade_Out(); + Fade_Palette_To(BlackPalette, FADE_PALETTE_SLOW, NULL); +#ifdef JAPANESE + VisiblePage.Clear(); +#endif + return(false); + + /* + ** Display the hall of fame. + */ + case SEL_FAME: + break; + + case SEL_TIMEOUT: + if (AllowAttract && RecordFile.Is_Available()) { + PlaybackGame = true; + if (RecordFile.Open(READ)) { + Load_Recording_Values(); + process = false; + Theme.Fade_Out(); + } else { + PlaybackGame = false; + selection = SEL_NONE; + } + } else { + selection = SEL_NONE; + } + break; + + default: + break; + } + } + } else { + + /* + ** For Debug_Map (editor) mode, if JP option is on, set to load that scenario + */ + Scenario = 1; + if (Special.IsJurassic && AreThingiesEnabled) { + ScenPlayer = SCEN_PLAYER_JP; + ScenDir = SCEN_DIR_EAST; + } + } + CCDebugString ("C&C95 - About to start game initialisation.\n"); +#ifdef FORCE_WINSOCK + if (GameToPlay == GAME_INTERNET){ + CommProtocol = COMM_PROTOCOL_MULTI_E_COMP; + if (!Special.IsFromWChat){ + FrameSendRate = 5; //3; + } + } +#endif //FORCE_WINSOCK + /* + ** Don't carry stray keystrokes into game. + */ + Kbd.Clear(); + + /* + ** Get a pointer to the compiler's random number seed. + ** the Get_EAX() must follow immediately after the srand(0) in order to save + ** the address of the random seed. (Currently not used.) + */ + srand(0); + RandSeedPtr = (long *)Get_EAX(); + + /* + ** Initialize the random number Seed. For multiplayer, this will have been done + ** in the connection dialogs. For single-player games, AND if we're not playing + ** back a recording, init the Seed to a random value. + */ + if (GameToPlay == GAME_NORMAL && !PlaybackGame) { + randomize(); + Seed = rand(); + } + + /* + ** If user has specified a desired random number seed, use it for multiplayer games + */ + if (CustomSeed != 0) { + Seed = CustomSeed; + } + + /* + ** Save initialization values if we're recording this game. + ** This must be done after 'Seed' has been initialized. + */ + if (RecordGame) { + if (RecordFile.Open(WRITE)) { + Save_Recording_Values(); + } else { + RecordGame = false; + } + } + + /* + ** Initialize the random-number generator. + */ + //Seed = 1; + + srand(Seed); + RandNumb = Seed; + SimRandIndex = 0; +#if (0) + DWORD actual; + HANDLE sfile = CreateFile("random.txt", GENERIC_WRITE, 0, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (sfile != INVALID_HANDLE_VALUE){ + SetFilePointer (sfile, 0, NULL, FILE_END); + + int minimum; + int maximum; + char whatever[80]; + for ( int i=0 ; i<1000 ; i++){ + minimum = rand(); + maximum = rand(); + + sprintf (whatever, "%04x\n", Random_Pick(minimum, maximum)); + WriteFile(sfile, whatever, strlen(whatever), &actual, NULL); + } + CloseHandle (sfile); + } +#endif + + + + /* + ** Load the scenario. Specify variation 'A' for the editor; for the game, + ** don't specify a variation, to make 'Set_Scenario_Name()' pick a random one. + ** Skip this if we've already loaded a save-game. + */ + if (!gameloaded) { + if (Debug_Map) { + Set_Scenario_Name(ScenarioName, Scenario, ScenPlayer, ScenDir, SCEN_VAR_A); + } else { + Set_Scenario_Name(ScenarioName, Scenario, ScenPlayer, ScenDir); + } + + /* + ** Start_Scenario() changes the palette; so, fade out & clear the screen + ** before calling it. + */ + Hide_Mouse(); + + if (selection != SEL_START_NEW_GAME) { + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + HiddenPage.Clear(); + VisiblePage.Clear(); + } + Show_Mouse(); + + Special.IsFromInstall = 0; + CCDebugString ("C&C95 - Starting scenario.\n"); + if (!Start_Scenario(ScenarioName)) { + return(false); + } + CCDebugString ("C&C95 - Scenario started OK.\n"); + } + + /* + ** For multiplayer games, initialize the inter-player message system. + ** Do this after loading the scenario, so the map's upper-left corner is + ** properly set. + */ + CCDebugString ("C&C95 - Initialising message system.\n"); + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + Messages.Init(Map.TacPixelX, Map.TacPixelY, 6, MAX_MESSAGE_LENGTH, 6*factor+1); + + /* + ** Hide the SeenBuff; force the map to render one frame. The caller can + ** then fade the palette in. + ** (If we loaded a game, this step will fade out the title screen. If we + ** started a scenario, Start_Scenario() will have played a couple of VQ + ** movies, which will have cleared the screen to black already.) + */ + CCDebugString ("C&C95 - About to call Call_Back.\n"); + Call_Back(); + + /* + ** This is desperately sad isnt it? + */ + Hide_Mouse(); + Hide_Mouse(); + Hide_Mouse(); + Hide_Mouse(); + WWMouse->Erase_Mouse(&HidPage, TRUE); + + + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + HiddenPage.Clear(); + VisiblePage.Clear(); + Set_Logic_Page(SeenBuff); + Map.Flag_To_Redraw(); + Call_Back(); + Map.Render(); + //Show_Mouse(); + + /* + ** Special hack initialization of 'MPlayerMaxAhead' to accommodate the + ** compression protocol technology. + */ +#ifdef FORCE_WINSOCK + if (CommProtocol==COMM_PROTOCOL_MULTI_E_COMP && GameToPlay!=GAME_NORMAL) { + if (!Special.IsFromWChat){ + MPlayerMaxAhead = FrameSendRate * 3; //2; + }else{ + MPlayerMaxAhead = WChatMaxAhead; + FrameSendRate = WChatSendRate; + } + } +#endif //FORCE_WINSOCK + + if (Debug_Map) { + while (Get_Mouse_State() > 1) { + Show_Mouse(); + } + } + + return(true); +} + + +/*********************************************************************************************** + * Play_Intro -- plays the introduction & logo movies * + * * + * INPUT: * + * for_real if true, this function plays the "real" intro; otherwise, it plays * + * a delicious smorgasbord of visual delights, guaranteed to titillate * + * the ocular & auditory nerve pathways. * + * Well, it plays movies, anyway. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/06/1995 BRR : Created. * + *=============================================================================================*/ +static void Play_Intro(bool for_real) +{ + bool playright = !Key_Down(KN_LCTRL) || !Key_Down(KN_RCTRL); + static int _counter = -1; + static char * _names[] = { +#ifdef DEMO + "LOGO", + +#else + + "INTRO2", +//#ifdef CHEAT_KEYS + "GDIEND1", + "GDIEND2", + "GDIFINA", + "GDIFINB", + "AIRSTRK", + "AKIRA", + "BANNER", + "BCANYON", + "BKGROUND", + "BOMBAWAY", + "BOMBFLEE", + "BURDET1", + "BURDET2", + "CC2TEASE", + "CONSYARD", + "DESFLEES", + "DESKILL", + "DESOLAT", + "DESSWEEP", + "FLAG", + "FLYY", + "FORESTKL", + "GAMEOVER", + "GDI1", + "GDI10", + "GDI11", + "GDI12", + "GDI13", + "GDI14", + "GDI15", + "GDI2", + "GDI3", + "GDI3LOSE", + "GDI4A", + "GDI4B", + "GDI5", + "GDI6", + "GDI7", + "GDI8A", + "GDI8B", + "GDI9", + "GDILOSE", + "GUNBOAT", + "HELLVALY", + "INSITES", + "KANEPRE", + "LANDING", + "LOGO", + "NAPALM", + "NITEJUMP", + "NOD1", + "NOD10A", + "NOD10B", + "NOD11", + "NOD12", + "NOD13", + "NOD1PRE", + "NOD2", + "NOD3", + "NOD4A", + "NOD4B", + "NOD5", + "NOD6", + "NOD7A", + "NOD7B", + "NOD8", + "NOD9", + "NODEND1", + "NODEND2", + "NODEND3", + "NODEND4", + "NODFINAL", + "NODFLEES", + "NODLOSE", + "NODSWEEP", + "NUKE", + "OBEL", + "PARATROP", + "PINTLE", + "PLANECRA", + "PODIUM", + "REFINT", + "RETRO", + "SABOTAGE", + "SAMDIE", + "SAMSITE", + "SEIGE", + "SETHPRE", + "SPYCRASH", + "STEALTH", + "SUNDIAL", + "TANKGO", + "TANKKILL", + "TBRINFO1", + "TBRINFO2", + "TBRINFO3", + "TIBERFX", + "TRTKIL_D", + "TURTKILL", + "VISOR", +//#endif +#endif + NULL + }; + + Keyboard::Clear(); + if (for_real) { + Hide_Mouse(); + Play_Movie("LOGO", THEME_NONE, false); + Show_Mouse(); + } else { + if (!Debug_Flag) { + _counter = 0; + } else { + if (playright) _counter++; + if (_counter == -1) _counter = 0; + } + Hide_Mouse(); + Play_Movie(_names[_counter], THEME_NONE); + Show_Mouse(); + if (!_names[_counter]) { + _counter = -1; + } + } +} + + +/*********************************************************************************************** + * Anim_Init -- Initialize the VQ animation control structure. * + * * + * VQ animations are controlled by a structure passed to the VQ player. This routine * + * initializes the structure to values required by C&C. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only need to call this routine once at the beginning of the game. * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + *=============================================================================================*/ +extern LPDIRECTSOUND SoundObject; +extern LPDIRECTSOUNDBUFFER PrimaryBufferPtr; +void Anim_Init(void) +{ + /* Configure player with INI file */ + VQA_DefaultConfig(&AnimControl); +// void const * font = Load_Font(FONT8); +// AnimControl.EVAFont = (char *)font; +// AnimControl.CapFont = (char *)font; + + AnimControl.DrawFlags = VQACFGF_TOPLEFT; + AnimControl.DrawFlags |= VQACFGF_BUFFER; + + AnimControl.DrawFlags |= VQACFGF_NOSKIP; + + //AnimControl.X1 =0; + //AnimControl.Y1 =0; + AnimControl.FrameRate = -1; + AnimControl.DrawRate = -1; + + AnimControl.DrawerCallback = VQ_Call_Back; + AnimControl.ImageWidth = 320; + AnimControl.ImageHeight = 200; + AnimControl.Vmode = 0; + AnimControl.ImageBuf = (unsigned char *)SysMemPage.Get_Offset(); + //AnimControl.VBIBit = VertBlank; + //AnimControl.DrawFlags |= VQACFGF_TOPLEFT; + AnimControl.OptionFlags |= VQAOPTF_CAPTIONS|VQAOPTF_EVA; + + if (SlowPalette) { + AnimControl.OptionFlags |= VQAOPTF_SLOWPAL; + } + +// AnimControl.AudioBuf = (unsigned char *)HidPage.Get_Buffer(); +// AnimControl.AudioBufSize = 32768U; + //AnimControl.DigiCard = NewConfig.DigitCard; + //AnimControl.HMIBufSize = 8192; + //AnimControl.DigiHandle = Get_Digi_Handle(); + //AnimControl.Volume = 0x00FF; + //AnimControl.AudioRate = 22050; +// if (NewConfig.Speed) AnimControl.AudioRate = 11025; + AnimControl.SoundObject = SoundObject; //Get_Sound_Object(); + AnimControl.PrimaryBufferPtr = PrimaryBufferPtr; //Get_Primart_Buffer(); + //if (!Debug_Quiet && Get_Digi_Handle() != -1) { + //AnimControl.OptionFlags |= VQAOPTF_AUDIO; + //} + + if (MonoClass::Is_Enabled()) { + AnimControl.OptionFlags |= VQAOPTF_MONO; + } +} + + +/*********************************************************************************************** + * Parse_Command_Line -- Parses the command line parameters. * + * * + * This routine should be called before the graphic mode is initialized. It examines the * + * command line parameters and sets the appropriate globals. If there is an error, then * + * it outputs a command summary and then returns false. * + * * + * INPUT: argc -- The number of command line arguments. * + * * + * argv -- Pointer to character string array that holds the individual arguments. * + * * + * OUTPUT: bool; Was the command line parsed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/18/1995 JLB : Created. * + *=============================================================================================*/ +bool Parse_Command_Line(int argc, char *argv[]) +{ + /* + ** Parse the command line and set globals to reflect the parameters + ** passed in. + */ +#ifdef DEMO + Scenario = 3; +#else + Scenario = 1; +#endif + ScenPlayer = SCEN_PLAYER_GDI; + ScenDir = SCEN_DIR_EAST; + Whom = HOUSE_GOOD; + Special.Init(); + + Debug_Map = false; +// Debug_Play_Map = false; + Debug_Unshroud = false; + + for (int index = 1; index < argc; index++) { + char * string; // Pointer to argument. + long code = 0; + + string = strupr(argv[index]); + + /* + ** Print usage text only if requested. + */ + if (stricmp("/?", string) == 0 || stricmp("-?", string) == 0 || stricmp("-h", string) == 0 || stricmp("/h", string) == 0) { + /* + ** Unrecognized command line parameter... Display usage + ** and then exit. + */ + #ifdef GERMAN + puts("Command & Conquer (c) 1995,1996 Westwood Studios\r\n" + "Parameter:\r\n" +// " -CD = Suchpfad fr Daten-Dateien festlegen.\r\n" + " -DESTNET = Netzwerkkennung des Zielrechners festlegen\r\n" + " (Syntax: DESTNETxx.xx.xx.xx)\r\n" + " -SOCKET = Kennung des Netzwerk-Sockets (0 - 16383)\n" + " -STEALTH = Namen im Mehrspieler-Modus verstecken (\"Boss-Modus\")\r\n" + " -MESSAGES = Mitteilungen von auáerhalb des Spiels zulassen\r\n" + // " -ELITE = Fortgeschrittene KI und Gefechtstechniken.\r\n" + "\r\n"); + #else + #ifdef FRENCH + puts("Command & Conquer (c) 1995, Westwood Studios\r\n" + "ParamŠtres:\r\n" +// " -CD = Recherche des fichiers dans le\r\n" +// " r‚pertoire indiqu‚.\r\n" + " -DESTNET = Sp‚cifier le num‚ro de r‚seau du systŠme de destination\r\n" + " (Syntaxe: DESTNETxx.xx.xx.xx)\r\n" + " -SOCKET = ID poste r‚seau (0 … 16383)\r\n" + " -STEALTH = Cacher les noms en mode multijoueurs (\"Mode Boss\")\r\n" + " -MESSAGES = Autorise les messages ext‚rieurs … ce jeu.\r\n" + "\r\n"); + #else + puts("Command & Conquer (c) 1995, 1996 Westwood Studios\r\n" + "Parameters:\r\n" + #ifdef NEVER + " CHEAT = Enable debug keys.\r\n" + " -EDITOR = Enable scenario editor.\r\n" + #endif +// " -CD = Set search path for data files.\r\n" + " -DESTNET = Specify Network Number of destination system\r\n" + " (Syntax: DESTNETxx.xx.xx.xx)\r\n" + " -SOCKET = Network Socket ID (0 - 16383)\n" + " -STEALTH = Hide multiplayer names (\"Boss mode\")\r\n" + " -MESSAGES = Allow messages from outside this game.\r\n" + " -o = Enable compatability with version 1.07.\r\n" +#ifdef JAPANESE + " -ENGLISH = Enable English keyboard compatibility.\r\n" +#endif + // " -ELITE = Advanced AI and combat characteristics.\r\n" + #ifdef NEVER + " -O[options]= Special control options;\r\n" + " 1 : Tiberium grows.\r\n" + " 2 : Tiberium grows and spreads.\r\n" + " A : Aggressive player unit defense enabled.\r\n" + " B : Bargraphs always displayed.\r\n" + " C : Capture the flag mode.\r\n" + " E : Elite defense mode disable (attacker advantage).\r\n" + " D : Deploy reversal allowed for construction yard.\r\n" + " F : Fleeing from direct immediate threats is enabled.\r\n" + " H : Hussled recharge time.\r\n" + " G : Growth for Tiberium slowed in multiplay.\r\n" + " I : Inert weapons -- no damage occurs.\r\n" + " J : 7th grade sound effects.\r\n" + " M : Monochrome debug messages.\r\n" + " N : Name the civilians and buildings.\r\n" + " P : Path algorithm displayed as it works.\r\n" + " Q : Quiet mode (no sound).\r\n" + " R : Road pieces are not added to buildings.\r\n" + " T : Three point turns for wheeled vehicles.\r\n" + " U : U can target and burn trees.\r\n" + " V : Show target selection by opponent.\r\n" + " X : Make a recording of a multiplayer game.\r\n" + " Y : Play a recording of a multiplayer game.\r\n" + " Z : Disaster containment team.\r\n" + #endif + "\r\n"); + #endif + #endif + return(false); + } + + + bool processed = true; + switch (Obfuscate(string)) { + + /* + ** Signal that easy mode is active. + */ + case PARM_EASY: + Special.IsHealthBar = true; + Special.IsEasy = true; + Special.IsDifficult = false; + break; + + /* + ** Signal that hard mode is active. + */ + case PARM_HARD: + Special.IsHealthBar = false; + Special.IsEasy = false; + Special.IsDifficult = true; + break; + +#ifdef VIRGIN_CHEAT_KEYS + case PARM_PLAYTEST: + Debug_Playtest = true; + break; +#endif + +#ifdef PARM_CHEATERIK + case PARM_CHEATERIK: + Debug_Playtest = true; + Debug_Flag = true; + break; +#endif + +#ifdef PARM_CHEATADAM + case PARM_CHEATADAM: + Debug_Playtest = true; + Debug_Flag = true; + break; +#endif + +#ifdef PARM_CHEATMIKE + case PARM_CHEATMIKE: + Debug_Playtest = true; + Debug_Flag = true; + break; +#endif + +#ifdef PARM_CHEATDAVID + case PARM_CHEATDAVID: + Debug_Playtest = true; + Debug_Flag = true; + break; +#endif + +#ifdef PARM_CHEATPHIL + case PARM_CHEATPHIL: + Debug_Playtest = true; + Debug_Flag = true; + break; +#endif + +#ifdef PARM_CHEATBILL + case PARM_CHEATBILL: + Debug_Playtest = true; + Debug_Flag = true; + break; +#endif + +#ifdef PARM_CHEAT_STEVET + + case PARM_CHEAT_STEVET: + Debug_Playtest = true; + Debug_Flag = true; + break; + +#endif + +#ifdef PARM_EDITORBILL + case PARM_EDITORBILL: + Debug_Map = true; + Debug_Unshroud = true; + Debug_Flag = true; + break; +#endif + +#ifdef PARM_EDITORERIK + case PARM_EDITORERIK: + Debug_Map = true; + Debug_Unshroud = true; + Debug_Flag = true; + break; +#endif + + case PARM_SPECIAL: + Special.IsJurassic = true; + AreThingiesEnabled = true; + break; + + /* + ** Special flag - is C&C being run from the install program? + */ + case PARM_INSTALL: +#ifndef DEMO + Special.IsFromInstall = true; +#endif + break; + + default: + processed = false; + break; + } + if (processed) continue; + + +#ifdef CHEAT_KEYS + /* + ** Scenario Editor Mode + */ + if (stricmp(string, "-CHECKMAP") == 0) { + Debug_Check_Map = true; + continue; + } + +#endif + + /* + ** Older version override. + */ + if (stricmp(string, "-O") == 0 || stricmp(string, "-0") == 0) { + IsV107 = true; + continue; + } + + /* + ** File search path override. + */ + if (strstr(string, "-CD")) { + CCFileClass::Set_Search_Drives(&string[3]); + continue; + } +#ifdef JAPANESE + /* + ** Enable english-compatible keyboard + */ + if (!stricmp(string,"-ENGLISH")) { + ForceEnglish = true; + continue; + } +#endif + + /* + ** Specify destination connection for network play + */ + if (strstr(string, "-DESTNET")) { + NetNumType net; + NetNodeType node; + + /* + ** Scan the command-line string, pulling off each address piece + */ + int i = 0; + char * p = strtok(string + 8,"."); + while (p) { + int x; + + sscanf(p,"%x",&x); // convert from hex string to int + if (i < 4) { + net[i] = (char)x; // fill NetNum + } else { + node[i-4] = (char)x; // fill NetNode + } + i++; + p = strtok(NULL,"."); + } + + /* + ** If all the address components were successfully read, fill in the + ** BridgeNet with a broadcast address to the network across the bridge. + */ + if (i >= 4) { + IsBridge = 1; + memset(node, 0xff, 6); + BridgeNet = IPXAddressClass(net, node); + } + continue; + } + + /* + ** Specify socket ID, as an offset from 0x4000. + */ + if (strstr(string, "-SOCKET")) { + unsigned short socket; + + socket = (unsigned short)(atoi(string + strlen("SOCKET"))); + socket += 0x4000; + if (socket >= 0x4000 && socket < 0x8000) { + Ipx.Set_Socket (socket); + } + continue; + } + + /* + ** Set the Net Stealth option + */ + if (strstr(string, "-STEALTH")) { + NetStealth = true; + continue; + } + + /* + ** Set the Net Protection option + */ + if (strstr(string, "-MESSAGES")) { + NetProtect = false; + continue; + } + + /* + ** Allow "attract" mode + */ + if (strstr(string, "-ATTRACT")) { + AllowAttract = true; + continue; + } + + /* + ** Set screen to 640x480 instead of 640x400 + */ + if (strstr(string,"-480")){ + ScreenHeight = 480; + continue; + } + + /* + ** Check for spawn from WChat + */ + if (strstr(string,"-WCHAT")){ + SpawnedFromWChat = true; + } + + /* + ** Allow use of MMX instructions + */ + if (strstr(string,"-MMX")){ + MMXAvailable = true; + continue; + } + + +#ifdef CHEAT_KEYS + /* + ** Allow solo net play + */ + if (stricmp(string, "-HANSOLO") == 0) { + MPlayerSolo = true; + continue; + } + + /* + ** Specify the random number seed (for debugging) + */ + if (strstr(string, "-SEED")) { + CustomSeed = (unsigned short)(atoi(string + strlen("SEED"))); + continue; + } +#endif + +#ifdef NEVER + /* + ** Handle the prog init differently in this case. + */ + if (strstr(string, "-V")) { + continue; + } +#endif + +#ifdef FIX_ME_LATER + /* + ** look for passed-in video mode to default to + */ + if (strnicmp(string, "-V", strlen("-V")) == 0) { + Set_Video_Mode(MCGA_MODE); // do this to get around first_time variable... + Set_Original_Video_Mode(atoi(string+2)); + continue; + } +#endif //FIX_ME_LATER + + /* + ** Special command line control parsing. + */ + if (strnicmp(string, "-X", strlen("-O")) == 0) { + string += strlen("-X"); + while (*string) { + char code = *string++; + switch (toupper(code)) { + +#ifdef ONHOLD + /* + ** Should human generated sound effects be used? + */ + case 'J': + Special.IsJuvenile = true; + break; +#endif + +#ifdef CHEAT_KEYS + /* + ** Monochrome debug screen enable. + */ + case 'M': + Special.IsMonoEnabled = true; + break; + + /* + ** Inert weapons -- no units take damage. + */ + case 'I': + Special.IsInert = true; + break; +#endif + +#ifdef CHEAT_KEYS + /* + ** Hussled recharge timer. + */ + case 'H': + Special.IsSpeedBuild = true; + break; + + /* + ** Turn on super-record mode, which thrashes your disk terribly, + ** but is really really cool. Well, sometimes it is, anyway. + ** At least, it can be. Once in a while. + ** This flag tells the recording system to re-open the file for + ** each write, so the recording survives a crash. + */ + case 'S': + SuperRecord = 1; + break; + + /* + ** "Record" a multi-player game + */ + case 'X': + RecordGame = 1; + break; + + /* + ** "Play Back" a multi-player game + */ + case 'Y': + PlaybackGame = 1; + break; +#endif + +#ifdef ONHOLD + /* + ** Bonus scenario enable. + */ + case 'Z': + Special.IsJurassic = true; + break; +#endif + + /* + ** Quiet mode override control. + */ + case 'Q': + Debug_Quiet = true; + break; + +#ifdef CHEAT_KEYS + /* + ** Target selection by human opponent (network/modem play) will + ** be visible to the player? + */ + case 'V': + Special.IsVisibleTarget = true; + break; +#endif + + default: +#ifdef GERMAN + puts("Ungltiger Parameter.\n"); +#else +#ifdef FRENCH + puts("Commande d'option invalide.\n"); +#else + puts("Invalid option switch.\n"); +#endif +#endif + return(false); + } + + } + + if (Special.IsMonoEnabled) { + MonoClass::Enable(); + } + continue; + } + } + return(true); +} + + +#ifdef ONHOLD +/*********************************************************************************************** + * Parse_INI_File -- Parses CONQUER.INI for certain options * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/18/1995 BRR : Created. * + *=============================================================================================*/ +void Parse_INI_File(void) +{ + char *buffer; // INI staging buffer pointer. + char buf[128]; + static char section[40]; + static char entry[40]; + static char name[40]; + int len; + int i; + + /* + ** These arrays store the coded version of the names Geologic, Period, & Jurassic. + ** Decode them by subtracting 83. For you curious types, the names look like: + ** š¸Â¿Âº¼¶ + ** £¸Å¼Â· + ** ÈÅ´ÆƼ¶ + ** If these INI entries aren't found, the IsJurassic flag does nothing. + */ + static char coded_section[] = {154,184,194,191,194,186,188,182,0}; + static char coded_entry[] = {163,184,197,188,194,183,0}; + static char coded_name[] = {157,200,197,180,198,198,188,182,0}; + + /*------------------------------------------------------------------------ + Fetch working pointer to the INI staging buffer. Make sure that the buffer + is cleared out before proceeding. + ------------------------------------------------------------------------*/ + buffer = (char *)_ShapeBuffer; + memset(buffer, '\0', _ShapeBufferSize); + + /*------------------------------------------------------------------------ + Decode the desired section, entry, & name + ------------------------------------------------------------------------*/ + strcpy (section,coded_section); + len = strlen(coded_section); + for (i = 0; i < len; i++) { + section[i] -= 83; + } + + strcpy (entry,coded_entry); + len = strlen(coded_entry); + for (i = 0; i < len; i++) { + entry[i] -= 83; + } + + strcpy (name,coded_name); + len = strlen(coded_name); + for (i = 0; i < len; i++) { + name[i] -= 83; + } + + /*------------------------------------------------------------------------ + Create filename and read the file. + ------------------------------------------------------------------------*/ + CCFileClass file ("CONQUER.INI"); + if (!file.Is_Available()) { + return; + } else { + file.Read(buffer, _ShapeBufferSize-1); + } + file.Close(); + + WWGetPrivateProfileString(section, entry, "", buf, sizeof(buf), buffer); + + if (!stricmp (buf,name)) + AreThingiesEnabled = true; + + memset (section, 0, sizeof(section)); + memset (entry, 0, sizeof(entry)); + memset (name, 0, sizeof(name)); +} +#endif + + +/*********************************************************************************************** + * Version_Number -- Determines the version number. * + * * + * This routine will determine the version number by analyzing the date and teim that the * + * program was compiled and then generating a unique version number based on it. The * + * version numbers are guaranteed to be larger for later dates. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the version number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/24/1995 JLB : Created. * + *=============================================================================================*/ +int Version_Number(void) +{ +#ifdef OBSOLETE + static bool initialized = false; + static int version; + static char * date = __DATE__; + static char * time = __TIME__; + static char const * months = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC"; + + if (!initialized) { + char * ptr; + char * tok; + + /* + ** Fetch the month and place in the first two digit positions. + */ + strupr(date); + tok = strtok(date, " "); + ptr = strstr(months, tok); + if (ptr) { + version = (((ptr - months) / 3)+1) * 10000; + } + + /* + ** Fetch the date and place that in the next two digit positions. + */ + tok = strtok(NULL, " "); + if (tok) { + version += atoi(tok) * 100; + } + + /* + ** Fetch the time and place that in the last two digit positions. + */ + tok = strtok(time, ": "); + if (tok) { + version += atoi(tok); + } + + + /* + ** Fetch the virgin text file (if present). + */ + RawFileClass file("VERSION.TXT"); + if (file.Is_Available()) { + file.Read(VersionText, sizeof(VersionText)); + VersionText[sizeof(VersionText)-1] = '\0'; + while (VersionText[sizeof(VersionText)-1] == '\r') { + VersionText[sizeof(VersionText)-1] = '\0'; + } + } else { + VersionText[0] = '\0'; + } + + initialized = true; + } + return(version); +#endif + +#ifdef WIN32 + +#if (FRENCH) + sprintf(VersionText, ".02"); // Win95 french version number +#endif //FRENCH + +#if (GERMAN) + sprintf(VersionText, ".01"); // Win95 german version number +#endif //GERMAN + +#if (JAPANESE) + sprintf(VersionText, ".01"); // Win95 german version number +#endif //GERMAN + +#if !(FRENCH | GERMAN | JAPANESE) + sprintf(VersionText, ".07"); // Win95 USA version number +#endif //FRENCH | GERMAN + + RawFileClass file("VERSION.TXT"); + char version [16]; + memset (version, 0, sizeof (version)); + if (file.Is_Available()){ + file.Read (version, sizeof (version)); + } + strncat (VersionText, version, sizeof (VersionText) - strlen (VersionText) - 1); + +#if (FRENCH) + return (1); // Win95 french version number +#endif //FRENCH + +#if (GERMAN) + return (1); // Win95 german version number +#endif //GERMAN + +#if (JAPANESE) + return (1); // Win95 german version number +#endif //GERMAN + +#if !(FRENCH | GERMAN | JAPANESE) + return (1); // Win95 USA version number +#endif //FRENCH | GERMAN + +#else + + +#ifdef PATCH + + #ifdef DEMO + sprintf(VersionText, " 1.0a"); // Demo version. + #else + strcpy(VersionText, ".34 "); + #endif + return(1); + +#else + + #ifdef DEMO + sprintf(VersionText, " 1.0a"); // Demo version. + #else + // sprintf(VersionText, ".%02dp", 13); // Patch version. + sprintf(VersionText, ".%02d", 14); // Master version. + #endif + return(1); +#endif + +#endif //WIN32 +} + + +/*************************************************************************** + * Save_Recording_Values -- Saves recording values to a recording file * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/15/1995 BRR : Created. * + *=========================================================================*/ +void Save_Recording_Values(void) +{ + RecordFile.Write(&GameToPlay, sizeof(GameToPlay)); + RecordFile.Write(&ModemGameToPlay, sizeof(ModemGameToPlay)); + RecordFile.Write(&BuildLevel, sizeof(BuildLevel)); + RecordFile.Write(MPlayerName, sizeof(MPlayerName)); + RecordFile.Write(&MPlayerPrefColor, sizeof(MPlayerPrefColor)); + RecordFile.Write(&MPlayerColorIdx, sizeof(MPlayerColorIdx)); + RecordFile.Write(&MPlayerHouse, sizeof(MPlayerHouse)); + RecordFile.Write(&MPlayerLocalID, sizeof(MPlayerLocalID)); + RecordFile.Write(&MPlayerCount, sizeof(MPlayerCount)); + RecordFile.Write(&MPlayerBases, sizeof(MPlayerBases)); + RecordFile.Write(&MPlayerCredits, sizeof(MPlayerCredits)); + RecordFile.Write(&MPlayerTiberium, sizeof(MPlayerTiberium)); + RecordFile.Write(&MPlayerGoodies, sizeof(MPlayerGoodies)); + RecordFile.Write(&MPlayerGhosts, sizeof(MPlayerGhosts)); + RecordFile.Write(&MPlayerUnitCount, sizeof(MPlayerUnitCount)); + RecordFile.Write(MPlayerID, sizeof(MPlayerID)); + RecordFile.Write(MPlayerHouses, sizeof(MPlayerHouses)); + RecordFile.Write(&Seed, sizeof(Seed)); + RecordFile.Write(&Scenario, sizeof(Scenario)); + RecordFile.Write(&ScenPlayer, sizeof(ScenPlayer)); + RecordFile.Write(&ScenDir, sizeof(ScenDir)); + RecordFile.Write(&Whom, sizeof(Whom)); + RecordFile.Write(&Special, sizeof(SpecialClass)); + RecordFile.Write(&Options, sizeof(GameOptionsClass)); + RecordFile.Write(&FrameSendRate, sizeof(FrameSendRate)); + RecordFile.Write(&CommProtocol, sizeof(CommProtocol)); + + if (SuperRecord) { + RecordFile.Close(); + } +} + + +/*************************************************************************** + * Load_Recording_Values -- Loads recording values from recording file * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/15/1995 BRR : Created. * + *=========================================================================*/ +void Load_Recording_Values(void) +{ + Read_MultiPlayer_Settings(); + + RecordFile.Read(&GameToPlay, sizeof(GameToPlay)); + RecordFile.Read(&ModemGameToPlay, sizeof(ModemGameToPlay)); + RecordFile.Read(&BuildLevel, sizeof(BuildLevel)); + RecordFile.Read(MPlayerName, sizeof(MPlayerName)); + RecordFile.Read(&MPlayerPrefColor, sizeof(MPlayerPrefColor)); + RecordFile.Read(&MPlayerColorIdx, sizeof(MPlayerColorIdx)); + RecordFile.Read(&MPlayerHouse, sizeof(MPlayerHouse)); + RecordFile.Read(&MPlayerLocalID, sizeof(MPlayerLocalID)); + RecordFile.Read(&MPlayerCount, sizeof(MPlayerCount)); + RecordFile.Read(&MPlayerBases, sizeof(MPlayerBases)); + RecordFile.Read(&MPlayerCredits, sizeof(MPlayerCredits)); + RecordFile.Read(&MPlayerTiberium, sizeof(MPlayerTiberium)); + RecordFile.Read(&MPlayerGoodies, sizeof(MPlayerGoodies)); + RecordFile.Read(&MPlayerGhosts, sizeof(MPlayerGhosts)); + RecordFile.Read(&MPlayerUnitCount, sizeof(MPlayerUnitCount)); + RecordFile.Read(MPlayerID, sizeof(MPlayerID)); + RecordFile.Read(MPlayerHouses, sizeof(MPlayerHouses)); + RecordFile.Read(&Seed, sizeof(Seed)); + RecordFile.Read(&Scenario, sizeof(Scenario)); + RecordFile.Read(&ScenPlayer, sizeof(ScenPlayer)); + RecordFile.Read(&ScenDir, sizeof(ScenDir)); + RecordFile.Read(&Whom, sizeof(Whom)); + RecordFile.Read(&Special, sizeof(SpecialClass)); + RecordFile.Read(&Options, sizeof(GameOptionsClass)); + RecordFile.Read(&FrameSendRate, sizeof(FrameSendRate)); + RecordFile.Read(&CommProtocol, sizeof(CommProtocol)); +} + + +/*********************************************************************************************** + * Obfuscate -- Sufficiently transform parameter to thwart casual hackers. * + * * + * This routine borrows from CRC and PGP technology to sufficiently alter the parameter * + * in order to make it difficult to reverse engineer the key phrase. This is designed to * + * be used for hidden game options that will be released at a later time over Westwood's * + * Web page or through magazine hint articles. * + * * + * Since this is a one way transformation, it becomes much more difficult to reverse * + * engineer the pass phrase even if the resultant pass code is known. This has an added * + * benefit of making this algorithm immune to traditional cyrptographic attacks. * + * * + * The largest strength of this transformation algorithm lies in the restriction on the * + * source vector being legal ASCII uppercase characters. This restriction alone makes even * + * a simple CRC transformation practically impossible to reverse engineer. This algorithm * + * uses far more than a simple CRC transformation to achieve added strength from advanced * + * attack methods. * + * * + * INPUT: string -- Pointer to the key phrase that will be transformed into a code. * + * * + * OUTPUT: Returns with the code that the key phrase is translated into. * + * * + * WARNINGS: A zero length pass phrase results in a 0x00000000 result code. * + * * + * HISTORY: * + * 08/19/1995 JLB : Created. * + *=============================================================================================*/ +long Obfuscate(char const * string) +{ + char buffer[128]; + + if (!string) return(0); + memset(buffer, '\xA5', sizeof(buffer)); + + /* + ** Copy key phrase into a working buffer. This hides any transformation done + ** to the string. + */ + strncpy(buffer, string, sizeof(buffer)); + buffer[sizeof(buffer)-1] = '\0'; + int length = strlen(buffer); + + /* + ** Only upper case letters are significant. + */ + strupr(buffer); + + /* + ** Ensure that only visible ASCII characters compose the key phrase. This + ** discourages the direct forced illegal character input method of attack. + */ + for (int index = 0; index < length; index++) { + if (!isgraph(buffer[index])) { + buffer[index] = 'A' + (index%26); + } + } + + /* + ** Increase the strength of even short pass phrases by extending the + ** length to be at least a minimum number of characters. This helps prevent + ** a weak pass phrase from compromising the obfuscation process. This + ** process also forces the key phrase to be an even multiple of four. + ** This is necessary to support the cypher process that occurs later. + */ + if (length < 16 || (length & 0x03)) { + int maxlen = 16; + if (((length+3) & 0x00FC) > maxlen) { + maxlen = ((length+3) & 0x00FC); + } + for (index = length; index < maxlen; index++) { + buffer[index] = 'A' + ((('?' ^ buffer[index-length]) + index) % 26); + } + length = index; + buffer[length] = '\0'; + } + + /* + ** Transform the buffer into a number. This transformation is character + ** order dependant. + */ + long code = Calculate_CRC(buffer, length); + + /* + ** Record a copy of this initial transformation to be used in a later + ** self referential transformation. + */ + long copy = code; + + /* + ** Reverse the character string and combine with the previous transformation. + ** This doubles the workload of trying to reverse engineer the CRC calculation. + */ + strrev(buffer); + code ^= Calculate_CRC(buffer, length); + + /* + ** Perform a self referential transformation. This makes a reverse engineering + ** by using a cause and effect attack more difficult. + */ + code = code ^ copy; + + /* + ** Unroll and combine the code value into the pass phrase and then perform + ** another self referential transformation. Although this is a trivial cypher + ** process, it gives the sophisticated hacker false hope since the strong + ** cypher process occurs later. + */ + strrev(buffer); // Restore original string order. + for (index = 0; index < length; index++) { + code ^= (unsigned char)buffer[index]; + unsigned char temp = (unsigned char)code; + buffer[index] ^= temp; + code >>= 8; + code |= (((long)temp)<<24); + } + + /* + ** Introduce loss into the vector. This strengthens the key against traditional + ** cryptographic attack engines. Since this also weakens the key against + ** unconventional attacks, the loss is limited to less than 10%. + */ + for (index = 0; index < length; index++) { + static unsigned char _lossbits[] = {0x00,0x08,0x00,0x20,0x00,0x04,0x10,0x00}; + static unsigned char _addbits[] = {0x10,0x00,0x00,0x80,0x40,0x00,0x00,0x04}; + + buffer[index] |= _addbits[index % (sizeof(_addbits)/sizeof(_addbits[0]))]; + buffer[index] &= ~_lossbits[index % (sizeof(_lossbits)/sizeof(_lossbits[0]))]; + } + + /* + ** Perform a general cypher transformation on the vector + ** and use the vector itself as the cypher key. This is a variation on the + ** cypher process used in PGP. It is a very strong cypher process with no known + ** weaknesses. However, in this case, the cypher key is the vector itself and this + ** opens up a weakness against attacks that have access to this transformation + ** algorithm. The sheer workload of reversing this transformation should be enough + ** to discourage even the most determined hackers. + */ + for (index = 0; index < length; index += 4) { + short key1 = buffer[index]; + short key2 = buffer[index+1]; + short key3 = buffer[index+2]; + short key4 = buffer[index+3]; + short val1 = key1; + short val2 = key2; + short val3 = key3; + short val4 = key4; + + val1 *= key1; + val2 += key2; + val3 += key3; + val4 *= key4; + + short s3 = val3; + val3 ^= val1; + val3 *= key1; + short s2 = val2; + val2 ^= val4; + val2 += val3; + val2 *= key3; + val3 += val2; + + val1 ^= val2; + val4 ^= val3; + + val2 ^= s3; + val3 ^= s2; + + buffer[index] = val1; + buffer[index+1] = val2; + buffer[index+2] = val3; + buffer[index+3] = val4; + } + + /* + ** Convert this final vector into a cypher key code to be + ** returned by this routine. + */ + code = Calculate_CRC(buffer, length); + + /* + ** Return the final code value. + */ + return(code); +} diff --git a/INTERNET.CPP b/INTERNET.CPP new file mode 100644 index 0000000..12e44f6 --- /dev/null +++ b/INTERNET.CPP @@ -0,0 +1,876 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/************************************************************************************* + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + ************************************************************************************* + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : INTERNET.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : March 11th, 1996 * + * * + * Last Update : August 5th, 1996 [ST] * + * * + *-----------------------------------------------------------------------------------* + * Overview: * + * * + * Miscellaneous junk related to H2H internet connection. * + * * + *-----------------------------------------------------------------------------------* + * Functions: * + * Check_From_WChat -- Interprets start game packet from WChat * + * Read_Game_Options -- Read the game setup options from the wchat packet * + * Is_User_WChat_Registered -- retrieve the users wchat entry from registry * + * Spawn_WChat -- spawns or switches focus to wchat * + * Spawn_Registration_App -- spawns the C&C/Planet westwood registration app * + * Do_The_Internet_Menu_Thang -- Handle case where user clicks on 'Internet' button * + * * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include "tcpip.h" +#include "ccdde.h" + + + +/*************************************************************************** +** Internet specific globals +*/ +char PlanetWestwoodHandle[] = {"Handle"}; //Planet WW user name +char PlanetWestwoodPassword[] = {"Password"}; //Planet WW password +char PlanetWestwoodIPAddress[IP_ADDRESS_MAX] = {"206.154.108.87"}; //IP of server or other player +long PlanetWestwoodPortNumber = 1234; //Port number to send to +bool PlanetWestwoodIsHost = false; //Flag true if player has control of game options +unsigned long PlanetWestwoodGameID; //Game ID +unsigned long PlanetWestwoodStartTime; //Time that game was started +HWND WChatHWND = 0; //Handle to Wchat window. +bool UseVirtualSubnetServer; +int InternetMaxPlayers; +int WChatMaxAhead; +int WChatSendRate; + + +int Read_Game_Options(void); + +extern bool SpawnedFromWChat; + + + + + + +/*********************************************************************************************** + * Check_From_WChat -- This function reads in C&CSPAWN.INI and interprets it * + * C&CSPAWN.INI is now sent to us by WCHAT via DDE * + * * + * * + * * + * INPUT: Name of C&CSPAWN.INI file. If NULL then get file from DDE Server * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/9/96 1:44PM ST : Created * + *=============================================================================================*/ +void Check_From_WChat(char *wchat_name) +{ +#ifndef DEMO + + char default_string[] = {"Error"}; + char key_string[256]; + char *ini_file; + RawFileClass wchat_file; + + /* + ** Get a pointer to C&CSPAWN.INI either by reading it from disk or getting it from + ** the DDE server. + */ + if (wchat_name){ + ini_file = new char [8192]; + }else{ + ini_file = DDEServer.Get_MPlayer_Game_Info(); +#if (0) + /* + ** Save it to disk as well so I can see it + */ + RawFileClass anotherfile ("FROMCHAT.TXT"); + anotherfile.Write(ini_file, DDEServer.Get_MPlayer_Game_Info_Length()); +#endif //(0) + } + + if (wchat_name){ + wchat_file.Set_Name(wchat_name); + } + + if (!wchat_name || wchat_file.Is_Available()){ + + /* + ** Read the ini file from disk if we founf it there + */ + if (wchat_name){ + wchat_file.Read(ini_file, wchat_file.Size()); + } + + /* + ** Get the IP address + */ + key_string[0] = 0; + + WWGetPrivateProfileString("Internet", + "Address", + default_string, + key_string, + sizeof(key_string), + ini_file); + + + if (!strcmp (key_string, default_string)) { + if (wchat_name) delete ini_file; + return; + } + strcpy (PlanetWestwoodIPAddress, key_string); + + + + /* + ** Get the port number + */ + key_string[0] = 0; + + WWGetPrivateProfileString("Internet", + "Port", + default_string, + key_string, + sizeof(key_string), + ini_file); + + + if (!strcmp (key_string, default_string)) { + if (wchat_name) delete ini_file; + return; + } + + PlanetWestwoodPortNumber = atol(key_string); + + + /* + ** Get host or client + */ + key_string[0] = 0; + + WWGetPrivateProfileString("Internet", + "Host", + default_string, + key_string, + sizeof(key_string), + ini_file); + + + if (!strcmp (key_string, default_string)) { + if (wchat_name) delete ini_file; + return; + } + + if (strchr (key_string, '1')){ + PlanetWestwoodIsHost = true; + }else{ + PlanetWestwoodIsHost = false; + } + + UseVirtualSubnetServer = WWGetPrivateProfileInt("Internet", "UseVSS", 0, ini_file); + + Special.IsFromWChat = true; + } + + if (wchat_name) delete ini_file; + +#else //DEMO + + wchat_name = wchat_name; + +#endif //DEMO + +} + +//EventClass Wibble; + + +/*************************************************************************** + * Read_Game_Options -- reads multiplayer game options from disk * + * * + * This routine is used for multiplayer games which read the game options * + * from disk, rather than through a connection dialog. * + * * + * INPUT: * + * name of C&CSPAWN.INI file. Null if data should be got from DDE server* * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: \ * + * none. * + * * + * HISTORY: * + * 01/11/1996 BRR : Created. * + *=========================================================================*/ +int Read_Game_Options(char *name) +{ + char *buffer; + + char filename[256] = {"INVALID.123"}; + + if (name){ + strcpy (filename, name); + } + + /*------------------------------------------------------------------------ + Create filename and read the file. + ------------------------------------------------------------------------*/ + CCFileClass file (filename); + + if (name && !file.Is_Available()) { + return(0); + } else { + if (name){ + buffer = new char [8192]; // INI staging buffer pointer. + memset(buffer, '\0', 8192); + file.Read(buffer, 8192-1); + file.Close(); + }else{ + buffer = DDEServer.Get_MPlayer_Game_Info(); + } + } + + /*------------------------------------------------------------------------ + Get the player's name + ------------------------------------------------------------------------*/ + WWGetPrivateProfileString("Options", "Handle", "Noname", MPlayerName, + sizeof(MPlayerName), buffer); + strcpy(MPlayerGameName, MPlayerName); + MPlayerColorIdx = WWGetPrivateProfileInt("Options", "Color", 0, buffer); + MPlayerPrefColor = MPlayerColorIdx; + MPlayerHouse = (HousesType)WWGetPrivateProfileInt("Options", "Side", + HOUSE_GOOD, buffer); + + MPlayerCredits = WWGetPrivateProfileInt("Options", "Credits", 0, buffer); + MPlayerBases = WWGetPrivateProfileInt("Options", "Bases", 0, buffer); + MPlayerTiberium = WWGetPrivateProfileInt("Options", "Tiberium", 0, buffer); + MPlayerGoodies = WWGetPrivateProfileInt("Options", "Crates", 0, buffer); + MPlayerGhosts = WWGetPrivateProfileInt("Options", "AI", 0, buffer); + BuildLevel = WWGetPrivateProfileInt("Options", "BuildLevel", 0, buffer); + MPlayerUnitCount = WWGetPrivateProfileInt("Options", "UnitCount", 0, buffer); + Seed = WWGetPrivateProfileInt("Options", "Seed", 0, buffer); + Special.IsCaptureTheFlag = WWGetPrivateProfileInt("Options", "CaptureTheFlag", 0, buffer); + PlanetWestwoodGameID = WWGetPrivateProfileInt("Internet", "GameID", 0, buffer); + PlanetWestwoodStartTime = WWGetPrivateProfileInt ("Internet", "StartTime", 0, buffer); + WChatHWND = (HWND) WWGetPrivateProfileInt("Internet", "HWND", (int)FindWindow("OWL_Window", "Westwood Chat"), buffer); + + InternetMaxPlayers = WWGetPrivateProfileInt("Internet", "MaxPlayers", 2, buffer); + + + if (MPlayerTiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + ScenarioIdx = WWGetPrivateProfileInt("Options", "Scenario", 0, buffer); + Scenario = ScenarioIdx; //MPlayerFilenum[ScenarioIdx]; + + Options.GameSpeed = 0; + + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + MPlayerMaxAhead = WChatMaxAhead = WWGetPrivateProfileInt("Timing", "MaxAhead", 9, buffer); + FrameSendRate = WChatSendRate = WWGetPrivateProfileInt("Timing", "SendRate", 3, buffer); + + if (name) delete buffer; + return (1); + +} + + + + +/*********************************************************************************************** + * Get_Registry_Sub_Key -- search a registry key for a sub-key * + * * + * * + * * + * INPUT: handle of key to search * + * text to search for * + * true if old key should be closed when new key opened * + * * + * OUTPUT: handle to the key we found or 0 * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/12/96 2:11PM ST : Created * + *=============================================================================================*/ + +extern HKEY Get_Registry_Sub_Key (HKEY base_key, char *search_key, BOOL close); + + + +void Just_Path(char *path, char *destpath) +{ + char *terminator = NULL; //He'll be back. + + strcpy (destpath, path); + terminator = strrchr (destpath, '\\'); + if (terminator){ + *terminator = 0; + } +} + + + + + +/*********************************************************************************************** + * Is_User_WChat_Registered -- retrieve the users wchat entry from the registry * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if users wchat entry was found in the registry * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/12/96 2:13PM ST : Created * + *=============================================================================================*/ +bool Is_User_WChat_Registered(char *buffer, int buffer_len) +{ + HKEY key; + char user_handle[256]; + DWORD user_handle_size = sizeof (user_handle); + char user_pword[256]; + DWORD user_pword_size = sizeof (user_pword); + + + /* + ** Check HKEY_CLASSES_ROOT first. Old versions of Wchat register there + */ + key = Get_Registry_Sub_Key (HKEY_CLASSES_ROOT, "Wchat", FALSE); + + if (key){ + key = Get_Registry_Sub_Key (key, "Nick1", TRUE); + if (key){ + + if (RegQueryValue(key, "Nick", user_handle, (long*)&user_handle_size) == ERROR_SUCCESS){ + + if (RegQueryValue(key, "Pass", user_pword, (long*)&user_pword_size) == ERROR_SUCCESS){ + + /* + ** If the first char of the users name is non-numberic and there is a password + ** then return success + */ + if ((user_handle[0] < '0' || user_handle[0] > '9') && user_pword[0]){ + RegCloseKey( key ); + return (TRUE); + } + } + } + } + + RegCloseKey ( key ); + } + + + + /* + ** Check HKEY_LOCAL_MACKINE/Software + */ + user_handle_size = sizeof (user_handle); + + key = Get_Registry_Sub_Key (HKEY_LOCAL_MACHINE, "SOFTWARE", FALSE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "Westwood", TRUE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "InetReg", TRUE); + if (!key) return (FALSE); + + //key = Get_Registry_Sub_Key (key, "UserName", TRUE); + //if (!key) return (FALSE); + + //key = Get_Registry_Sub_Key (key, "Nick", TRUE); + //if (!key) return (FALSE); + + if (RegQueryValueEx(key, "UserName", NULL, NULL, (unsigned char*)user_handle, &user_handle_size) != ERROR_SUCCESS){ + RegCloseKey(key); + return (FALSE); + } + + RegCloseKey(key); + memcpy (buffer, user_handle, min(buffer_len, user_handle_size)); + + /* + ** If the first char of the users name is non-numeric then return success + */ + if (user_handle[0] < '0' || user_handle[0] > '9'){ + return (TRUE); + }else{ + return (FALSE); + } +} + + + +/*********************************************************************************************** + * Spawn_WChat -- spawns or switches focus to wchat * + * * + * * + * * + * INPUT: can launch. If set then we are allowed to launch WChat if not already running * + * * + * OUTPUT: True if wchat was spawned * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 12:33PM ST : Created * + *=============================================================================================*/ +bool Poke_WChat(void); +bool Spawn_WChat(bool can_launch) +{ + CCDebugString ("C&C95 - In Spawn_WChat.\n"); + char packet[10] = {"Hello"}; + HWND chat_window = NULL; + + /* + ** See if WChat is already running... + */ + if (WChatHWND && IsWindow (WChatHWND) ){ + chat_window = WChatHWND; + }else{ + chat_window = FindWindow ( "OWL_Window", "Westwood Chat" ); + } + + if (chat_window){ + /* + ** WChat is already running. Minimize myself then try to give it focus. + */ + Set_Palette(BlackPalette); + VisiblePage.Clear(); + ShowWindow (MainWindow, SW_MINIMIZE); + /* + ** Give windoze a couple of secs to sort itself out. + */ + CountDownTimerClass wibble_timer; + wibble_timer.Set ( 60 * 3, true); + + while (wibble_timer.Time()){ + /* + ** Call our message loop to make sure we get all the messages that are sent to us + ** when we minimise. + */ + Keyboard::Check(); + } + + /* + ** Send chat a tickle message so it knows to send the game stats to the server. + */ + if (GameStatisticsPacketSent && !PlanetWestwoodIsHost) { + Send_Data_To_DDE_Server (packet, strlen(packet), DDEServerClass::DDE_TICKLE); + } + /* + ** Give the focus to WChat + */ + SetForegroundWindow ( chat_window ); + ShowWindow ( chat_window, SW_RESTORE ); + return(true); + } + + /* + ** Fail if we aren't allowed to launch wchat and we couldnt find its window. + */ + if (!can_launch) return (false); + + /* + ** Find where WChat was installed to + */ + + HKEY key; + char wchat_loc[256]; + DWORD wchat_loc_size = 256; + + key = Get_Registry_Sub_Key (HKEY_LOCAL_MACHINE, "SOFTWARE", FALSE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "Westwood", TRUE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "WChat", TRUE); + if (!key) return (FALSE); + + //key = Get_Registry_Sub_Key (key, "UserName", TRUE); + //if (!key) return (FALSE); + + //key = Get_Registry_Sub_Key (key, "Nick", TRUE); + //if (!key) return (FALSE); + + if (RegQueryValueEx(key, "InstallPath", NULL, NULL, (unsigned char*)wchat_loc, &wchat_loc_size) != ERROR_SUCCESS){ + RegCloseKey(key); + return (FALSE); + } + + RegCloseKey(key); + + PROCESS_INFORMATION process_info; + STARTUPINFO start_info; + memset ((void*)&start_info, 0, sizeof(start_info)); + start_info.cb = sizeof(start_info); + char justpath [256]; + Just_Path(wchat_loc, justpath); + + /* + ** We found WChat in the registry. Minimize myself then try to spawn it. + */ + Set_Palette(BlackPalette); + VisiblePage.Clear(); + ShowWindow (MainWindow, SW_MINIMIZE); + /* + ** Give windoze a couple of secs to sort itself out. + */ + CountDownTimerClass wibble_timer; + wibble_timer.Set ( 60 * 3, true); + + while (wibble_timer.Time()){ + /* + ** Call our message loop to make sure we get all the messages that are sent to us + ** when we minimise. + */ + Keyboard::Check(); + } + bool success = CreateProcess (wchat_loc, NULL, NULL, NULL, false, 0, NULL, justpath, &start_info, &process_info); + + if (success){ + return (true); + }else{ + ShowWindow (MainWindow, SW_RESTORE); + while ( Keyboard::Check() ) {}; + return (false); + } +} + + + + +/*********************************************************************************************** + * Spawn_Registration_App -- spawns the C&C/Planet westwood registration app * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: True if app was spawned * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 12:33PM ST : Created * + *=============================================================================================*/ +bool Spawn_Registration_App(void) +{ + + /* + ** Find where inetreg was installed to + */ + + HKEY key; + char inetreg_loc[256]; + DWORD inetreg_loc_size = 256; + + key = Get_Registry_Sub_Key (HKEY_LOCAL_MACHINE, "SOFTWARE", FALSE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "Westwood", TRUE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "InetReg", TRUE); + if (!key) return (FALSE); + + if (RegQueryValueEx(key, "InstallPath", NULL, NULL, (unsigned char*)inetreg_loc, &inetreg_loc_size) != ERROR_SUCCESS){ + RegCloseKey(key); + return (FALSE); + } + + RegCloseKey(key); + + PROCESS_INFORMATION process_info; + STARTUPINFO start_info; + char justpath [256]; + memset ((void*)&start_info, 0, sizeof(start_info)); + start_info.cb = sizeof(start_info); + Just_Path(inetreg_loc, justpath); + + BOOL success = CreateProcess (inetreg_loc, NULL, NULL, NULL, false, 0, NULL, justpath, &start_info, &process_info); + if (success){ + //WaitForSingleObject (process_info.hProcess, 1000*10000); + //SetForegroundWindow ( MainWindow ); + //ShowWindow ( MainWindow, SW_RESTORE ); + } + return (success); + +} + + + + + +/*********************************************************************************************** + * Do_The_Internet_Menu_Thang -- Handle case where user clicks on 'Internet' button * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 8:30PM ST : Created * + *=============================================================================================*/ +bool Do_The_Internet_Menu_Thang(void) +{ +#ifndef DEMO + + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 120 *factor; // dialog width + int d_dialog_h = 80*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_margin1=10; + int d_txt6_h=15; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*factor; +#else + int d_cancel_w = 40*factor; +#endif + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*factor; + + char packet[10] = {"Hello"}; + +#if (GERMAN | FRENCH) + int width=160*factor; + int height=80*factor; +#else + int width=120*factor; + int height=80*factor; +#endif //GERMAN | FRENCH + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String((char*)Text_String (TXT_CONNECTING), SeenBuff.Get_Height(), width, height); + +#if (GERMAN | FRENCH) + d_dialog_w = width + 25*factor; + d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + d_cancel_x = d_dialog_cx - (d_cancel_w / 2); +#endif + + /* + ** Button Enumerations + */ + enum { + BUTTON_CANCEL = 100, + }; + + /* + ** Buttons + */ + //TextButtonClass *buttons; // button list + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + //buttons = &cancelbtn; + + Fancy_Text_Print(TXT_NONE, 0, 0, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + + char users_name[256]; + int buffer_len = sizeof (users_name); + bool process; + bool display; + KeyNumType input; + + + if (!Special.IsFromWChat && !SpawnedFromWChat){ + /* + ** If the user is registered with Planet Westwood then spawn WChat. + */ + if (Is_User_WChat_Registered(users_name, buffer_len)){ + GameStatisticsPacketSent = false; + if (!Spawn_WChat(true)){ + Set_Logic_Page(SeenBuff); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + CCMessageBox().Process(TXT_ERROR_UNABLE_TO_RUN_WCHAT, TXT_OK); + LogicPage->Clear(); + return(false); + } + }else{ + Set_Logic_Page(SeenBuff); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + if (CCMessageBox().Process(TXT_EXPLAIN_REGISTRATION, TXT_REGISTER, TXT_CANCEL)){ + LogicPage->Clear(); + return(false); + }else{ + LogicPage->Clear(); + Spawn_Registration_App(); + return(false); + } + } + } + + /* + ** + ** User is registered and we spawned WChat. Wait for a game start message from WChat. + ** + */ + + process = true; + display = true; + + while (process){ + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored = FALSE; + display = true; + } + + if (display) { + + Set_Logic_Page(SeenBuff); + + Hide_Mouse(); + /* + ** Redraw backgound & dialog box + */ + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /* + ** Dialog & Field labels + */ + Draw_Caption (TXT_NONE, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(TXT_CONNECTING, d_dialog_cx-width/2, d_dialog_y + 25*factor, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + //cancelbtn.Zap(); + //buttons = &cancelbtn; + + /* + .................... Rebuild the button list .................... + */ + //buttons->Draw_All(); + cancelbtn.Draw_Me(true); + + Show_Mouse(); + display = false; + } + + + + /* + ** See if the game start packet has arrived from wchat yet. + */ + if (DDEServer.Get_MPlayer_Game_Info()){ + //MessageBox (NULL, "About to restore focus to C&C95", "C&C95", MB_OK); + //SetForegroundWindow ( MainWindow ); + //ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + return(true); + } + + //input = buttons->Input(); + input = cancelbtn.Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + + /* + ** Cancel. Just return to the main menu + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + Send_Data_To_DDE_Server (packet, strlen(packet), DDEServerClass::DDE_CONNECTION_FAILED); + GameStatisticsPacketSent = false; + Spawn_WChat(false); + break; + } + + } + +#endif //DEMO + + return (false); + + +} + + + + + + + + diff --git a/INTERPAL.CPP b/INTERPAL.CPP new file mode 100644 index 0000000..712a06e --- /dev/null +++ b/INTERPAL.CPP @@ -0,0 +1,458 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INTERPAL.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : December 7th 1995 * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * This module contains functions to allow use of old 320x200 animations on a 640x400 screen * + * * + * Functions: * + * Read_Interpolation_Palette -- reads an interpolation palette table from disk * + * Write_Interpolation_Palette -- writes an interpolation palette to disk * + * Create_Palette_Interpolation_Table -- build the palette interpolation table * + * Increase_Palette_Luminance -- increase the contrast of a palette * + * Interpolate_2X_Scale -- Stretch a 320x200 graphic buffer into 640x400 * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +BOOL InterpolationPaletteChanged = FALSE; +extern "C" { +extern void __cdecl Asm_Interpolate (unsigned char* src_ptr , + unsigned char* dest_ptr , + int lines , + int src_width , + int dest_width); + +extern void __cdecl Asm_Interpolate_Line_Double (unsigned char* src_ptr , + unsigned char* dest_ptr , + int lines , + int src_width , + int dest_width); + +extern void __cdecl Asm_Interpolate_Line_Interpolate (unsigned char* src_ptr , + unsigned char* dest_ptr , + int lines , + int src_width , + int dest_width); + +} + +extern "C"{ + unsigned char PaletteInterpolationTable[SIZE_OF_PALETTE][SIZE_OF_PALETTE]; + unsigned char *InterpolationPalette; +} + + + +/*********************************************************************************************** + * Read_Interpolatioin_Palette -- reads an interpolation palette table from disk * + * * + * * + * * + * INPUT: name of palette file * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/12/95 12:15PM ST : Created * + *=============================================================================================*/ + +void Read_Interpolation_Palette (char const *palette_file_name) +{ + CCFileClass palette_file(palette_file_name); + + if (palette_file.Is_Available()){ + palette_file.Open(READ); + palette_file.Read(&PaletteInterpolationTable[0][0],256*256); + palette_file.Close(); + InterpolationPaletteChanged = FALSE; + } +} + + +/*********************************************************************************************** + * Write_Interpolatioin_Palette -- writes an interpolation palette table to disk * + * * + * * + * * + * INPUT: name of palette file * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/12/95 12:15PM ST : Created * + *=============================================================================================*/ + +void Write_Interpolation_Palette (char const *palette_file_name) +{ + CCFileClass palette_file(palette_file_name); + + if (!palette_file.Is_Available()){ + palette_file.Open(WRITE); + palette_file.Write(&PaletteInterpolationTable[0][0],256*256); + palette_file.Close(); + } +} + + + + + +/*************************************************************************** + * CREATE_PALETTE_INTERPOLATION_TABLE * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/06/1995 MG : Created. * + *=========================================================================*/ +void Create_Palette_Interpolation_Table( void ) +{ + + Asm_Create_Palette_Interpolation_Table(); + + #if (0) + + int i; + int j; + int p; + unsigned char *first_palette_ptr; + unsigned char *second_palette_ptr; + unsigned char *match_pal_ptr; + int first_r; + int first_g; + int first_b; + int second_r; + int second_g; + int second_b; + int diff_r; + int diff_g; + int diff_b; + int dest_r; + int dest_g; + int dest_b; + int distance; + int closest_distance; + int index_of_closest_color; + + // + // Create an interpolation table for the current palette. + // + first_palette_ptr = (unsigned char *) InterpolationPalette; + for ( i = 0; i < SIZE_OF_PALETTE; i ++ ) { + + // + // Get the first palette entry's RGB. + // + first_r = *first_palette_ptr; + first_palette_ptr ++; + first_g = *first_palette_ptr; + first_palette_ptr ++; + first_b = *first_palette_ptr; + first_palette_ptr ++; + + second_palette_ptr = (unsigned char *) InterpolationPalette; + for ( j = 0; j < SIZE_OF_PALETTE; j ++ ) { + // + // Get the second palette entry's RGB. + // + second_r = *second_palette_ptr; + second_palette_ptr ++; + second_g = *second_palette_ptr; + second_palette_ptr ++; + second_b = *second_palette_ptr; + second_palette_ptr ++; + + // + // Now calculate the RGB halfway between the first and second colors. + // + dest_r = ( first_r + second_r ) >> 1; + dest_g = ( first_g + second_g ) >> 1; + dest_b = ( first_b + second_b ) >> 1; + + // + // Now find the color in the palette that most closely matches the interpolated color. + // + index_of_closest_color = 0; +// closest_distance = (256 * 256) * 3; + closest_distance = 500000; + match_pal_ptr = (unsigned char *) InterpolationPalette; + for ( p = 0; p < SIZE_OF_PALETTE; p ++ ) { + diff_r = ( ((int) (*match_pal_ptr)) - dest_r ); + match_pal_ptr ++; + diff_g = ( ((int) (*match_pal_ptr)) - dest_g ); + match_pal_ptr ++; + diff_b = ( ((int) (*match_pal_ptr)) - dest_b ); + match_pal_ptr ++; + + distance = ( diff_r * diff_r ) + ( diff_g * diff_g ) + ( diff_b * diff_b ); + if ( distance < closest_distance ) { + closest_distance = distance; + index_of_closest_color = p; + } + } + + PaletteInterpolationTable[ i ][ j ] = (unsigned char) index_of_closest_color; + } + } + + #endif + InterpolationPaletteChanged = FALSE; + return; + +} + + + + + + + + + +/*********************************************************************************************** + * Increase_Palette_Luminance -- increase contrast of colours in a palette * + * * + * * + * * + * INPUT: ptr to palette * + * percentage increase of red * + * percentage increase of green * + * percentage increase of blue * + * cap value for colours * + * * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/12/95 12:16PM ST : Created * + *=============================================================================================*/ + +void Increase_Palette_Luminance (unsigned char *palette , int red_percentage , int green_percentage , int blue_percentage ,int cap) +{ + + unsigned int red; + unsigned int green; + unsigned int blue; + for (int i=0 ; iGet_IsDirectDraw()){ + if (!source->Lock()){ + if (dest == &SeenBuff) Show_Mouse(); + return; + } + source_locked = TRUE; + } + if (dest->Get_IsDirectDraw()){ + if (!dest->Lock()) { + if (source_locked){ + source->Unlock(); + } + if (dest == &SeenBuff) Show_Mouse(); + return; + } + dest_locked = TRUE; + } + + + // + // Get pointers to the source and destination buffers. + // + src_ptr = (unsigned char *) source->Get_Offset(); + dest_ptr = (unsigned char *) dest->Get_Offset(); + end_of_source = src_ptr + ( source->Get_Width() * source->Get_Height() ); + + // + // Get width of source and dest buffers. + // + src_width = source->Get_Width(); + dest_width = 2*(dest->Get_Width() + dest->Get_XAdd() + dest->Get_Pitch()); + last_dest_ptr = dest_ptr; + + /* + ** Call the appropriate assembly language copy routine + */ +#if (1) + switch (CopyType){ + case 0: + Asm_Interpolate ( src_ptr , dest_ptr , source->Get_Height() , src_width , dest_width); + break; + + case 1: + Asm_Interpolate_Line_Double( src_ptr , dest_ptr , source->Get_Height() , src_width , dest_width); + break; + + case 2: + Asm_Interpolate_Line_Interpolate( src_ptr , dest_ptr , source->Get_Height() , src_width , dest_width); + break; + } +#endif + +#if (0) + // + // Copy over the first pixel (upper left). + // + *dest_ptr = *src_ptr; + src_ptr ++; + dest_ptr ++; + + // + // Scale copy. + // + width_counter = 0; + while ( src_ptr < end_of_source ) { + + // + // Blend this pixel with the one to the left and place this new color in the dest buffer. + // + *dest_ptr = PaletteInterpolationTable[ (*src_ptr) ][ (*( src_ptr - 1 )) ]; + dest_ptr ++; + + // + // Now place the source pixel into the dest buffer. + // + *dest_ptr = *src_ptr; + + src_ptr ++; + dest_ptr ++; + + width_counter ++; + if ( width_counter == src_width ) { + width_counter = 0; + last_dest_ptr += dest_width; + dest_ptr = last_dest_ptr; + } + } + +#endif + if (source_locked) source->Unlock(); + if (dest_locked) dest->Unlock(); + if (dest == &SeenBuff) Show_Mouse(); + +} +#endif + + + + diff --git a/INTRO.CPP b/INTRO.CPP new file mode 100644 index 0000000..8559343 --- /dev/null +++ b/INTRO.CPP @@ -0,0 +1,313 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\intro.cpv 1.6 16 Oct 1995 16:50:18 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INTRO.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : May 8, 1995 * + * * + * Last Update : May 8, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "textblit.h" + +#ifndef DEMO + +VQAHandle *Open_Movie(char *name); +VQAHandle *Open_Movie(char *name) +{ + if (!Debug_Quiet && Get_Digi_Handle() != -1) { + AnimControl.OptionFlags |= VQAOPTF_AUDIO; + } else { + AnimControl.OptionFlags &= ~VQAOPTF_AUDIO; + } + + VQAHandle * vqa = VQA_Alloc(); + if (vqa) { + VQA_Init(vqa, MixFileHandler); + + if (VQA_Open(vqa, name, &AnimControl) != 0) { + VQA_Free(vqa); + vqa = 0; + } + } + return(vqa); +} + + +/*********************************************************************************************** + * Choose_Side -- play the introduction movies, select house * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 5/08/1995 BWG : Created. * + *=============================================================================================*/ +void Choose_Side(void) +{ + //static char const _yellowpal[]={0x0,0x0,0xC9,0x0,0xBA,0x0,0x93,0x0,0x61,0x0,0x0,0x0,0x0,0x0,0xEE,0x0}; + //static char const _redpal[] ={0x0,0x0,0xA8,0x0,0xD9,0x0,0xDA,0x0,0xE1,0x0,0x0,0x0,0x0,0x0,0xD4,0x0}; + //static char const _graypal[] ={0x0,0x0,0x17,0x0,0x10,0x0,0x12,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x1C,0x0}; + + static char const _yellowpal[]={0x0,0xC9,0xBA,0x93,0x61,0xEE,0xee,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0}; + static char const _redpal[] ={0x0,0xa8,0xd9,0xda,0xe1,0xd4,0xDA,0x0,0xE1,0x0,0x0,0x0,0x0,0x0,0xD4,0x0}; + static char const _graypal[] ={0x0,0x17,0x10,0x12,0x14,0x1c,0x12,0x1c,0x14,0x0,0x0,0x0,0x0,0x0,0x1C,0x0}; + + + void *anim; + VQAHandle *gdibrief=0, *nodbrief=0; + void const *staticaud, *oldfont; + void const *speechg, *speechn, *speech; + int statichandle, speechhandle, speechplaying = 0; + int oldfontxspacing = FontXSpacing; + int setpalette = 0; + int gdi_start_palette; + + MEMORYSTATUS mem_info; + mem_info.dwLength=sizeof(mem_info); + GlobalMemoryStatus(&mem_info); + + TextPrintBuffer = new GraphicBufferClass(SeenBuff.Get_Width(), SeenBuff.Get_Height(), (void*)NULL); + TextPrintBuffer->Clear(); + BlitList.Clear(); + PseudoSeenBuff = new GraphicBufferClass(320,200,(void*)NULL); + int frame = 0, endframe = 255, selection = 0, lettersdone = 0; + + Hide_Mouse(); +/* Change to the six-point font for Text_Print */ + oldfont = Set_Font(ScoreFontPtr); + + Call_Back(); + + staticaud = Load_Alloc_Data(CCFileClass("STRUGGLE.AUD")); + speechg = Load_Alloc_Data(CCFileClass("GDI_SLCT.AUD")); + speechn = Load_Alloc_Data(CCFileClass("NOD_SLCT.AUD")); + +// staticaud = MixFileClass::Retrieve("STRUGGLE.AUD"); +// speechg = MixFileClass::Retrieve("GDI_SLCT.AUD"); +// speechn = MixFileClass::Retrieve("NOD_SLCT.AUD"); + + if (Special.IsFromInstall){ + if (mem_info.dwTotalPhys >= 12*1024*1024){ + VisiblePage.Clear(); + PreserveVQAScreen = 1; + Play_Movie("INTRO2", THEME_NONE, false); + } + BreakoutAllowed = true; + } + + //anim = Open_Animation("CHOOSE.WSA",NULL,0L,(WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE),Palette); + anim = Open_Animation("CHOOSE.WSA",NULL,0L,(WSAOpenType)(WSA_OPEN_FROM_DISK | WSA_OPEN_TO_PAGE),Palette); + Call_Back(); + InterpolationPaletteChanged = TRUE; + InterpolationPalette = Palette; + Read_Interpolation_Palette("SIDES.PAL"); + + nodbrief = Open_Movie("NOD1PRE.VQA"); + gdi_start_palette = Load_Interpolated_Palettes("NOD1PRE.VQP"); + Call_Back(); + gdibrief = Open_Movie("GDI1.VQA"); + Load_Interpolated_Palettes("GDI1.VQP" , TRUE); + + WWMouse->Erase_Mouse(&HidPage, TRUE); + HiddenPage.Clear(); + PseudoSeenBuff->Clear(); + SysMemPage.Clear(); + //if (!Special.IsFromInstall) { + VisiblePage.Clear(); + Set_Palette(Palette); + //} else { + //setpalette = 1; + //} + + statichandle = Play_Sample(staticaud,255,64); + CountDownTimerClass sample_timer; + sample_timer.Set(0x3f); + Alloc_Object(new ScorePrintClass(TXT_GDI_NAME, 0, 180,_yellowpal)); +#ifdef FRENCH + Alloc_Object(new ScorePrintClass(TXT_GDI_NAME2, 0, 187,_yellowpal)); +#endif + Alloc_Object(new ScorePrintClass(TXT_NOD_NAME, 180, 180,_redpal)); + +#ifdef GERMAN + Alloc_Object(new ScorePrintClass(TXT_SEL_TRANS,57, 190,_graypal)); +#else +#ifdef FRENCH + Alloc_Object(new ScorePrintClass(TXT_SEL_TRANS,103, 194,_graypal)); +#else + Alloc_Object(new ScorePrintClass(TXT_SEL_TRANS,103, 190,_graypal)); +#endif +#endif + Keyboard::Clear(); + + while (Get_Mouse_State()) Show_Mouse(); + + while (endframe != frame || (speechplaying && Is_Sample_Playing(speech)) ) { + Animate_Frame(anim, SysMemPage, frame++); + if (setpalette) { + Wait_Vert_Blank(); + Set_Palette(Palette); + setpalette = 0; + } + SysMemPage.Blit(*PseudoSeenBuff,0,22, 0,22, 320,156); + + /* + ** If the sample has stopped or is about to then restart it + */ + if (!Is_Sample_Playing(staticaud) || !sample_timer.Time()) { + Stop_Sample(statichandle); + statichandle = Play_Sample(staticaud,255,64); + sample_timer.Set(0x3f); + } + Call_Back_Delay(3); // delay only if haven't clicked + + /* keep the mouse hidden until the letters are thru printing */ + if (!lettersdone) { + lettersdone = true; + for(int i=0; i < MAXSCOREOBJS; i++) if (ScoreObjs[i]) lettersdone = 0; + if (lettersdone) { + Show_Mouse(); + } + } + if (frame >= Get_Animation_Frame_Count(anim)) frame = 0; + if (Keyboard::Check() && endframe == 255) { + if ((Keyboard::Get() & 0x10FF) == KN_LMOUSE) { + if ((_Kbd->MouseQY > 48*2) && (_Kbd->MouseQY < 150*2)) { + if ((_Kbd->MouseQX > 18*2) && (_Kbd->MouseQX < 148*2)) { + + // Chose GDI + Whom = HOUSE_GOOD; + ScenPlayer = SCEN_PLAYER_GDI; + endframe = 0; + speechhandle = Play_Sample(speechg); + speechplaying = true; + speech = speechg; + + } else if ((_Kbd->MouseQX > 160*2) && (_Kbd->MouseQX < 300*2)) { + // Chose Nod + selection = 1; + endframe = 14; + Whom = HOUSE_BAD; + ScenPlayer = SCEN_PLAYER_NOD; + speechhandle = Play_Sample(speechn); + speechplaying = true; + speech = speechn; + } + } + } + } + } + + Hide_Mouse(); + Close_Animation(anim); + + // erase the "choose side" text + PseudoSeenBuff->Fill_Rect(0,180,319,199,0); + SeenBuff.Fill_Rect(0,180*2, 319*2, 199*2, 0); + Interpolate_2X_Scale (PseudoSeenBuff , &SeenBuff ,"SIDES.PAL"); + Keyboard::Clear(); + SysMemPage.Clear(); + + /* + ** Skip the briefings if we're in special mode. + */ + if (Special.IsJurassic && AreThingiesEnabled) { + if (nodbrief) { + VQA_Close(nodbrief); + VQA_Free(nodbrief); + nodbrief = NULL; + } + if (gdibrief) { + VQA_Close(gdibrief); + VQA_Free(gdibrief); + gdibrief = NULL; + } + } + + /* play the scenario 1 briefing movie */ + if (Whom == HOUSE_GOOD) { + if (nodbrief) { + VQA_Close(nodbrief); + VQA_Free(nodbrief); + } + if (gdibrief) { + PaletteCounter = gdi_start_palette; + VQA_Play(gdibrief, VQAMODE_RUN); + VQA_Close(gdibrief); + VQA_Free(gdibrief); + } + } else { + if (gdibrief) { + VQA_Close(gdibrief); + VQA_Free(gdibrief); + } + if (nodbrief) { + VQA_Play(nodbrief, VQAMODE_RUN); + VQA_Close(nodbrief); + VQA_Free(nodbrief); + } + } + + Free_Interpolated_Palettes(); + Set_Primary_Buffer_Format(); +/* get rid of all the animating objects */ + for (int i = 0; i < MAXSCOREOBJS; i++) if (ScoreObjs[i]) { + delete ScoreObjs[i]; + ScoreObjs[i] = 0; + } + + if (Whom == HOUSE_GOOD) { + /* + ** Make sure the screen's fully clear after the movie plays + */ + VisiblePage.Clear(); + memset(BlackPalette, 0x01, 768); + Set_Palette(BlackPalette); + memset(BlackPalette, 0x00, 768); + } else { + PreserveVQAScreen = 1; + } + Free(staticaud); + Free(speechg); + Free(speechn); + + Set_Font(oldfont); + FontXSpacing = oldfontxspacing; + + delete PseudoSeenBuff; + delete TextPrintBuffer; + TextPrintBuffer = NULL; + BlitList.Clear(); +} +#endif diff --git a/INTRO.H b/INTRO.H new file mode 100644 index 0000000..50a3a95 --- /dev/null +++ b/INTRO.H @@ -0,0 +1,42 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INTRO.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : May 8, 1995 * + * * + * Last Update : May 8, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef INTRO_H +#define INTRO_H + +void Choose_Side(void); + +#endif diff --git a/IOMAP.CPP b/IOMAP.CPP new file mode 100644 index 0000000..8463c11 --- /dev/null +++ b/IOMAP.CPP @@ -0,0 +1,1051 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\iomap.cpv 2.18 16 Oct 1995 16:50:34 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IOMAP.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : January 16, 1995 * + * * + * Last Update : January 16, 1995 [BR] * + * * + *---------------------------------------------------------------------------------------------* + * All map-related loading/saving routines should go in this module, so it can be overlayed. * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CellClass::Code_Pointers -- codes class's pointers for load/save * + * CellClass::Decode_Pointers -- decodes pointers for load/save * + * CellClass::Load -- Reads from a save game file. * + * CellClass::Save -- Write to a save game file. * + * CellClass::Should_Save -- Should the cell be written to disk? * + * DisplayClass::Code_Pointers -- codes class's pointers for load/save * + * DisplayClass::Decode_Pointers -- decodes pointers for load/save * + * GScreenClass::Code_Pointers -- codes class's pointers for load/save * + * GScreenClass::Decode_Pointers -- decodes pointers for load/save * + * HelpClass::Code_Pointers -- codes class's pointers for load/save * + * HelpClass::Decode_Pointers -- decodes pointers for load/save * + * MapClass::Code_Pointers -- codes class's pointers for load/save * + * MapClass::Decode_Pointers -- decodes pointers for load/save * + * MouseClass::Code_Pointers -- codes class's pointers for load/save * + * MouseClass::Decode_Pointers -- decodes pointers for load/save * + * MouseClass::Load -- Loads from a save game file. * + * MouseClass::Save -- Saves to a save game file. * + * PowerClass::Code_Pointers -- codes class's pointers for load/save * + * PowerClass::Decode_Pointers -- decodes pointers for load/save * + * RadarClass::Code_Pointers -- codes class's pointers for load/save * + * RadarClass::Decode_Pointers -- decodes pointers for load/save * + * ScrollClass::Code_Pointers -- codes class's pointers for load/save * + * ScrollClass::Decode_Pointers -- decodes pointers for load/save * + * SidebarClass::Code_Pointers -- codes class's pointers for load/save * + * SidebarClass::Decode_Pointers -- decodes pointers for load/save * + * SidebarClass::StripClass::Code_Pointers -- codes class's pointers for load/save * + * SidebarClass::StripClass::Decode_Pointers -- decodes pointers for load/save * + * TabClass::Code_Pointers -- codes class's pointers for load/save * + * TabClass::Decode_Pointers -- decodes pointers for load/save * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * CellClass::Should_Save -- Should the cell be written to disk? * + * * + * This function will determine if the cell needs to be written to disk. Any cell that * + * contains special data should be written to disk. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Should this cell's data be written to disk? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Should_Save(void) const +{ + return( + (Smudge != SMUDGE_NONE) || + (TType != TEMPLATE_NONE) || + (Overlay != OVERLAY_NONE) || + IsMapped || + IsVisible || + IsTrigger || + Flag.Composite || + OccupierPtr || + Overlapper[0] || Overlapper[1] || Overlapper[2] + ); +} + + +/*********************************************************************************************** + * CellClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Load(FileClass & file) +{ + int rc; + TriggerClass * trig; + + /* + -------------------------- Load the object data -------------------------- + */ + rc = Read_Object(this, sizeof(CellClass), sizeof(CellClass), file, 0); + + /* + ------------------------ Load the trigger pointer ------------------------ + */ + if (rc) { + if (IsTrigger) { + if (file.Read(&trig,sizeof(trig)) != sizeof(trig)) + return(false); + CellTriggers[Cell_Number()] = trig; + } + } + + return(rc); +} + + +/*********************************************************************************************** + * CellClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Save(FileClass & file) +{ + int rc; + TriggerClass * trig; + + /* + -------------------------- Save the object data -------------------------- + */ + rc = Write_Object(this, sizeof(CellClass), file); + + /* + ------------------------ Save the trigger pointer ------------------------ + */ + if (rc) { + if (IsTrigger) { + trig = CellTriggers[Cell_Number()]; + if (file.Write(&trig,sizeof(trig)) != sizeof(trig)) + return(false); + } + } + + return(rc); +} + + +/*********************************************************************************************** + * CellClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void CellClass::Code_Pointers(void) +{ + if (Cell_Occupier()) { + OccupierPtr = (ObjectClass *)OccupierPtr->As_Target(); + } + + if (Overlapper[0]) { + Overlapper[0] = (ObjectClass *)Overlapper[0]->As_Target(); + } + + if (Overlapper[1]) { + Overlapper[1] = (ObjectClass *)Overlapper[1]->As_Target(); + } + + if (Overlapper[2]) { + Overlapper[2] = (ObjectClass *)Overlapper[2]->As_Target(); + } + + /* + ------------------------ Convert trigger pointer ------------------------- + */ + if (IsTrigger) { + CellTriggers[Cell_Number()] = (TriggerClass *)CellTriggers[Cell_Number()]->As_Target(); + } +} + + +/*********************************************************************************************** + * CellClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void CellClass::Decode_Pointers(void) +{ + if (OccupierPtr) { + OccupierPtr = As_Object((TARGET)OccupierPtr); + Check_Ptr((void *)OccupierPtr,__FILE__,__LINE__); + } + + if (Overlapper[0]) { + Overlapper[0] = As_Object((TARGET)Overlapper[0]); + Check_Ptr((void *)Overlapper[0],__FILE__,__LINE__); + } + + if (Overlapper[1]) { + Overlapper[1] = As_Object((TARGET)Overlapper[1]); + Check_Ptr((void *)Overlapper[1],__FILE__,__LINE__); + } + + if (Overlapper[2]) { + Overlapper[2] = As_Object((TARGET)Overlapper[2]); + Check_Ptr((void *)Overlapper[2],__FILE__,__LINE__); + } + + /* + ** Convert trigger pointer. + */ + if (IsTrigger) { + CellTriggers[Cell_Number()] = As_Trigger( (TARGET)CellTriggers[Cell_Number()] ); + Check_Ptr((void *)CellTriggers[Cell_Number()],__FILE__,__LINE__); + } +} + + +/*********************************************************************************************** + * MouseClass::Load -- Loads from a save game file. * + * * + * Loading the map is very complicated. Here are the steps: * + * - Read the Theater for this save-game * + * - call Init_Theater to perform theater-specific inits * + * - call Free_Cells to free the cell array, because loading the map object will overwrite * + * the pointer to the cell array * + * - read the map object from disk * + * - call Alloc_Cells to re-allocate the cell array * + * - call Init_Cells to set the cells to a known state, because not every cell will be loaded * + * - read the cell objects into the cell array * + * - After the map & all objects have been loaded & the pointers decoded, Init_IO() >MUST< be * + * called to restore the map's button list to the proper state. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool MouseClass::Load(FileClass & file) +{ + unsigned count; + CELL cell = 0; + int index; +// int rc; +// int i; +// int j; + + /*------------------------------------------------------------------------ + Load Theater: Even though this value is located in the DisplayClass, + it must be loaded first so initialization can be done before any other + map data is loaded. If initialization isn't done first, data read from + disk will be over-written when initialization occurs. This code must + go in the most-derived Map class. + ------------------------------------------------------------------------*/ + if (file.Read (&Theater,sizeof(Theater)) != sizeof(Theater)) + return(false); + + /* + ** Remove any old theater specific uncompressed shapes + */ + if (Theater != LastTheater){ + Reset_Theater_Shapes(); + } + + /* + ------------------------- Init display mixfiles -------------------------- + */ + Init_Theater(Theater); + TerrainTypeClass::Init(Theater); + TemplateTypeClass::Init(Theater); + OverlayTypeClass::Init(Theater); + UnitTypeClass::Init(Theater); + InfantryTypeClass::Init(Theater); + BuildingTypeClass::Init(Theater); + BulletTypeClass::Init(Theater); + AnimTypeClass::Init(Theater); + AircraftTypeClass::Init(Theater); + SmudgeTypeClass::Init(Theater); + + LastTheater = Theater; + + /* + ** Free the cell array, because we're about to overwrite its pointers + */ + Free_Cells(); + + /* + ** Read the entire map object in. Only read in sizeof(MouseClass), so if we're + ** in editor mode, none of the map editor object is read in. + */ + if (!Read_Object(this, sizeof(VectorClass), sizeof(MouseClass), file, VTable)) { + return(false); + } + + /* + ** Reallocate the cell array + */ + Alloc_Cells(); + + /* + ** Init all cells to empty + */ + Init_Cells(); + + /* + --------------------------- Read # cells saved --------------------------- + */ + if (file.Read(&count, sizeof(count)) != sizeof(count)) { + return(false); + } + + /* + ------------------------------- Read cells ------------------------------- + */ + for (index = 0; index < count; index++) { + if (file.Read(&cell, sizeof(cell)) != sizeof(cell)) + return(false); + + if (!(*this)[cell].Load(file)) + return(false); + } + + return(true); +} + + +/*********************************************************************************************** + * MouseClass::Save -- Save to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool MouseClass::Save(FileClass & file) +{ + unsigned count; + long pos; + + /* + -------------------------- Save Theater >first< -------------------------- + */ + if (file.Write (&Theater,sizeof(Theater)) != sizeof(Theater)) + return(false); + + if (!Write_Object(this, sizeof(MouseClass), file)) + return(false); + + /* + ---------------------- Record current file position ---------------------- + */ + pos = file.Seek(0, SEEK_CUR); + + /* + ---------------------- write out placeholder bytes ----------------------- + */ + if (file.Write(&count, sizeof(count)) != sizeof(count)) + return(false); + + /* + ------------------------ Save cells that need it ------------------------- + */ + count = 0; + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if ((*this)[cell].Should_Save()) { + if (file.Write(&cell, sizeof(cell)) != sizeof(cell)) + return(false); + + count++; + + if (!(*this)[cell].Save(file)) + return(false); + } + } + + /* + -------------------------- Save # cells written -------------------------- + */ + file.Seek(pos, SEEK_SET); + + if (file.Write(&count, sizeof(count)) != sizeof(count)) + return(false); + + file.Seek(0, SEEK_END); + + return(true); +} + + +/*********************************************************************************************** + * MouseClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void MouseClass::Code_Pointers(void) +{ +// Control.Code_Pointers(); + + ScrollClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * MouseClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void MouseClass::Decode_Pointers(void) +{ +// Control.Decode_Pointers(); + + ScrollClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * ScrollClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void ScrollClass::Code_Pointers(void) +{ + HelpClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * ScrollClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void ScrollClass::Decode_Pointers(void) +{ + HelpClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * HelpClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void HelpClass::Code_Pointers(void) +{ + TabClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * HelpClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void HelpClass::Decode_Pointers(void) +{ + TabClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * TabClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TabClass::Code_Pointers(void) +{ + SidebarClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * TabClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TabClass::Decode_Pointers(void) +{ + SidebarClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * PowerClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void PowerClass::Code_Pointers(void) +{ + RadarClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * PowerClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void PowerClass::Decode_Pointers(void) +{ + RadarClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * SidebarClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void SidebarClass::Code_Pointers(void) +{ + for (int i = 0; i < COLUMNS; i++) { + Column[i].Code_Pointers(); + } + + PowerClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * SidebarClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void SidebarClass::Decode_Pointers(void) +{ + for (int i = 0; i < COLUMNS; i++) { + Column[i].Decode_Pointers(); + } + + PowerClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Code_Pointers(void) +{ +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Decode_Pointers(void) +{ +} + + +/*********************************************************************************************** + * RadarClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void RadarClass::Code_Pointers(void) +{ + DisplayClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * RadarClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void RadarClass::Decode_Pointers(void) +{ + DisplayClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * DisplayClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void DisplayClass::Code_Pointers(void) +{ + /* + ** Code PendingObjectPtr. + */ + if (PendingObjectPtr) { + PendingObjectPtr = (ObjectClass *)PendingObjectPtr->As_Target(); + } + + /* + ** Chain to parent. + */ + MapClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * DisplayClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void DisplayClass::Decode_Pointers(void) +{ + /* + ** Decode PendingObjectPtr. We can't decode PendingObject here, because we'd + ** have to reference PendingObjectPtr->Class_Of(), and the object that + ** PendingObjectPtr is pointing to hasn't been decoded yet. Since we can't + ** decode PendingObjectPtr, we can't set the placement cursor shape here + ** either. These have to be done as last-minute fixups. + */ + if (PendingObjectPtr) { + PendingObjectPtr = As_Object((TARGET)PendingObjectPtr); + Check_Ptr((void *)PendingObjectPtr,__FILE__,__LINE__); + } + + /* + ** Chain to parent. + */ + MapClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * MapClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void MapClass::Code_Pointers(void) +{ + CELL cell; + + /* + ------------------------- Code the cell pointers ------------------------- + */ + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + (*this)[cell].Code_Pointers(); + } + + GScreenClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * MapClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void MapClass::Decode_Pointers(void) +{ + CELL cell; + + /* + ------------------------ Decode the cell pointers ------------------------ + */ + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + (*this)[cell].Decode_Pointers(); + } + + GScreenClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * GScreenClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void GScreenClass::Code_Pointers(void) +{ +} + + +/*********************************************************************************************** + * GScreenClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void GScreenClass::Decode_Pointers(void) +{ +} diff --git a/IOOBJ.CPP b/IOOBJ.CPP new file mode 100644 index 0000000..5062600 --- /dev/null +++ b/IOOBJ.CPP @@ -0,0 +1,2637 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\ioobj.cpv 2.18 16 Oct 1995 16:51:22 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IOOBJ.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : January 16, 1995 * + * * + * Last Update : January 16, 1995 [BR] * + * * + *---------------------------------------------------------------------------------------------* + * All object-related loading/saving routines should go in this module, so it can be overlayed.* + *---------------------------------------------------------------------------------------------* + * Functions: * + * TeamTypeClass::Load -- Reads from a save game file. * + * TeamTypeClass::Save -- Write to a save game file. * + * TeamTypeClass::Code_Pointers -- codes class's pointers for load/save * + * TeamTypeClass::Decode_Pointers -- decodes pointers for load/save * + * TeamClass::Load -- Reads from a save game file. * + * TeamClass::Save -- Write to a save game file. * + * TeamClass::Code_Pointers -- codes class's pointers for load/save * + * TeamClass::Decode_Pointers -- decodes pointers for load/save * + * TriggerClass::Load -- Reads from a save game file. * + * TriggerClass::Save -- Write to a save game file. * + * TriggerClass::Code_Pointers -- codes class's pointers for load/save * + * TriggerClass::Decode_Pointers -- decodes pointers for load/save * + * AircraftClass::Load -- Reads from a save game file. * + * AircraftClass::Save -- Write to a save game file. * + * AircraftClass::Code_Pointers -- codes class's pointers for load/save * + * AircraftClass::Decode_Pointers -- decodes pointers for load/save * + * AnimClass::Load -- Reads from a save game file. * + * AnimClass::Save -- Write to a save game file. * + * AnimClass::Code_Pointers -- codes class's pointers for load/save * + * AnimClass::Decode_Pointers -- decodes pointers for load/save * + * BuildingClass::Load -- Reads from a save game file. * + * BuildingClass::Save -- Write to a save game file. * + * BuildingClass::Code_Pointers -- codes class's pointers for load/save * + * BuildingClass::Decode_Pointers -- decodes pointers for load/save * + * BulletClass::Load -- Reads from a save game file. * + * BulletClass::Save -- Write to a save game file. * + * BulletClass::Code_Pointers -- codes class's pointers for load/save * + * BulletClass::Decode_Pointers -- decodes pointers for load/save * + * InfantryClass::Load -- Reads from a save game file. * + * InfantryClass::Save -- Write to a save game file. * + * InfantryClass::Code_Pointers -- codes class's pointers for load/save * + * InfantryClass::Decode_Pointers -- decodes pointers for load/save * + * OverlayClass::Load -- Reads from a save game file. * + * OverlayClass::Save -- Write to a save game file. * + * OverlayClass::Code_Pointers -- codes class's pointers for load/save * + * OverlayClass::Decode_Pointers -- decodes pointers for load/save * + * ReinforcementClass::Load -- Reads from a save game file. * + * ReinforcementClass::Save -- Write to a save game file. * + * ReinforcementClass::Code_Pointers -- codes class's pointers for load/save * + * ReinforcementClass::Decode_Pointers -- decodes pointers for load/save * + * SmudgeClass::Load -- Reads from a save game file. * + * SmudgeClass::Save -- Write to a save game file. * + * SmudgeClass::Code_Pointers -- codes class's pointers for load/save * + * SmudgeClass::Decode_Pointers -- decodes pointers for load/save * + * TemplateClass::Load -- Reads from a save game file. * + * TemplateClass::Save -- Write to a save game file. * + * TemplateClass::Code_Pointers -- codes class's pointers for load/save * + * TemplateClass::Decode_Pointers -- decodes pointers for load/save * + * TerrainClass::Load -- Reads from a save game file. * + * TerrainClass::Save -- Write to a save game file. * + * TerrainClass::Code_Pointers -- codes class's pointers for load/save * + * TerrainClass::Decode_Pointers -- decodes pointers for load/save * + * UnitClass::Load -- Reads from a save game file. * + * UnitClass::Save -- Write to a save game file. * + * UnitClass::Code_Pointers -- codes class's pointers for load/save * + * UnitClass::Decode_Pointers -- decodes pointers for load/save * + * FactoryClass::Load -- Reads from a save game file. * + * FactoryClass::Save -- Write to a save game file. * + * FactoryClass::Code_Pointers -- codes class's pointers for load/save * + * FactoryClass::Decode_Pointers -- decodes pointers for load/save * + * LayerClass::Load -- Reads from a save game file. * + * LayerClass::Save -- Write to a save game file. * + * LayerClass::Code_Pointers -- codes class's pointers for load/save * + * LayerClass::Decode_Pointers -- decodes pointers for load/save * + * HouseClass::Load -- Reads from a save game file. * + * HouseClass::Save -- Write to a save game file. * + * HouseClass::Code_Pointers -- codes class's pointers for load/save * + * HouseClass::Decode_Pointers -- decodes pointers for load/save * + * ScoreClass::Load -- Reads from a save game file. * + * ScoreClass::Save -- Write to a save game file. * + * ScoreClass::Code_Pointers -- codes class's pointers for load/save * + * ScoreClass::Decode_Pointers -- decodes pointers for load/save * + * FlyClass::Code_Pointers -- codes class's pointers for load/save * + * FlyClass::Decode_Pointers -- decodes pointers for load/save * + * FuseClass::Code_Pointers -- codes class's pointers for load/save * + * FuseClass::Decode_Pointers -- decodes pointers for load/save * + * TarComClass::Code_Pointers -- codes class's pointers for load/save * + * TarComClass::Decode_Pointers -- decodes pointers for load/save * + * TurretClass::Code_Pointers -- codes class's pointers for load/save * + * TurretClass::Decode_Pointers -- decodes pointers for load/save * + * DriveClass::Code_Pointers -- codes class's pointers for load/save * + * DriveClass::Decode_Pointers -- decodes pointers for load/save * + * FootClass::Code_Pointers -- codes class's pointers for load/save * + * FootClass::Decode_Pointers -- decodes pointers for load/save * + * RadioClass::Code_Pointers -- codes class's pointers for load/save * + * RadioClass::Decode_Pointers -- decodes pointers for load/save * + * TechnoClass::Code_Pointers -- codes class's pointers for load/save * + * TechnoClass::Decode_Pointers -- decodes pointers for load/save * + * FlasherClass::Code_Pointers -- codes class's pointers for load/save * + * FlasherClass::Decode_Pointers -- decodes pointers for load/save * + * CargoClass::Code_Pointers -- codes class's pointers for load/save * + * CargoClass::Decode_Pointers -- decodes pointers for load/save * + * MissionClass::Code_Pointers -- codes class's pointers for load/save * + * MissionClass::Decode_Pointers -- decodes pointers for load/save * + * ObjectClass::Code_Pointers -- codes class's pointers for load/save * + * ObjectClass::Decode_Pointers -- decodes pointers for load/save * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * TeamTypeClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool TeamTypeClass::Load(FileClass & file) +{ + return(Read_Object(this, sizeof(AbstractTypeClass), sizeof(*this), file, VTable)); +} + + +/*********************************************************************************************** + * TeamTypeClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool TeamTypeClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * TeamTypeClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TeamTypeClass::Code_Pointers(void) +{ + /* + -------------------------- Code the Class array -------------------------- + */ + for (int i = 0; i < ClassCount; i++) { + Class[i] = (TechnoTypeClass *)TechnoType_To_Target(Class[i]); + } +} + + +/*********************************************************************************************** + * TeamTypeClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TeamTypeClass::Decode_Pointers(void) +{ + /* + ------------------------- Decode the Class array ------------------------- + */ + for (int i = 0; i < ClassCount; i++) { + Class[i] = Target_To_TechnoType((TARGET)Class[i]); + Check_Ptr((void *)Class[i],__FILE__,__LINE__); + } +} + + +/*********************************************************************************************** + * TeamClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool TeamClass::Load(FileClass & file) +{ + return(Read_Object(this, sizeof(AbstractClass), sizeof(*this), file, VTable)); +} + + +/*********************************************************************************************** + * TeamClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool TeamClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * TeamClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TeamClass::Code_Pointers(void) +{ + TeamTypeClass const * cls; + + /* + -------------------- Code Class & House for this team -------------------- + */ + cls = Class; + ((TeamTypeClass *&)Class) = (TeamTypeClass *)cls->As_Target(); + ((HouseClass *&)House) = (HouseClass *)House->Class->House; + + /* + --------------------------- Code the 'Member' ---------------------------- + */ + if (Member) { + Member = (FootClass *)Member->As_Target(); + } +} + + +/*********************************************************************************************** + * TeamClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TeamClass::Decode_Pointers(void) +{ + /* + ------------------- Decode Class & House for this team ------------------- + */ + ((TeamTypeClass *&)Class) = As_TeamType((TARGET)Class); + Check_Ptr((void *)Class,__FILE__,__LINE__); + ((HouseClass *&)House) = HouseClass::As_Pointer((HousesType)House); + Check_Ptr((void *)House,__FILE__,__LINE__); + + /* + -------------------------- Decode the 'Member' --------------------------- + */ + if (Member) { + switch (Target_Kind((TARGET)Member)) { + case KIND_INFANTRY: + Member = As_Infantry((TARGET)Member); + break; + + case KIND_UNIT: + Member = As_Unit((TARGET)Member); + break; + + case KIND_AIRCRAFT: + Member = As_Aircraft((TARGET)Member); + break; + + default: + Member = 0; + break; + } + + Check_Ptr((void *)Member,__FILE__,__LINE__); + } +} + + +/*********************************************************************************************** + * TriggerClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool TriggerClass::Load(FileClass & file) +{ + int rc = Read_Object(this, sizeof(*this), sizeof(*this), file, 0); + + /* + -------------------------- Add to HouseTriggers -------------------------- + */ + if (rc) { + if (House != HOUSE_NONE) { + HouseTriggers[House].Add(this); + } + } + + return(rc); +} + + +/*********************************************************************************************** + * TriggerClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool TriggerClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * TriggerClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TriggerClass::Code_Pointers(void) +{ + if (Team) { + Team = (TeamTypeClass *)Team->As_Target(); + } +} + + +/*********************************************************************************************** + * TriggerClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TriggerClass::Decode_Pointers(void) +{ + if (Team) { + Team = As_TeamType((TARGET)Team); + Check_Ptr((void *)Team,__FILE__,__LINE__); + } +} + + +/*********************************************************************************************** + * AircraftClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Load(FileClass & file) +{ + return(Read_Object(this, sizeof(AbstractClass), sizeof(*this), file, VTable)); +} + + +/*********************************************************************************************** + * AircraftClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * AircraftClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void AircraftClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'Class' ------------------------------ + */ + ((AircraftTypeClass *&)Class) = (AircraftTypeClass *)Class->Type; + + /* + ---------------------------- Chain to parent ----------------------------- + */ + FootClass::Code_Pointers(); + FlyClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * AircraftClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void AircraftClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'Class' ----------------------------- + */ + ((AircraftTypeClass const *&)Class) = &AircraftTypeClass::As_Reference((AircraftType)Class); + Check_Ptr((void *)Class,__FILE__,__LINE__); + + /* + ---------------------------- Chain to parent ----------------------------- + */ + FootClass::Decode_Pointers(); + FlyClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * AnimClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool AnimClass::Load(FileClass & file) +{ + return(Read_Object(this, sizeof(AbstractClass), sizeof(*this), file, VTable)); +} + + +/*********************************************************************************************** + * AnimClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool AnimClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * AnimClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void AnimClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'Class' ------------------------------ + */ + ((AnimTypeClass *&)Class) = (AnimTypeClass *)Class->Type; + + /* + ----------------------------- Code 'Object' ------------------------------ + */ + if (Object) { + Object = (ObjectClass *)Object->As_Target(); + } + + /* + ---------------------------- Chain to parent ----------------------------- + */ + ObjectClass::Code_Pointers(); + StageClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * AnimClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void AnimClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'Class' ----------------------------- + */ + ((AnimTypeClass const *&)Class) = &AnimTypeClass::As_Reference((AnimType)Class); + Check_Ptr((void *)Class,__FILE__,__LINE__); + + /* + ---------------------------- Decode 'Object' ----------------------------- + */ + if (Object) { + Object = As_Object((TARGET)Object); + Check_Ptr((void *)Object,__FILE__,__LINE__); + } + + /* + ---------------------------- Chain to parent ----------------------------- + */ + ObjectClass::Decode_Pointers(); + StageClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * BuildingClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool BuildingClass::Load(FileClass & file) +{ + return(Read_Object(this, sizeof(AbstractClass), sizeof(*this), file, VTable)); +} + + +/*********************************************************************************************** + * BuildingClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool BuildingClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * BuildingClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void BuildingClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'Class' ------------------------------ + */ + ((BuildingTypeClass const *&)Class) = (BuildingTypeClass *)Class->Type; + + /*------------------------------------------------------------------------ + Code the Factory value; there's not target conversion routine for factories, + so just use its Array ID, plus 1 so it doesn't look like a NULL value when + it's converted back + ------------------------------------------------------------------------*/ + if (Factory) { + Factory = (FactoryClass *)(Factories.ID(Factory) + 1); + } + + /* + ---------------------------- Chain to parent ----------------------------- + */ + TechnoClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * BuildingClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void BuildingClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'Class' ----------------------------- + */ + ((BuildingTypeClass const *&)Class) = &BuildingTypeClass::As_Reference((StructType)Class); + Check_Ptr((void *)Class,__FILE__,__LINE__); + + /*------------------------------------------------------------------------ + Decode the Factory value, subtracting off the '1' we added when coding it + ------------------------------------------------------------------------*/ + if (Factory) { + Factory = Factories.Raw_Ptr((int)Factory - 1); + Check_Ptr((void *)Factory,__FILE__,__LINE__); + } + + /* + ---------------------------- Chain to parent ----------------------------- + */ + TechnoClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * BulletClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool BulletClass::Load(FileClass & file) +{ + return(Read_Object(this, sizeof(AbstractClass), sizeof(*this), file, VTable)); +} + + +/*********************************************************************************************** + * BulletClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool BulletClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * BulletClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void BulletClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'Class' ------------------------------ + */ + ((BulletTypeClass *&)Class) = (BulletTypeClass *)Class->Type; + + /* + ----------------------------- Code 'Payback' ----------------------------- + */ + if (Payback) + Payback = (TechnoClass *)Payback->As_Target(); + + /* + ---------------------------- Chain to parent ----------------------------- + */ + ObjectClass::Code_Pointers(); + FlyClass::Code_Pointers(); + FuseClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * BulletClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void BulletClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'Class' ----------------------------- + */ + ((BulletTypeClass const *&)Class) = &BulletTypeClass::As_Reference((BulletType)Class); + Check_Ptr((void *)Class,__FILE__,__LINE__); + + /* + ---------------------------- Decode 'Payback' ---------------------------- + */ + if (Payback) { + Payback = As_Techno((TARGET)Payback); + Check_Ptr((void *)Payback,__FILE__,__LINE__); + } + + /* + ---------------------------- Chain to parent ----------------------------- + */ + ObjectClass::Decode_Pointers(); + FlyClass::Decode_Pointers(); + FuseClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * InfantryClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Load(FileClass & file) +{ + return(Read_Object(this, sizeof(AbstractClass), sizeof(*this), file, VTable)); +} + + +/*********************************************************************************************** + * InfantryClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * InfantryClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void InfantryClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'Class' ------------------------------ + */ + ((InfantryTypeClass *&)Class) = (InfantryTypeClass *)Class->Type; + + /* + ---------------------------- Chain to parent ----------------------------- + */ + FootClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * InfantryClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void InfantryClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'Class' ----------------------------- + */ + ((InfantryTypeClass const *&)Class) = &InfantryTypeClass::As_Reference((InfantryType)Class); + Check_Ptr((void *)Class,__FILE__,__LINE__); + + /* + ---------------------------- Chain to parent ----------------------------- + */ + FootClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * OverlayClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool OverlayClass::Load(FileClass & file) +{ + return(Read_Object(this, sizeof(AbstractClass), sizeof(*this), file, VTable)); +} + + +/*********************************************************************************************** + * OverlayClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool OverlayClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * OverlayClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void OverlayClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'Class' ------------------------------ + */ + ((OverlayTypeClass *&)Class) = (OverlayTypeClass *)Class->Type; + + /* + ---------------------------- Chain to parent ----------------------------- + */ + ObjectClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * OverlayClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void OverlayClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'Class' ----------------------------- + */ + ((OverlayTypeClass const *&)Class) = &OverlayTypeClass::As_Reference((OverlayType)Class); + Check_Ptr((void *)Class,__FILE__,__LINE__); + + /* + ---------------------------- Chain to parent ----------------------------- + */ + ObjectClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * SmudgeClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool SmudgeClass::Load(FileClass & file) +{ + return(Read_Object(this, sizeof(AbstractClass), sizeof(*this), file, VTable)); +} + + +/*********************************************************************************************** + * SmudgeClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool SmudgeClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * SmudgeClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void SmudgeClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'Class' ------------------------------ + */ + ((SmudgeTypeClass const *&)Class) = (SmudgeTypeClass *)Class->Type; + + /* + ---------------------------- Chain to parent ----------------------------- + */ + ObjectClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * SmudgeClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void SmudgeClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'Class' ----------------------------- + */ + ((SmudgeTypeClass const *&)Class) = &SmudgeTypeClass::As_Reference((SmudgeType)Class); + Check_Ptr((void *)Class,__FILE__,__LINE__); + + /* + ---------------------------- Chain to parent ----------------------------- + */ + ObjectClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * TemplateClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool TemplateClass::Load(FileClass & file) +{ + return(Read_Object(this, sizeof(AbstractClass), sizeof(*this), file, VTable)); +} + + +/*********************************************************************************************** + * TemplateClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool TemplateClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * TemplateClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TemplateClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'Class' ------------------------------ + */ + ((TemplateTypeClass *&)Class) = (TemplateTypeClass *)Class->Type; + + /* + ---------------------------- Chain to parent ----------------------------- + */ + ObjectClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * TemplateClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TemplateClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'Class' ----------------------------- + */ + ((TemplateTypeClass const *&)Class) = &TemplateTypeClass::As_Reference((TemplateType)Class); + Check_Ptr((void *)Class,__FILE__,__LINE__); + + /* + ---------------------------- Chain to parent ----------------------------- + */ + ObjectClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * TerrainClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool TerrainClass::Load(FileClass & file) +{ + return(Read_Object(this, sizeof(AbstractClass), sizeof(*this), file, VTable)); +} + + +/*********************************************************************************************** + * TerrainClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool TerrainClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * TerrainClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TerrainClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'Class' ------------------------------ + */ + ((TerrainTypeClass *&)Class) = (TerrainTypeClass *)Class->Type; + + /* + ---------------------------- Chain to parent ----------------------------- + */ + ObjectClass::Code_Pointers(); + StageClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * TerrainClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TerrainClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'Class' ----------------------------- + */ + ((TerrainTypeClass const *&)Class) = &TerrainTypeClass::As_Reference((TerrainType)Class); + Check_Ptr((void *)Class,__FILE__,__LINE__); + + /* + ---------------------------- Chain to parent ----------------------------- + */ + ObjectClass::Decode_Pointers(); + StageClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * UnitClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Load(FileClass & file) +{ + return(Read_Object(this, sizeof(AbstractClass), sizeof(*this), file, VTable)); +} + + +/*********************************************************************************************** + * UnitClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * UnitClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void UnitClass::Code_Pointers(void) +{ + TarComClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * UnitClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void UnitClass::Decode_Pointers(void) +{ + TarComClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * FactoryClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Load(FileClass & file) +{ + return(Read_Object(this, sizeof(StageClass), sizeof(*this), file, 0)); +} + + +/*********************************************************************************************** + * FactoryClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * FactoryClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FactoryClass::Code_Pointers(void) +{ + if (Object) { + Object = (TechnoClass *)Object->As_Target(); + } + + ((HouseClass *&)House) = (HouseClass *)House->Class->House; + + StageClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * FactoryClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FactoryClass::Decode_Pointers(void) +{ + if (Object) { + Object = As_Techno((TARGET)Object); + Check_Ptr((void *)Object,__FILE__,__LINE__); + } + + ((HouseClass *&)House) = HouseClass::As_Pointer((HousesType)House); + Check_Ptr((void *)House,__FILE__,__LINE__); + + StageClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * LayerClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool LayerClass::Load(FileClass & file) +{ + int count; + int i; + ObjectClass * ptr; + + /* + ---------------------- Read # elements in the layer ---------------------- + */ + if (file.Read(&count,sizeof(count)) != sizeof(count)) { + return(false); + } + + /* + ---------------------------- Clear the array ----------------------------- + */ + Clear(); + + /* + ----------------------- Read in all array elements ----------------------- + */ + for (i = 0; i < count; i++) { + if (file.Read(&ptr, sizeof(ObjectClass *)) != sizeof(ObjectClass *)) { + return(false); + } + Add(ptr); + } + + return(true); +} + + +/*********************************************************************************************** + * LayerClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool LayerClass::Save(FileClass & file) +{ + int count; + int i; + ObjectClass * ptr; + + /* + ------------------------- Save # array elements -------------------------- + */ + count = Count(); + if (file.Write(&count, sizeof(count)) != sizeof(count)) + return(false); + + /* + --------------------------- Save all elements ---------------------------- + */ + for (i = 0; i < count; i++) { + ptr = (*this)[i]; + if (file.Write(&ptr, sizeof(ObjectClass *)) != sizeof(ObjectClass *)) + return(false); + } + + return(true); +} + + +/*********************************************************************************************** + * LayerClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void LayerClass::Code_Pointers(void) +{ + ObjectClass *obj; + + for (int i = 0; i < Count(); i++) { + obj = (*this)[i]; + (*this)[i] = (ObjectClass *)(obj->As_Target()); + } +} + + +/*********************************************************************************************** + * LayerClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void LayerClass::Decode_Pointers(void) +{ + TARGET target; + + for (int i = 0; i < Count(); i++) { + target = (TARGET)(*this)[i]; + (*this)[i] = (ObjectClass *)As_Object(target); + Check_Ptr((*this)[i],__FILE__,__LINE__); + } +} + + +/*********************************************************************************************** + * HouseClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Load(FileClass & file) +{ + return(Read_Object(this, sizeof(*this), sizeof(*this), file, 0)); +} + + +/*********************************************************************************************** + * HouseClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * HouseClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void HouseClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'Class' ------------------------------ + */ + ((HouseTypeClass const *&)Class) = (HouseTypeClass const *)Class->House; +} + + +/*********************************************************************************************** + * HouseClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void HouseClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'Class' ----------------------------- + */ + ((HouseTypeClass const *&)Class) = &HouseTypeClass::As_Reference((HousesType)Class); + Check_Ptr((void *)Class,__FILE__,__LINE__); +} + + +/*********************************************************************************************** + * ScoreClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool ScoreClass::Load(FileClass & file) +{ + return(Read_Object(this, sizeof(*this), sizeof(*this), file, 0)); +} + + +/*********************************************************************************************** + * ScoreClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool ScoreClass::Save(FileClass & file) +{ + return(Write_Object(this, sizeof(*this), file)); +} + + +/*********************************************************************************************** + * ScoreClass::Code_Pointers -- codes class's pointers for load/save * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void ScoreClass::Code_Pointers(void) +{ +} + + +/*********************************************************************************************** + * ScoreClass::Decode_Pointers -- decodes pointers for load/save * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void ScoreClass::Decode_Pointers(void) +{ +} + + +/*********************************************************************************************** + * FlyClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FlyClass::Code_Pointers(void) +{ +} + + +/*********************************************************************************************** + * FlyClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FlyClass::Decode_Pointers(void) +{ +} + + +/*********************************************************************************************** + * FuseClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FuseClass::Code_Pointers(void) +{ +} + + +/*********************************************************************************************** + * FuseClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FuseClass::Decode_Pointers(void) +{ +} + + +/*********************************************************************************************** + * TarComClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TarComClass::Code_Pointers(void) +{ + TurretClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * TarComClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TarComClass::Decode_Pointers(void) +{ + TurretClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * TurretClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TurretClass::Code_Pointers(void) +{ + DriveClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * TurretClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TurretClass::Decode_Pointers(void) +{ + DriveClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * DriveClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void DriveClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'Class' ------------------------------ + */ + ((UnitTypeClass *&)Class) = (UnitTypeClass *)Class->Type; + + /* + ---------------------------- Chain to parent ----------------------------- + */ + FootClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * DriveClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void DriveClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'Class' ----------------------------- + */ + ((UnitTypeClass const *&)Class) = &UnitTypeClass::As_Reference((UnitType)Class); + Check_Ptr((void *)Class,__FILE__,__LINE__); + + /* + ---------------------------- Chain to parent ----------------------------- + */ + FootClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * FootClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FootClass::Code_Pointers(void) +{ + if (Team) + Team = (TeamClass *)Team->As_Target(); + + if (Member) { + Member = (FootClass *)Member->As_Target(); + } + + TechnoClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * FootClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FootClass::Decode_Pointers(void) +{ + if (Team) { + Team = As_Team((TARGET)Team); + Check_Ptr((void *)Team,__FILE__,__LINE__); + } + + if (Member) { + Member = (FootClass *)As_Techno((TARGET)Member); + Check_Ptr((void *)Member,__FILE__,__LINE__); + } + + TechnoClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * RadioClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void RadioClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'Radio' ------------------------------ + */ + if (Radio) { + Radio = (RadioClass *)Radio->As_Target(); + } + + MissionClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * RadioClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void RadioClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'Radio' ----------------------------- + */ + if (Radio) { + Radio = As_Techno((TARGET)Radio); + Check_Ptr((void *)Radio,__FILE__,__LINE__); + } + + MissionClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * TechnoClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TechnoClass::Code_Pointers(void) +{ + /* + ------------------------------ Code 'House' ------------------------------ + */ + ((HouseClass *&)House) = (HouseClass *)(House->Class->House); + + FlasherClass::Code_Pointers(); + StageClass::Code_Pointers(); + CargoClass::Code_Pointers(); + DoorClass::Code_Pointers(); + + RadioClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * TechnoClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TechnoClass::Decode_Pointers(void) +{ + /* + ----------------------------- Decode 'House' ----------------------------- + */ + ((HouseClass *&)House) = HouseClass::As_Pointer((HousesType)House); + Check_Ptr((void *)House,__FILE__,__LINE__); + + FlasherClass::Decode_Pointers(); + StageClass::Decode_Pointers(); + CargoClass::Decode_Pointers(); + DoorClass::Decode_Pointers(); + + RadioClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * FlasherClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FlasherClass::Code_Pointers(void) +{ +} + + +/*********************************************************************************************** + * FlasherClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FlasherClass::Decode_Pointers(void) +{ +} + + +/*********************************************************************************************** + * CargoClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void CargoClass::Code_Pointers(void) +{ + /* + ---------------------------- Code 'CargoHold' ---------------------------- + */ + if (CargoHold) { + CargoHold = (FootClass *)CargoHold->As_Target(); + } +} + + +/*********************************************************************************************** + * CargoClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void CargoClass::Decode_Pointers(void) +{ + /* + --------------------------- Decode 'CargoHold' --------------------------- + */ + if (CargoHold) { + CargoHold = (FootClass *)As_Techno((TARGET)CargoHold); + Check_Ptr((void *)CargoHold,__FILE__,__LINE__); + } +} + + +/*********************************************************************************************** + * MissionClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void MissionClass::Code_Pointers(void) +{ + ObjectClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * MissionClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void MissionClass::Decode_Pointers(void) +{ + ObjectClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * ObjectClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void ObjectClass::Code_Pointers(void) +{ + if (Next) { + Next = (ObjectClass *)Next->As_Target(); + } + + if (Trigger) { + Trigger = (TriggerClass *)Trigger->As_Target(); + } +} + + +/*********************************************************************************************** + * ObjectClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void ObjectClass::Decode_Pointers(void) +{ + if (Next) { + Next = As_Object((TARGET)Next); + Check_Ptr((void *)Next,__FILE__,__LINE__); + } + + if (Trigger) { + Trigger = As_Trigger((TARGET)Trigger); + Check_Ptr((void *)Trigger,__FILE__,__LINE__); + } +} + diff --git a/IPX.CPP b/IPX.CPP new file mode 100644 index 0000000..6ab3d2c --- /dev/null +++ b/IPX.CPP @@ -0,0 +1,1111 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\ipx.cpv 2.17 16 Oct 1995 16:49:34 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPX.CPP * + * * + * Programmer : Barry Nance * + * from Client/Server LAN Programming * + * Westwood-ized by Bill Randolph * + * * + * Start Date : December 14, 1994 * + * * + * Last Update : December 15, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Pitfalls: * + * - Never try to use a closed socket; always check the return code from * + * IPX_Open_Socket(). * + * - Always give IPX an outstanding ECB for listening, before you send. * + * - It turns out that IPX is pretty bad about saving registers, so if * + * you have any register variables in your program, they may get * + * trashed. To circumvent this, all functions in this module save & * + * restore the registers before invoking any IPX or NETX function. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * IPX_SPX_Installed -- checks for installation of IPX/SPX * + * IPX_Open_Socket -- opens an IPX socket for sending or receiving * + * IPX_Close_Socket -- closes an open socket * + * IPX_Get_Connection_Number -- gets local Connection Number * + * IPX_Get_1st_Connection_Num -- gets 1st Connect Number for given user * + * IPX_Get_Internet_Address -- gets Network Number & Node Address * + * IPX_Get_User_ID -- gets user ID from Connection Number * + * IPX_Listen_For_Packet -- commands IPX to listen for a packet * + * IPX_Send_Packet -- commands IPX to send a packet * + * IPX_Get_Local_Target -- fills in ImmediateAddress field of ECB * + * IPX_Cancel_Event -- cancels an operation in progress * + * Let_IPX_Breath -- gives IPX some CPU time * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "ipx95.h" + + +/*************************************************************************** + * IPX_SPX_Installed -- checks for installation of IPX/SPX * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = not installed; 1 = IPX only, 2 = IPX and SPX are installed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/14/1994 BR : Created. * + *=========================================================================*/ +int IPX_SPX_Installed(void) +{ + +#ifndef NOT_FOR_WIN95 + + return (IPX_Initialise()); + +#else //NOT_FOR_WIN95 + + union REGS regs; + struct SREGS sregs; + RMIType rmi; + + /*------------------------------------------------------------------------ + Init all registers to 0's + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + + /*------------------------------------------------------------------------ + Fill in registers for the DPMI call, function 0x300 + ------------------------------------------------------------------------*/ + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = 0x002f; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + /*------------------------------------------------------------------------ + Fill in registers for the real-mode interrupt handler. + To test for the presence of IPX, set AH to 0x7a, AL to 0, and invoke + interrupt 0x2f (the "multiplex" interrupt). If IPX is installed, + AL will be 0xff, and ES:DI will contain the IPX/SPX function address. + ------------------------------------------------------------------------*/ + rmi.eax = 0x00007a00; + + /*------------------------------------------------------------------------ + call DPMI + ------------------------------------------------------------------------*/ + int386x(DPMI_INT, ®s, ®s, &sregs); + + /*------------------------------------------------------------------------ + If IPX isn't there, return 0 + ------------------------------------------------------------------------*/ + if ( (rmi.eax & 0x00ff) != 0xff) { + return(0); + } + + /*------------------------------------------------------------------------ + Test for SPX by invoking the IPX_SPX function with BX = 0x10, and AL = 0. + If SPX is present, AL will be 0xff. + ------------------------------------------------------------------------*/ + /*........................................................................ + Fill in registers for the DPMI call + ........................................................................*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + /*........................................................................ + Fill in registers for the interrupt call + ........................................................................*/ + rmi.ebx = 0x00000010; + /*........................................................................ + call DPMI + ........................................................................*/ + int386x(DPMI_INT, ®s, ®s, &sregs); + + /*------------------------------------------------------------------------ + SPX is installed; return '2' + ------------------------------------------------------------------------*/ + if ( (rmi.eax & 0x00ff) == 0xff) { + return(2); + } + + /*------------------------------------------------------------------------ + SPX is not installed; return '1' + ------------------------------------------------------------------------*/ + return(1); +#endif //NOT_FOR_WIN95 +} /* end of IPX_SPX_Installed */ + + +/*************************************************************************** + * IPX_Open_Socket -- opens an IPX socket for sending or receiving * + * * + * INPUT: * + * socket the socket number to open * + * * + * OUTPUT: * + * 0 = OK * + * -1 = IPX not installed * + * 0xfe = socket table is full * + * 0xff = socket is already open * + * * + * WARNINGS: * + * The application must define its socket number carefully. Use * + * values from 0x4000 to 0x8000 for custom socket numbers. The app * + * must know its own socket number as well as the socket number of * + * a destination workstation. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +int IPX_Open_Socket(unsigned short socket) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + int rc; + + /*------------------------------------------------------------------------ + Open the socket: + DX = socket number + AL = 0 for short-lived socket, 0xff for long-lived socket + ------------------------------------------------------------------------*/ + /*........................................................................ + Fill in registers for the DPMI call + ........................................................................*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG (&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF (&rmi); + /*........................................................................ + Fill in registers for the interrupt call + ........................................................................*/ + rmi.ebx = IPX_OPEN_SOCKET; // function code + rmi.edx = socket; // desired socket # + rmi.eax = 0x00ff; // make this a long-lived socket + /*........................................................................ + call DPMI + ........................................................................*/ + int386x (DPMI_INT, ®s, ®s, &sregs); + + rc = (rmi.eax & 0xff); + + return(rc); + +} /* end of IPX_Open_Socket */ + +#endif +/*************************************************************************** + * IPX_Close_Socket -- closes an open socket * + * * + * INPUT: * + * socket socket number to close * + * * + * OUTPUT: * + * 0 = ok, -1 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +int IPX_Close_Socket(unsigned short socket) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + + /*------------------------------------------------------------------------ + Close the socket: + DX = socket number + ------------------------------------------------------------------------*/ + /*........................................................................ + Fill in registers for the DPMI call + ........................................................................*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + /*........................................................................ + Fill in registers for the interrupt call + ........................................................................*/ + rmi.ebx = IPX_CLOSE_SOCKET; + rmi.edx = socket; + /*........................................................................ + call DPMI + ........................................................................*/ + int386x(DPMI_INT, ®s, ®s, &sregs); + + return(0); + +} /* end of IPX_Close_Socket */ +#endif //NOT_FOR_WIN95 + +/*************************************************************************** + * IPX_Get_Connection_Number -- gets local Connection Number * + * * + * This Novell call will the return the user's local "Connection Number". * + * This value will be 0 if the user isn't logged into Novell, so this * + * routine can be used to detect if other calls (such as Get_Local_Target) * + * will be OK. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * Connection Number, 0 = none * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +int IPX_Get_Connection_Number(void) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + int num; + + /*------------------------------------------------------------------------ + Call Interrupt 0x21, with AH = 0xdc. This tells Novell to put the local + connection number into AL. + ------------------------------------------------------------------------*/ + /*........................................................................ + Fill in registers for the DPMI call + ........................................................................*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = 0x21; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + /*........................................................................ + Fill in registers for the interrupt call + ........................................................................*/ + rmi.eax = 0x0000dc00; + /*........................................................................ + call DPMI + ........................................................................*/ + int386x(DPMI_INT, ®s, ®s, &sregs); + + num = rmi.eax & 0x00ff; + + return(num); + +} /* end of IPX_Get_Connection_Number */ + +#endif //NOT_FOR_WIN95 + +/*************************************************************************** + * IPX_Get_1st_Connection_Num -- gets 1st Connect Number for given user * + * * + * This gets the Connection Number for the given User ID. Since a user * + * may be logged in more than once, this just returns the first connection * + * found and ignores the others. * + * * + * INPUT: * + * username name of the user to get the Connection Number for * + * * + * OUTPUT: * + * first-found Connection Number for that user, 0 if user not logged in * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +int IPX_Get_1st_Connection_Num (char *username) +{ + struct request_buffer { + unsigned short len; // username length + 5 + unsigned char buffer_type; // ConnectionNum = 0x15 + unsigned short object_type; // set ot 0x0100 + unsigned char name_len; // length of username + char name [48]; // copy of username + unsigned short reserved; + }; + struct reply_buffer { + unsigned short len; + unsigned char number_connections; // will be 0 - 100 + unsigned char connection_num [100]; // array of connection numbers + unsigned short reserved[2]; + }; + union REGS regs; + struct SREGS sregs; + RMIType rmi; + struct request_buffer *reqbuf; + struct reply_buffer *replybuf; + unsigned short segment; // for DOS allocation + unsigned short selector; // for DOS allocation + int num_conns; // # connections returned + int conn_num; // connection number + int rc; + + /*------------------------------------------------------------------------ + Allocate DOS memory to store the buffers passed to the interrupt + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call + regs.x.ebx = (sizeof(struct request_buffer) + // # paragraphs to allocate + sizeof(struct reply_buffer) + 15) >> 4; + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + /*........................................................................ + If the carry flag is set, DPMI is indicating an error. + ........................................................................*/ + if (regs.x.cflag) { + return(0); + } + + /*........................................................................ + Get pointers to allocated memory. + 'reqbuf' is just the returned real-mode segment, multiplied by 16. + 'replybuf' is an offset from 'reqbuf'. + ........................................................................*/ + segment = regs.w.ax; + selector = regs.w.dx; + reqbuf = (struct request_buffer *)(segment << 4); + replybuf = (struct reply_buffer *) + (((char *)reqbuf) + sizeof (struct request_buffer)); + + /*------------------------------------------------------------------------ + Init the contents of the request & reply buffers + ------------------------------------------------------------------------*/ + reqbuf->len = (unsigned short)(strlen(username) + 5); + reqbuf->buffer_type = 0x15; + reqbuf->object_type = 0x0100; + reqbuf->name_len = (unsigned char) strlen(username); + strcpy(reqbuf->name, username); + reqbuf->reserved = reqbuf->reserved; // prevent compiler warning + replybuf->len = 101; + replybuf->reserved[0] = replybuf->reserved[0]; // prevent compiler warning + replybuf->reserved[0] = replybuf->reserved[1]; // prevent compiler warning + + /*------------------------------------------------------------------------ + Invoke Int 21 with AH=0xe3, DS:SI=&request_buffer, ES:DI=&reply_buffer + ------------------------------------------------------------------------*/ + /*........................................................................ + Fill in registers for the DPMI call + ........................................................................*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = 0x21; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + /*........................................................................ + Fill in registers for the interrupt call + ........................................................................*/ + rmi.eax = 0x0000e300; + rmi.ds = segment; + rmi.esi = 0; + rmi.es = segment; + rmi.edi = sizeof(struct request_buffer); + /*........................................................................ + call DPMI + ........................................................................*/ + int386x(DPMI_INT, ®s, ®s, &sregs); + + /*------------------------------------------------------------------------ + Stash the 1st connection number + ------------------------------------------------------------------------*/ + rc = (rmi.eax & 0x00ff); // if AL !=0, error + num_conns = replybuf->number_connections; // # times user is logged in + conn_num = (int )replybuf->connection_num[0]; // 1st connection # + + /*------------------------------------------------------------------------ + Free DOS memory + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + /*------------------------------------------------------------------------ + Return error if function failed, or user not logged in + ------------------------------------------------------------------------*/ + if (rc != 0 || num_conns==0) { + return(0); + } else { + return(conn_num); + } +} +#endif //NOT_FOR_WIN95 + +/*************************************************************************** + * IPX_Get_Internet_Address -- gets Network Number & Node Address * + * * + * Once you've obtained a Connection Number from IPX_Get_Connection_Number * + * or IPX_Get_1st_Connection_Num, use this function to translate it into * + * a Network Number and Node Address; then, place those numbers in the * + * IPX header for outgoing packets. * + * * + * INPUT: * + * connection_number Connection Number to translate * + * network_number ptr: will hold Network Number * + * physical_node ptr: will hold Node Address * + * * + * OUTPUT: * + * 0 = OK, -1 = error * + * * + * WARNINGS: * + * If connection_number is 0 and NETX isn't running, this routine * + * will just put garbage into the network_number and physical_node. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +int IPX_Get_Internet_Address(int connection_number, + unsigned char *network_number, unsigned char *physical_node) +{ + struct request_buffer { + unsigned short len; + unsigned char buffer_type; // Internet = 0x13 + unsigned char connection_number; // Conn. Number to translate + }; + struct reply_buffer { + unsigned short len; + unsigned char network_number [4]; // filled in by IPX + unsigned char physical_node [6]; // filled in by IPX + unsigned short server_socket; // filled in by IPX, but don't use! + }; + union REGS regs; + struct SREGS sregs; + RMIType rmi; + struct request_buffer *reqbuf; + struct reply_buffer *replybuf; + unsigned short segment; // for DOS allocation + unsigned short selector; // for DOS allocation + + /*------------------------------------------------------------------------ + Error if invalid connection is given + ------------------------------------------------------------------------*/ + if (connection_number==0) + return(-1); + + /*------------------------------------------------------------------------ + Allocate DOS memory to store the buffers passed to the interrupt + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call + regs.x.ebx = (sizeof(struct request_buffer) + // # paragraphs to allocate + sizeof(struct reply_buffer) + 15) >> 4; + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + /*........................................................................ + If the carry flag is set, DPMI is indicating an error. + ........................................................................*/ + if (regs.x.cflag) { + return(-1); + } + + /*........................................................................ + Get pointers to allocated memory. + 'reqbuf' is just the returned real-mode segment, multiplied by 16. + 'replybuf' is an offset from 'reqbuf'. + ........................................................................*/ + segment = regs.w.ax; + selector = regs.w.dx; + reqbuf = (struct request_buffer *)(segment << 4); + replybuf = (struct reply_buffer *) + (((char *)reqbuf) + sizeof (struct request_buffer)); + + /*------------------------------------------------------------------------ + Init the contents of the request & reply buffers + ------------------------------------------------------------------------*/ + reqbuf->len = 2; + reqbuf->buffer_type = 0x13; + reqbuf->connection_number = (unsigned char)connection_number; + replybuf->len = 12; + replybuf->network_number[0] = replybuf->network_number[0]; // suppress warning + replybuf->physical_node[0] = replybuf->physical_node[0]; // suppress warning + replybuf->server_socket = replybuf->server_socket; // suppress warning + + /*------------------------------------------------------------------------ + Invoke Int 21 with AH=0xe3, DS:SI=&request_buffer, ES:DI=&reply_buffer + ------------------------------------------------------------------------*/ + /*........................................................................ + Fill in registers for the DPMI call + ........................................................................*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = 0x21; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + /*........................................................................ + Fill in registers for the interrupt call + ........................................................................*/ + rmi.eax = 0x0000e300; + rmi.ds = segment; + rmi.esi = 0; + rmi.es = segment; + rmi.edi = sizeof(struct request_buffer); + /*........................................................................ + call DPMI + ........................................................................*/ + int386x(DPMI_INT, ®s, ®s, &sregs); + + memcpy(network_number, replybuf->network_number, 4); + memcpy(physical_node, replybuf->physical_node, 6); + + /*------------------------------------------------------------------------ + Free DOS memory + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + return(0); + +} /* end of IPX_Get_Internet_Address */ + +#endif // NOT_FOR_WIN95 + +/*************************************************************************** + * IPX_Get_User_ID -- gets user ID from Connection Number * + * * + * INPUT: * + * connection_number Connection Number to get User ID for * + * user_id ptr to buffer to put User ID into; * + * size must be >= 48 chars * + * * + * OUTPUT: * + * 0 = OK, -1 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +int IPX_Get_User_ID(int connection_number, char *user_id) +{ + struct request_buffer { + unsigned short len; + unsigned char buffer_type; // 0x16 = UserID buffer type + unsigned char connection_number; // Connection Number to get ID for + }; + struct reply_buffer { + unsigned short len; + unsigned char object_id[4]; + unsigned char object_type[2]; + char object_name[48]; + char login_time[7]; + unsigned short reserved; + }; + union REGS regs; + struct SREGS sregs; + RMIType rmi; + struct request_buffer *reqbuf; + struct reply_buffer *replybuf; + unsigned short segment; // for DOS allocation + unsigned short selector; // for DOS allocation + + /*------------------------------------------------------------------------ + Error if invalid connection is given + ------------------------------------------------------------------------*/ + if (connection_number==0) + return(-1); + + /*------------------------------------------------------------------------ + Allocate DOS memory to store the buffers passed to the interrupt + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call + regs.x.ebx = (sizeof(struct request_buffer) + // # paragraphs to allocate + sizeof(struct reply_buffer) + 15) >> 4; + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + /*........................................................................ + If the carry flag is set, DPMI is indicating an error. + ........................................................................*/ + if (regs.x.cflag) { + return(-1); + } + + /*........................................................................ + Get pointers to allocated memory. + 'reqbuf' is just the returned real-mode segment, multiplied by 16. + 'replybuf' is an offset from 'reqbuf'. + ........................................................................*/ + segment = regs.w.ax; + selector = regs.w.dx; + reqbuf = (struct request_buffer *)(segment << 4); + replybuf = (struct reply_buffer *) + (((char *)reqbuf) + sizeof (struct request_buffer)); + + /*------------------------------------------------------------------------ + Init the contents of the request & reply buffers + ------------------------------------------------------------------------*/ + reqbuf->len = 2; + reqbuf->buffer_type = 0x16; + reqbuf->connection_number = (unsigned char)connection_number; + replybuf->len = sizeof(struct reply_buffer) - 2; + replybuf->object_id[0] = replybuf->object_id[0]; // suppress warnings + replybuf->object_type[0] = replybuf->object_type[0]; // suppress warnings + replybuf->login_time[0] = replybuf->login_time[0]; // suppress warnings + replybuf->reserved = replybuf->reserved; // suppress warnings + + /*------------------------------------------------------------------------ + Invoke Int 21 with AH=0xe3, DS:SI=&request_buffer, ES:DI=&reply_buffer + ------------------------------------------------------------------------*/ + /*........................................................................ + Fill in registers for the DPMI call + ........................................................................*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = 0x21; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + /*........................................................................ + Fill in registers for the interrupt call + ........................................................................*/ + rmi.eax = 0x0000e300; + rmi.ds = segment; + rmi.esi = 0; + rmi.es = segment; + rmi.edi = sizeof(struct request_buffer); + /*........................................................................ + call DPMI + ........................................................................*/ + int386x(DPMI_INT, ®s, ®s, &sregs); + + /*------------------------------------------------------------------------ + Fill in the caller's buffer with the user name + ------------------------------------------------------------------------*/ + strncpy(user_id, replybuf->object_name, 48); + + /*------------------------------------------------------------------------ + Free DOS memory + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + return(0); + +} /* end of IPX_Get_User_ID */ +#endif //NOT_FOR_WIN95 + + +/*************************************************************************** + * IPX_Listen_For_Packet -- commands IPX to listen for a packet * + * * + * Before calling this function, you must fill in an ECB: * + * SocketNumber: must contain the socket you've opened, * + * and are "listening" on * + * Event_Service_Routine: optionally points to a callback routine * + * PacketCount: set to 2, to tell IPX there are 2 areas to * + * store the incoming data in * + * Packet[0].Address: set to the address of an IPXHeaderType * + * Packet[0].Length: sizeof(IPXHeaderType) * + * Packet[1].Address: address of data buffer, for the packet * + * Packet[1].Length: size of the data buffer * + * * + * When the packet is received, ECBType.CompletionCode will be 0 if * + * successful. Otherwise, some error occurred. * + * * + * You should initialize the ECB to 0's before filling it in. * + * * + * INPUT: * + * ecb_ptr pointer to a filled-in ECB; MUST be real-mode memory * + * * + * OUTPUT: * + * 0 = OK, IPX error otherwise * + * * + * WARNINGS: * + * The ECB must be located in real-mode memory, as well as the values * + * pointed to in the ECB (the IPX Header, the buffer to send, etc.) * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +int IPX_Listen_For_Packet(struct ECB *ecb_ptr) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + + /*------------------------------------------------------------------------ + Call IPX with ES:SI=ecb_ptr + ------------------------------------------------------------------------*/ + /*........................................................................ + Fill in registers for the DPMI call + ........................................................................*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + /*........................................................................ + Fill in registers for the interrupt call + ........................................................................*/ + rmi.ebx = IPX_LISTEN_FOR_PACKET; + rmi.es = (short)((long)ecb_ptr >> 4); + rmi.esi = (long)((long)ecb_ptr & 0x000f); + /*........................................................................ + call DPMI + ........................................................................*/ + int386x(DPMI_INT, ®s, ®s, &sregs); + + return(rmi.eax & 0x00ff); + +} /* end of IPX_Listen_For_Packet */ +#endif //NOT_FOR_WIN95 + +/*************************************************************************** + * IPX_Send_Packet -- commands IPX to send a packet * + * * + * Before calling this function, you must fill in an ECB: * + * SocketNumber: must contain the socket you've opened, * + * and are sending on * + * Event_Service_Routine: optionally points to a callback routine * + * PacketCount: set to 2, to tell IPX there are 2 areas the * + * outgoing data is stored in * + * Packet[0].Address: set to the address of an IPXHeaderType * + * Packet[0].Length: sizeof(IPXHeaderType) * + * Packet[1].Address: address of buffer containing data to send * + * Packet[1].Length: size of the data buffer * + * ImmediateAddress: must be filled in with the node address of * + * the bridge that will route the message; * + * fill this in by calling IPX_Get_Local_Target * + * * + * Also, you must fill in the IPXHeaderType with certain values: * + * PacketType: set to 4 for IPX * + * DestNetworkNumber: Network Number of the destination system * + * DestNetworkNode: Node Address of the destination system * + * DestNetworkSocket: the destination system's socket to send to; * + * this doesn't have to be the same as the * + * socket opened on the local machine. * + * * + * You should initialize the ECB & IPXHeader to 0's before filling them in.* + * * + * INPUT: * + * ecb_ptr pointer to a filled-in ECB * + * * + * OUTPUT: * + * none. This function doesn't return anything; the caller must check * + * its ECB.CompletionCode for: 0 = OK, IPX Error otherwise. * + * * + * WARNINGS: * + * The ECB must be located in real-mode memory, as well as the values * + * pointed to in the ECB (the IPX Header, the buffer to send, etc.) * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +void IPX_Send_Packet(struct ECB *ecb_ptr) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + + /*------------------------------------------------------------------------ + Call IPX with ES:SI=ecb_ptr + ------------------------------------------------------------------------*/ + /*........................................................................ + Fill in registers for the DPMI call + ........................................................................*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + /*........................................................................ + Fill in registers for the interrupt call + ........................................................................*/ + rmi.ebx = IPX_SEND_PACKET; + rmi.es = (short)((long)ecb_ptr >> 4); + rmi.esi = (long)((long)ecb_ptr & 0x000f); + /*........................................................................ + call DPMI + ........................................................................*/ + int386x(DPMI_INT, ®s, ®s, &sregs); + +} /* end of IPX_Send_Packet */ +#endif //NOT_FOR_WIN95 + +/*************************************************************************** + * IPX_Get_Local_Target -- fills in ImmediateAddress field of ECB * + * * + * Use this function to fill in the ECB's ImmediateAddress field when * + * sending a packet. The Immediate Address is the node address of the * + * bridge that must route the message. If there is no bridge, it's * + * filled in with the destination Node Address. In either case, it * + * will contain the proper value for sending. * + * * + * INPUT: * + * dest_network destination Network Number * + * dest_node destination Node Address * + * dest_socket destination Socket Number * + * bridge_address field to fill in with Immediate Address * + * * + * OUTPUT: * + * 0 = OK, error otherwise * + * * + * WARNINGS: * + * If the Connection Number is 0 (user not logged in), dest_network * + * and dest_node will be garbage, and this routine will probably crash. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +int IPX_Get_Local_Target(unsigned char *dest_network, unsigned char *dest_node, + unsigned short dest_socket, unsigned char *bridge_address) +{ + struct request_buffer { + unsigned char network_number [4]; + unsigned char physical_node [6]; + unsigned short socket; + }; + struct reply_buffer { + unsigned char local_target [6]; + }; + unsigned int rc; + union REGS regs; + struct SREGS sregs; + RMIType rmi; + struct request_buffer *reqbuf; + struct reply_buffer *replybuf; + unsigned short segment; // for DOS allocation + unsigned short selector; // for DOS allocation + + /*------------------------------------------------------------------------ + Allocate DOS memory to store the buffers passed to the interrupt + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call + regs.x.ebx = (sizeof(struct request_buffer) + // # paragraphs to allocate + sizeof(struct reply_buffer) + 15) >> 4; + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + /*........................................................................ + If the carry flag is set, DPMI is indicating an error. + ........................................................................*/ + if (regs.x.cflag) { + return(-1); + } + + /*........................................................................ + Get pointers to allocated memory. + 'reqbuf' is just the returned real-mode segment, multiplied by 16. + 'replybuf' is an offset from 'reqbuf'. + ........................................................................*/ + segment = regs.w.ax; + selector = regs.w.dx; + reqbuf = (struct request_buffer *)(segment << 4); + replybuf = (struct reply_buffer *) + (((char *)reqbuf) + sizeof (struct request_buffer)); + + /*------------------------------------------------------------------------ + Init the contents of the request & reply buffers + ------------------------------------------------------------------------*/ + memcpy(reqbuf->network_number, dest_network, 4); + memcpy(reqbuf->physical_node, dest_node, 6); + reqbuf->socket = dest_socket; + + /*------------------------------------------------------------------------ + Invoke IPX with DS:SI=&request_buffer, ES:DI=&reply_buffer + ------------------------------------------------------------------------*/ + /*........................................................................ + Fill in registers for the DPMI call + ........................................................................*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + /*........................................................................ + Fill in registers for the interrupt call + ........................................................................*/ + rmi.ebx = IPX_GET_LOCAL_TARGET; + rmi.ds = segment; + rmi.esi = 0; + rmi.es = segment; + rmi.edi = sizeof(struct request_buffer); + /*........................................................................ + call DPMI + ........................................................................*/ + int386x(DPMI_INT, ®s, ®s, &sregs); + + rc = (rmi.eax & 0x00ff); + memcpy(bridge_address, replybuf->local_target, 6); + + /*------------------------------------------------------------------------ + Free DOS memory + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + return(rc); + +} /* end of IPX_Get_Local_Target */ + +#endif //NOT_FOR_WIN95 + +/*************************************************************************** + * IPX_Cancel_Event -- cancels an operation in progress * + * * + * INPUT: * + * ecb_ptr pointer to ECB event to cancel * + * * + * OUTPUT: * + * ??? * + * * + * WARNINGS: * + * The ECB must be located in real-mode memory, as well as the values * + * pointed to in the ECB (the IPX Header, the buffer to send, etc.) * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +int IPX_Cancel_Event(struct ECB *ecb_ptr) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + unsigned int rc; + + /*........................................................................ + Fill in registers for the DPMI call + ........................................................................*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + /*........................................................................ + Fill in registers for the interrupt call + ........................................................................*/ + rmi.ebx = IPX_CANCEL_EVENT; + rmi.es = (short)((long)ecb_ptr >> 4); + rmi.esi = (long)((long)ecb_ptr & 0x000f); + /*........................................................................ + call DPMI + ........................................................................*/ + int386x(DPMI_INT, ®s, ®s, &sregs); + + rc = (rmi.eax & 0x00ff); + + return(rc); + +} /* end of IPX_Cancel_Event */ +#endif //NOT_FOR_WIN95 + +/*************************************************************************** + * Let_IPX_Breath -- gives IPX some CPU time * + * * + * Use this function if you're polling the ECB's InUse flag, waiting * + * for it to go to 0: * + * * + * while ECBType.InUse * + * Let_IPX_Breath(); * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +void Let_IPX_Breath(void) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + + /*........................................................................ + Fill in registers for the DPMI call + ........................................................................*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + /*........................................................................ + Fill in registers for the interrupt call + ........................................................................*/ + rmi.ebx = IPX_RELINQUISH_CONTROL; + /*........................................................................ + call DPMI + ........................................................................*/ + int386x(DPMI_INT, ®s, ®s, &sregs); + +} /* end of Let_IPX_Breath */ +#endif //NOT_FOR_WIN95 diff --git a/IPX.H b/IPX.H new file mode 100644 index 0000000..d4e3962 --- /dev/null +++ b/IPX.H @@ -0,0 +1,188 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\ipx.h_v 2.17 16 Oct 1995 16:46:26 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPX.H * + * * + * Programmer : Barry Nance * + * from Client/Server LAN Programming * + * Westwood-ized by Bill Randolph * + * * + * Start Date : December 14, 1994 * + * * + * Last Update : December 14, 1994 [BR] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IPX_H +#define IPX_H + +/* +******************************** Structures ********************************* +*/ +typedef unsigned char NetNumType[4]; +typedef unsigned char NetNodeType[6]; +typedef char UserID[48]; + +/*--------------------------------------------------------------------------- +This is the IPX Packet structure. It's followed by the data itself, which +can be up to 546 bytes long. Annotation of 'IPX' means IPX will set this +field; annotation of 'APP' means the application must set the field. +NOTE: All header fields are ordered high-byte,low-byte. +---------------------------------------------------------------------------*/ +typedef struct IPXHEADER { + unsigned short CheckSum; // IPX: Not used; always 0xffff + unsigned short Length; // IPX: Total size, incl header & data + unsigned char TransportControl; // IPX: # bridges message crossed + unsigned char PacketType; // APP: Set to 4 for IPX (5 for SPX) + unsigned char DestNetworkNumber[4]; // APP: destination Network Number + unsigned char DestNetworkNode[6]; // APP: destination Node Address + unsigned short DestNetworkSocket; // APP: destination Socket Number + unsigned char SourceNetworkNumber[4]; // IPX: source Network Number + unsigned char SourceNetworkNode[6]; // IPX: source Node Address + unsigned short SourceNetworkSocket; // IPX: source Socket Number +} IPXHeaderType; + +/*--------------------------------------------------------------------------- +This is the IPX Event Control Block. It serves as a communications area +between IPX and the application for a single IPX operation. You should set +up a separate ECB for each IPX operation you perform. +---------------------------------------------------------------------------*/ +typedef struct ECB { + void *Link_Address; + void (*Event_Service_Routine)(void); // APP: event handler (NULL=none) + unsigned char InUse; // IPX: 0 = event complete + unsigned char CompletionCode; // IPX: event's return code + unsigned short SocketNumber; // APP: socket to send data through + unsigned short ConnectionID; // returned by Listen (???) + unsigned short RestOfWorkspace; + unsigned char DriverWorkspace[12]; + unsigned char ImmediateAddress[6]; // returned by Get_Local_Target + unsigned short PacketCount; + struct { + void *Address; + unsigned short Length; + } Packet[2]; +} ECBType; + + +/*--------------------------------------------------------------------------- +This structure is used for calling DPMI function 0x300, Call-Real-Mode- +Interrupt. It passes register values to & from the interrupt handler. +---------------------------------------------------------------------------*/ +typedef struct { + long edi; + long esi; + long ebp; + long Reserved; + long ebx; + long edx; + long ecx; + long eax; + short Flags; + short es; + short ds; + short fs; + short gs; + short ip; + short cs; + short sp; + short ss; +} RMIType; + +/* +********************************** Defines ********************************** +*/ +/*--------------------------------------------------------------------------- +These defines are for the IPX functions. The function number is specified +by placing it in BX when IPX is called. There are two ways to invoke IPX: +use interrupt 0x7a, or use a function whose address is obtained by calling +interrupt 0x2f with AX=0x7a00; the function address is returned in ES:DI. +This is the preferred method, since other apps are known to use int 0x7a. +---------------------------------------------------------------------------*/ +#define IPX_OPEN_SOCKET 0x0000 +#define IPX_CLOSE_SOCKET 0x0001 +#define IPX_GET_LOCAL_TARGET 0x0002 +#define IPX_SEND_PACKET 0x0003 +#define IPX_LISTEN_FOR_PACKET 0x0004 +#define IPX_SCHEDULE_EVENT 0x0005 +#define IPX_CANCEL_EVENT 0x0006 +#define IPX_GET_INTERVAL_MARKER 0x0008 +#define IPX_GET_INTERNETWORK_ADDRESS 0x0009 +#define IPX_RELINQUISH_CONTROL 0x000A +#define IPX_DISCONNECT_FROM_TARGET 0x000B + +/*--------------------------------------------------------------------------- +These defines are for various IPX error codes: +---------------------------------------------------------------------------*/ +#define IPXERR_CONNECTION_SEVERED 0x00ec +#define IPXERR_CONNECTION_FAILED 0x00ed +#define IPXERR_NO_CONNECTION 0x00ee +#define IPXERR_CONNECTION_TABLE_FULL 0x00ef +#define IPXERR_NO_CANCEL_ECB 0x00f9 +#define IPXERR_NO_PATH 0x00fa +#define IPXERR_ECB_INACTIVE 0x00fc +#define IPXERR_INVALID_PACKET_LENGTH 0x00fd +#define IPXERR_SOCKET_TABLE_FULL 0x00fe +#define IPXERR_SOCKET_ERROR 0x00ff + +/*--------------------------------------------------------------------------- +These defines are for various interrupt vectors and DPMI functions: +---------------------------------------------------------------------------*/ +#define IPX_INT 0x007a +#define DPMI_INT 0x0031 +#define DPMI_ALLOC_DOS_MEM 0x0100 +#define DPMI_FREE_DOS_MEM 0x0101 +#define DPMI_CALL_REAL_INT 0x0300 +#define DPMI_LOCK_MEM 0x0600 +#define DPMI_UNLOCK_MEM 0x0601 + +/* +******************************** Prototypes ********************************* +*/ +/*--------------------------------------------------------------------------- +NOTE: All routines return 0 for a success code and one of the above IPX +error codes if there's an error, EXCEPT: +- IPX_SPX_Installed (0 = not installed) +- Get_Connection_Number / Get_1st_Connection_Number (0 = no connection) +---------------------------------------------------------------------------*/ +/* +.................................. ipx.cpp .................................. +*/ +int IPX_SPX_Installed(void); +int IPX_Open_Socket(unsigned short socket); +int IPX_Close_Socket(unsigned short socket); +int IPX_Get_Connection_Number(void); +int IPX_Get_1st_Connection_Num (char *username); +int IPX_Get_Internet_Address(int connection_number, + unsigned char *network_number, unsigned char *physical_node); +int IPX_Get_User_ID(int connection_number, char *user_id); +int IPX_Listen_For_Packet(struct ECB *ecb_ptr); +void IPX_Send_Packet(struct ECB *ecb_ptr); +int IPX_Get_Local_Target(unsigned char *dest_network, unsigned char *dest_node, + unsigned short dest_socket, unsigned char *bridge_address); +int IPX_Cancel_Event(struct ECB *ecb_ptr); +void Let_IPX_Breath(void); + +#endif diff --git a/IPX95.CPP b/IPX95.CPP new file mode 100644 index 0000000..29678cc --- /dev/null +++ b/IPX95.CPP @@ -0,0 +1,85 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPX95PP * + * * + * Programmer : Steve Tall * + * * + * Start Date : January 22nd, 1996 * + * * + * Last Update : January 22nd, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * * + * * + * * + *-------------------------------------------------------------------------* + * Functions: * + * * + * * + * * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "ipx95.h" + +int IPX_Open_Socket(unsigned short socket) +{ + return (IPX_Open_Socket95((int)socket)); +} + + +int IPX_Close_Socket(unsigned short socket) +{ + IPX_Close_Socket95((int)socket); + return (0); +} + + +int IPX_Get_Connection_Number(void) +{ + return (IPX_Get_Connection_Number95()); +} + + + +int IPX_Broadcast_Packet(unsigned char *buf, int buflen) +{ + return(IPX_Broadcast_Packet95(buf, buflen)); +} + +extern "C"{ + extern void __cdecl Int3(void); +} + +int IPX_Get_Local_Target(unsigned char *dest_network, unsigned char *dest_node, + unsigned short dest_socket, unsigned char *bridge_address) +{ + //Int3(); + return (IPX_Get_Local_Target95(dest_network, dest_node, dest_socket, bridge_address)); +} + + diff --git a/IPX95.H b/IPX95.H new file mode 100644 index 0000000..4282c0b --- /dev/null +++ b/IPX95.H @@ -0,0 +1,57 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPX95PP * + * * + * Programmer : Steve Tall * + * * + * Start Date : January 22nd, 1996 * + * * + * Last Update : January 22nd, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * * + * * + * * + *-------------------------------------------------------------------------* + * Functions: * + * * + * * + * * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +extern "C"{ + extern BOOL __stdcall IPX_Initialise(void); + extern BOOL __stdcall IPX_Get_Outstanding_Buffer95(unsigned char *buffer); + extern void __stdcall IPX_Shut_Down95(void); + extern int __stdcall IPX_Send_Packet95(unsigned char *, unsigned char *, int, unsigned char*, unsigned char*); + extern int __stdcall IPX_Broadcast_Packet95(unsigned char *, int); + extern BOOL __stdcall IPX_Start_Listening95(void); + extern int __stdcall IPX_Open_Socket95(int socket); + extern void __stdcall IPX_Close_Socket95(int socket); + extern int __stdcall IPX_Get_Connection_Number95(void); + extern int __stdcall IPX_Get_Local_Target95(unsigned char *, unsigned char*, unsigned short, unsigned char*); +} diff --git a/IPXADDR.CPP b/IPXADDR.CPP new file mode 100644 index 0000000..b52494a --- /dev/null +++ b/IPXADDR.CPP @@ -0,0 +1,494 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\ipxaddr.cpv 2.17 16 Oct 1995 16:50:58 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXADDR.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : December 19, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * IPXAddressClass::IPXAddressClass -- class constructor * + * IPXAddressClass::IPXAddressClass -- class constructor form 2 * + * IPXAddressClass::IPXAddressClass -- class constructor form 3 * + * IPXAddressClass::Set_Address -- sets the IPX address values * + * IPXAddressClass::Set_Address -- sets the IPX values from a header * + * IPXAddressClass::Get_Address -- retrieves the IPX address values * + * IPXAddressClass::Get_Address -- copies address into an IPX header * + * IPXAddressClass::Is_Broadcast -- tells if this is a broadcast address * + * IPXAddressClass::operator== -- overloaded comparison operator * + * IPXAddressClass::operator!= -- overloaded comparison operator * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "tcpip.h" + +/*************************************************************************** + * IPXAddressClass::IPXAddressClass -- class constructor * + * * + * This default constructor generates a broadcast address. * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +IPXAddressClass::IPXAddressClass(void) +{ + NetworkNumber[0] = 0xff; + NetworkNumber[1] = 0xff; + NetworkNumber[2] = 0xff; + NetworkNumber[3] = 0xff; + NodeAddress[0] = 0xff; + NodeAddress[1] = 0xff; + NodeAddress[2] = 0xff; + NodeAddress[3] = 0xff; + NodeAddress[4] = 0xff; + NodeAddress[5] = 0xff; + +} /* end of IPXAddressClass */ + + +/*************************************************************************** + * IPXAddressClass::IPXAddressClass -- class constructor form 2 * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +IPXAddressClass::IPXAddressClass(NetNumType net, NetNodeType node) +{ + memcpy(NetworkNumber,net,4); + memcpy(NodeAddress,node,6); + +} /* end of IPXAddressClass */ + + +/*************************************************************************** + * IPXAddressClass::IPXAddressClass -- class constructor form 3 * + * * + * This form of the constructor takes an IPX header as an argument. It * + * extracts the address from the Source address fields in the header. * + * * + * INPUT: * + * header Header from which to extract the address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +IPXAddressClass::IPXAddressClass(IPXHeaderType *header) +{ + memcpy(NetworkNumber,header->SourceNetworkNumber,4); + memcpy(NodeAddress,header->SourceNetworkNode,6); + +} /* end of IPXAddressClass */ + + +/*************************************************************************** + * IPXAddressClass::Set_Address -- sets the IPX address values * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +void IPXAddressClass::Set_Address(NetNumType net, NetNodeType node) +{ + memcpy(NetworkNumber,net,4); + memcpy(NodeAddress,node,6); + +} /* end of Set_Address */ + + +/*************************************************************************** + * IPXAddressClass::Set_Address -- sets the IPX values from a header * + * * + * This routine extracts the source addresses from the given IPX header. * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +void IPXAddressClass::Set_Address(IPXHeaderType *header) +{ + +#ifdef VIRTUAL_SUBNET_SERVER + if (Winsock.Get_Connected()){ + memset(NetworkNumber, 1 ,4); + memset(NodeAddress, 0, 6); + unsigned short target_mask = *(unsigned short*)header; + /* + ** If this is a head to head game (no VSS) -- + ** If mask is 0 then this packet was broadcast from the other player + ** Otherwise exclusive or with 3 to get other players mask + */ + if (!UseVirtualSubnetServer){ + if (target_mask == 0){ + target_mask = 1 << PlanetWestwoodIsHost; + } + target_mask ^= 3; + } + + *(unsigned short*) &NodeAddress[0] = target_mask; + + }else{ + memcpy(NetworkNumber,header->SourceNetworkNumber,4); + memcpy(NodeAddress,header->SourceNetworkNode,6); + } +#else //VIRTUAL_SUBNET_SERVER + if (header){ + memcpy(NetworkNumber,header->SourceNetworkNumber,4); + memcpy(NodeAddress,header->SourceNetworkNode,6); + }else{ + /* + ** Address is meaningless when using winsock + */ + memset(NetworkNumber, 1 ,4); + memset(NodeAddress, 1, 6); + } +#endif //VIRTUAL_SUBNET_SERVER +} /* end of Set_Address */ + + +/*************************************************************************** + * IPXAddressClass::Get_Address -- retrieves the IPX address values * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +void IPXAddressClass::Get_Address(NetNumType net, NetNodeType node) +{ + memcpy(net,NetworkNumber,4); + memcpy(node,NodeAddress,6); + +} /* end of Get_Address */ + + +/*************************************************************************** + * IPXAddressClass::Get_Address -- copies address into an IPX header * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +void IPXAddressClass::Get_Address(IPXHeaderType *header) +{ + memcpy(header->DestNetworkNumber,NetworkNumber,4); + memcpy(header->DestNetworkNode,NodeAddress,6); + +} /* end of Get_Address */ + + +/*************************************************************************** + * IPXAddressClass::Is_Broadcast -- tells if this is a broadcast address * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +bool IPXAddressClass::Is_Broadcast(void) +{ + if ( NetworkNumber[0] == 0xff && + NetworkNumber[1] == 0xff && + NetworkNumber[2] == 0xff && + NetworkNumber[3] == 0xff && + NodeAddress[0] == 0xff && + NodeAddress[1] == 0xff && + NodeAddress[2] == 0xff && + NodeAddress[3] == 0xff && + NodeAddress[4] == 0xff && + NodeAddress[5] == 0xff) { + return(true); + } else { + return(false); + } +} + + +/*************************************************************************** + * IPXAddressClass::operator== -- overloaded comparison operator * + * * + * Since, if NETX isn't running, the network number on a received packet * + * can be bogus (all 0's), only the node address is used for comparison * + * purposes here. * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * 0 = not equal, 1 = equal * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator == (IPXAddressClass & addr) +{ + /*------------------------------------------------------------------------ + If either Network Number is all 0's (which can happen if the system is + not running NETX), compare only the Node Addresses. + ------------------------------------------------------------------------*/ + if ( (NetworkNumber[0]==0 && + NetworkNumber[1]==0 && + NetworkNumber[2]==0 && + NetworkNumber[3]==0) || + (addr.NetworkNumber[0]==0 && + addr.NetworkNumber[1]==0 && + addr.NetworkNumber[2]==0 && + addr.NetworkNumber[3]==0) ) { + + if (memcmp(NodeAddress,addr.NodeAddress,6)==0) { + return(true); + } else { + return(false); + } + + } else { + + /*------------------------------------------------------------------------ + Otherwise, compare both the Network Numbers and Node Addresses + ------------------------------------------------------------------------*/ + if (memcmp(NodeAddress,addr.NodeAddress,6)==0 && memcmp(NetworkNumber,addr.NetworkNumber,4)==0) { + return(true); + } else { + return(false); + } + } +} + + +/*************************************************************************** + * IPXAddressClass::operator!= -- overloaded comparison operator * + * * + * Since, if NETX isn't running, the network number on a received packet * + * can be bogus (all 0's), only the node address is used for comparison * + * purposes here. * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * 0 = equal, 1 = not equal * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator != (IPXAddressClass & addr) +{ + /*------------------------------------------------------------------------ + If either Network Number is all 0's (which can happen if the system is + not running NETX), compare only the Node Addresses. + ------------------------------------------------------------------------*/ + if ( (NetworkNumber[0]==0 && + NetworkNumber[1]==0 && + NetworkNumber[2]==0 && + NetworkNumber[3]==0) || + (addr.NetworkNumber[0]==0 && + addr.NetworkNumber[1]==0 && + addr.NetworkNumber[2]==0 && + addr.NetworkNumber[3]==0) ) { + + if (memcmp(NodeAddress,addr.NodeAddress,6)==0) { + return(false); + } else { + return(true); + } + } else { + + /*------------------------------------------------------------------------ + Otherwise, compare both the Network Numbers and Node Addresses + ------------------------------------------------------------------------*/ + if (memcmp(NodeAddress,addr.NodeAddress,6)==0 && memcmp(NetworkNumber,addr.NetworkNumber,4)==0) { + return(false); + } else { + return(true); + } + } +} + + +/*************************************************************************** + * IPXAddressClass::operator > -- overloaded comparison operator * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * TRUE = greater, FALSE = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator > (IPXAddressClass & addr) +{ + return(memcmp(this, &addr, 10) > 0); + +} /* end of operator != */ + + +/*************************************************************************** + * IPXAddressClass::operator < -- overloaded comparison operator * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * TRUE = less, FALSE = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator < (IPXAddressClass & addr) +{ + return(memcmp(this, &addr, 10) < 0); + +} /* end of operator != */ + + +/*************************************************************************** + * IPXAddressClass::operator >= -- overloaded comparison operator * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * TRUE = greater or equal, FALSE = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator >= (IPXAddressClass & addr) +{ + return(memcmp(this, &addr, 10) >= 0); + +} /* end of operator != */ + + +/*************************************************************************** + * IPXAddressClass::operator <= -- overloaded comparison operator * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * TRUE = less or equal, FALSE = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator <= (IPXAddressClass & addr) +{ + return(memcmp(this, &addr, 10) <= 0); + +} /* end of operator != */ + + diff --git a/IPXADDR.H b/IPXADDR.H new file mode 100644 index 0000000..28a7c13 --- /dev/null +++ b/IPXADDR.H @@ -0,0 +1,106 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\ipxaddr.h_v 2.19 16 Oct 1995 16:44:54 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXADDR.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : December 19, 1994 [BR] * + * * + * This class is useful for any IPX-related code. It's just a utility * + * to help manage those annoying IPX address fields. This class lets you * + * compare addresses, copy addresses to & from the IPX header, etc. * + * * + * The class has no virtual functions, so you can treat this class just * + * like a data structure; it can be loaded & saved, and even transmitted * + * across the net. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IPXADDR_H +#define IPXADDR_H + +#include "ipx.h" // for NetNumType & NetNodeType + + +/* +***************************** Class Declaration ***************************** +*/ +class IPXAddressClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Constructors: + .....................................................................*/ + IPXAddressClass(void); + IPXAddressClass(NetNumType net, NetNodeType node); + IPXAddressClass(IPXHeaderType *header); + + /*..................................................................... + Set the address from explicit variables, or from the SOURCE values + in an IPX packet header. + .....................................................................*/ + void Set_Address(NetNumType net, NetNodeType node); + void Set_Address(IPXHeaderType *header); + /*..................................................................... + Get the address values explicitly, or copy them into the DESTINATION + values in an IPX packet header. + .....................................................................*/ + void Get_Address (NetNumType net, NetNodeType node); + void Get_Address(IPXHeaderType *header); + + /*..................................................................... + Tells if this address is a broadcast address + .....................................................................*/ + bool Is_Broadcast(void); + + /*..................................................................... + Overloaded operators: + .....................................................................*/ + int operator == (IPXAddressClass & addr); + int operator != (IPXAddressClass & addr); + int operator > (IPXAddressClass &addr); + int operator < (IPXAddressClass &addr); + int operator >= (IPXAddressClass &addr); + int operator <= (IPXAddressClass &addr); + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + /* + --------------------------- Private Interface ---------------------------- + */ + private: + NetNumType NetworkNumber; + NetNodeType NodeAddress; +}; + +#endif +/**************************** end of ipxaddr.h *****************************/ diff --git a/IPXCONN.CPP b/IPXCONN.CPP new file mode 100644 index 0000000..26b8873 --- /dev/null +++ b/IPXCONN.CPP @@ -0,0 +1,680 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\ipxconn.cpv 1.9 16 Oct 1995 16:50:52 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXCONN.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : April 9, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * IPXConnClass::IPXConnClass -- class constructor * + * IPXConnClass::~IPXConnClass -- class destructor * + * IPXConnClass::Init -- hardware-specific initialization routine * + * IPXConnClass::Configure -- One-time initialization routine * + * IPXConnClass::Start_Listening -- commands IPX to listen * + * IPXConnClass::Stop_Listening -- commands IPX to stop listen * + * IPXConnClass::Send -- sends a packet; invoked by SequencedConnection * + * IPXConnClass::Open_Socket -- opens communications socket * + * IPXConnClass::Close_Socket -- closes the socket * + * IPXConnClass::Send_To -- sends the packet to the given address * + * IPXConnClass::Broadcast -- broadcasts the given packet * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "ipx95.h" +#include "tcpip.h" + +/* +********************************* Globals *********************************** +*/ +unsigned short IPXConnClass::Socket; +int IPXConnClass::ConnectionNum; +ECBType * IPXConnClass::ListenECB; +IPXHeaderType * IPXConnClass::ListenHeader; +char * IPXConnClass::ListenBuf; +ECBType * IPXConnClass::SendECB; +IPXHeaderType * IPXConnClass::SendHeader; +char * IPXConnClass::SendBuf; +long IPXConnClass::Handler; +int IPXConnClass::Configured = 0; +int IPXConnClass::SocketOpen = 0; +int IPXConnClass::Listening = 0; +int IPXConnClass::PacketLen; + + +/*************************************************************************** + * IPXConnClass::IPXConnClass -- class constructor * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the recieve queue * + * maxlen max length of an application packet * + * magicnum the packet "magic number" for this connection * + * address address of destination (NULL = no address) * + * id connection's unique numerical ID * + * name connection's name * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +IPXConnClass::IPXConnClass (int numsend, int numreceive, int maxlen, + unsigned short magicnum, IPXAddressClass *address, int id, char *name) : +#ifdef SEQ_NET + SequencedConnClass (numsend, numreceive, maxlen, magicnum, +#else + NonSequencedConnClass (numsend, numreceive, maxlen, magicnum, +#endif + 2, // retry delta + -1, // max retries + 60) // timeout +{ + NetNumType net; + NetNodeType node; + + /*------------------------------------------------------------------------ + Save the values passed in + ------------------------------------------------------------------------*/ + if (address) + Address = (*address); + ID = id; + strcpy (Name, name); + + if (!Winsock.Get_Connected()){ + /*------------------------------------------------------------------------ + If our Address field is an actual address (ie NULL wasn't passed to the + constructor), pre-compute the ImmediateAddress value for the SendECB. + This allows pre-computing of the ImmediateAddress for all connections + created after Configure() is called. + ------------------------------------------------------------------------*/ + if (!Address.Is_Broadcast() && Configured==1) { + Address.Get_Address(net,node); + /*..................................................................... + If the user is logged in & has a valid Novell Connection Number, get the + bridge address the "official" way + .....................................................................*/ + if (ConnectionNum != 0) { + if (IPX_Get_Local_Target (net, node, Socket, ImmediateAddress)!=0) + memcpy(ImmediateAddress,node,6); + } else { + + /*..................................................................... + Otherwise, use the destination node address as the ImmediateAddress, and + just hope there's no network bridge in the path. + .....................................................................*/ + memcpy(ImmediateAddress,node,6); + } + + Immed_Set = 1; + } else { + memset (ImmediateAddress, 0, 6); + Immed_Set = 0; + } + } + +} /* end of IPXConnClass */ + + +/*************************************************************************** + * IPXConnClass::Init -- hardware-specific initialization routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void IPXConnClass::Init (void) +{ + /*------------------------------------------------------------------------ + Invoke the parent's Init routine + ------------------------------------------------------------------------*/ +#ifdef SEQ_NET + SequencedConnClass::Init(); +#else + NonSequencedConnClass::Init(); +#endif +} + + +/*************************************************************************** + * IPXConnClass::Configure -- One-time initialization routine * + * * + * This routine sets up static members that are shared by all IPX * + * connections (ie those variables used by the Send/Listen/Broadcast * + * routines). * + * * + * INPUT: * + * socket socket ID for sending & receiving * + * conn_num local IPX Connection Number (0 = not logged in) * + * listen_ecb ptr to ECBType for listening * + * send_ecb ptr to ECBType for sending * + * listen_header ptr to IPXHeaderType for listening * + * send_header ptr to IPXHeaderType for sending * + * listen_buf ptr to buffer for listening * + * send_buf ptr to buffer for sending * + * handler_rm_ptr REAL-MODE pointer to event service routine * + * (high word = segment, low word = offset) * + * maxpacketlen max packet size to listen for * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * - All pointers must be protected-mode pointers, but must point to * + * DOS real-mode memory (except the Handler segment/offset) * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void IPXConnClass::Configure (unsigned short socket, int conn_num, + ECBType *listen_ecb, ECBType *send_ecb, IPXHeaderType *listen_header, + IPXHeaderType *send_header, char *listen_buf, char *send_buf, + long handler_rm_ptr, int maxpacketlen) +{ + /*------------------------------------------------------------------------ + Save the values passed in + ------------------------------------------------------------------------*/ + Socket = socket; + ConnectionNum = conn_num; + ListenECB = listen_ecb; + SendECB = send_ecb; + ListenHeader = listen_header; + SendHeader = send_header; + ListenBuf = listen_buf; + SendBuf = send_buf; + Handler = handler_rm_ptr; + PacketLen = maxpacketlen; + + Configured = 1; + +} /* end of Configure */ + + +/*************************************************************************** + * IPXConnClass::Start_Listening -- commands IPX to listen * + * * + * This routine may be used to start listening in polled mode (if the * + * ECB's Event_Service_Routine is NULL), or in interrupt mode; it's * + * up to the caller to fill the ECB in. If in polled mode, Listening * + * must be restarted every time a packet comes in. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * - The ListenECB must have been properly filled in by the IPX Manager.* + * - Configure must be called before calling this routine. * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +bool IPXConnClass::Start_Listening(void) +{ + +#ifndef NOT_FOR_WIN95 + + if (Winsock.Get_Connected()) return (true); + + /*------------------------------------------------------------------------ + Open the Socket + ------------------------------------------------------------------------*/ + if (!Open_Socket(Socket)) + return(false); + + if (IPX_Start_Listening95()){ + Listening =1; + return (TRUE); + }else{ + return (FALSE); + } + +#else + + void *hdr_ptr; + unsigned long hdr_val; + void *buf_ptr; + unsigned long buf_val; + int rc; + + /*------------------------------------------------------------------------ + Don't do a thing unless we've been configured, and we're not listening. + ------------------------------------------------------------------------*/ + if (Configured==0 || Listening==1) + return(false); + + /*------------------------------------------------------------------------ + Open the Socket + ------------------------------------------------------------------------*/ + if (!Open_Socket(Socket)) + return(false); + + /*------------------------------------------------------------------------ + Clear the ECB & header + ------------------------------------------------------------------------*/ + memset(ListenECB, 0, sizeof(ECBType)); + memset(ListenHeader, 0, sizeof(IPXHeaderType)); + + /*------------------------------------------------------------------------ + Convert protected-mode ptrs to real-mode ptrs + ------------------------------------------------------------------------*/ + hdr_val = (unsigned long)ListenHeader; + hdr_ptr = (void *)(((hdr_val & 0xffff0) << 12) | (hdr_val & 0x000f)); + + buf_val = (unsigned long)ListenBuf; + buf_ptr = (void *)(((buf_val & 0xffff0) << 12) | (buf_val & 0x000f)); + + /*------------------------------------------------------------------------ + Fill in the ECB + ------------------------------------------------------------------------*/ + ListenECB->SocketNumber = Socket; + ListenECB->PacketCount = 2; + ListenECB->Packet[0].Address = hdr_ptr; + ListenECB->Packet[0].Length = sizeof(IPXHeaderType); + ListenECB->Packet[1].Address = buf_ptr; + ListenECB->Packet[1].Length = (unsigned short)PacketLen; + + ((long &)ListenECB->Event_Service_Routine) = Handler; + + /*------------------------------------------------------------------------ + Command IPX to listen + ------------------------------------------------------------------------*/ + rc = IPX_Listen_For_Packet(ListenECB); + if (rc!=0) { + Close_Socket(Socket); + return(false); + } else { + Listening = 1; + return(true); + } + +#endif //NOT_FOR_WIN95 +} /* end of Start_Listening */ + + +/*************************************************************************** + * IPXConnClass::Stop_Listening -- commands IPX to stop listen * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * - This routine MUST NOT be called if IPX is not listening already! * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +bool IPXConnClass::Stop_Listening(void) +{ + /*------------------------------------------------------------------------ + Don't do anything unless we're already Listening. + ------------------------------------------------------------------------*/ + if (Listening==0) + return(false); + +#ifndef NOT_FOR_WIN95 + + if (Winsock.Get_Connected()){ + Listening = 0; + return (true); + }else{ + IPX_Shut_Down95(); + Close_Socket(Socket); + } + +#else //NOT_FOR_WIN95 + + /*------------------------------------------------------------------------ + Shut IPX down. + ------------------------------------------------------------------------*/ + IPX_Cancel_Event(ListenECB); + Close_Socket(Socket); + +#endif //NOT_FOR_WIN95 + + Listening = 0; + + /*------------------------------------------------------------------------ + All done. + ------------------------------------------------------------------------*/ + return(true); + +} /* end of Stop_Listening */ + + +/*************************************************************************** + * IPXConnClass::Send -- sends a packet; invoked by SequencedConnection * + * * + * INPUT: * + * socket desired socket ID number * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +int IPXConnClass::Send(char *buf, int buflen) +{ + /*------------------------------------------------------------------------ + Invoke our own Send_To routine, filling in our Address as the destination. + ------------------------------------------------------------------------*/ + if (Immed_Set) { + return(Send_To (buf, buflen, &Address, ImmediateAddress)); + } else { + return(Send_To (buf, buflen, &Address, NULL)); + } + +} /* end of Send */ + + +/*************************************************************************** + * IPXConnClass::Open_Socket -- opens communications socket * + * * + * INPUT: * + * socket desired socket ID number * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +int IPXConnClass::Open_Socket(unsigned short socket) +{ + int rc; + + if (Winsock.Get_Connected()){ + SocketOpen = 1; + return (true); + } + + SocketOpen = 0; + + /*------------------------------------------------------------------------ + Try to open a listen socket. The socket may have been left open by + a previously-crashed program, so ignore the state of the SocketOpen + flag for this call; use IPX to determine if the socket was already open. + ------------------------------------------------------------------------*/ + rc = IPX_Open_Socket(socket); + if (rc) { + /* + ................. If already open, close & reopen it .................. + */ + if (rc==IPXERR_SOCKET_ERROR) { + IPX_Close_Socket(socket); + rc = IPX_Open_Socket(socket); + /* + .................. Still can't open: return error .................. + */ + if (rc) { + return(false); + } + } + } + + SocketOpen = 1; + + return(true); +} + + +/*************************************************************************** + * IPXConnClass::Close_Socket -- closes the socket * + * * + * INPUT: * + * socket desired socket ID number * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Calling this routine when the sockets aren't open may crash! * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +void IPXConnClass::Close_Socket(unsigned short socket) +{ + if (Winsock.Get_Connected()){ + SocketOpen = 0; + return; + } + /*------------------------------------------------------------------------ + Never, ever, ever, under any circumstances whatsoever, close a socket + that isn't open. You'll regret it forever (or until at least until you're + through rebooting, which, if you're on a Pentium is the same thing). + ------------------------------------------------------------------------*/ + if (SocketOpen==1) + IPX_Close_Socket(socket); + + SocketOpen = 0; + +} /* end of Close_Socket */ + + +/*************************************************************************** + * IPXConnClass::Send_To -- sends the packet to the given address * + * * + * The "ImmediateAddress" field of the SendECB must be filled in with the * + * address of a bridge, or the node address of the destination if there * + * is no bridge. The NETX call to find this address will always crash * + * if NETX isn't loaded (ConnectionNum is 0), so this case is trapped & * + * prevented. * + * Also, if the address of this IPX connection is known when the * + * constructor is called, and Configure has been called, Get_Local_Target * + * is called to precompute the ImmediateAddress; this case is detected & * + * if the value is already computed, it's just memcpy'd over. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * address Address to send to * + * immed ImmediateAddress value, NULL if none * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +#pragma off (unreferenced) +int IPXConnClass::Send_To(char *buf, int buflen, IPXAddressClass *address, + NetNodeType immed) +{ + + void *hdr_ptr; + void *buf_ptr; + unsigned long hdr_val; + unsigned long buf_val; + NetNumType net; + NetNodeType node; + int rc; + + unsigned short target_mask; + + unsigned char send_address[6]; + + if (Winsock.Get_Connected()){ + +#ifdef VIRTUAL_SUBNET_SERVER + if (immed){ + memcpy(send_address, immed, 6); + }else{ + address->Get_Address(net,node); + memcpy (send_address, node, 6); + } + /* + ** Use first two bytes of ipx address as target mask + */ + unsigned short *maskptr = (unsigned short*)&send_address[0]; + target_mask = *maskptr; + + char *tempsend = new char [buflen + sizeof (target_mask)]; + + *(unsigned short*)tempsend = htons(target_mask); + memcpy (tempsend+2, buf, buflen); +#if (0) +char tempbuf[256]; +CommHeaderType *packet = (CommHeaderType *)(&tempsend[2]); +static char pcode [4][18]={ + "PACKET_DATA_ACK", // this is a data packet requiring an ACK + "PACKET_DATA_NOACK", // this is a data packet not requiring an ACK + "PACKET_ACK", // this is an ACK for a packet + "PACKET_COUNT" // for computational purposes +}; + +sprintf (tempbuf, "Sending unicast packet type %d, ID=%d, code=%s, length=%d\n", tempsend[sizeof(CommHeaderType)+2], + packet->PacketID, + pcode[packet->Code], + buflen + sizeof (target_mask)); +CCDebugString (tempbuf); +#endif //(0) + + Winsock.Write((void*)tempsend, buflen + sizeof (target_mask)); + delete [] tempsend; +#else // VIRTUAL_SUBNET_SERVER + Winsock.Write((void*)buf, buflen); +#endif // VIRTUAL_SUBNET_SERVER + + return (true); + } + + + + if (immed) { + memcpy(send_address, immed, 6); + //memcpy(node, immed, 6); + //memset (net, 0, sizeof(net) ); + address->Get_Address(net,node); + } else { + address->Get_Address(net,node); + /*..................................................................... + If the user is logged in & has a valid Novell Connection Number, get the + bridge address the "official" way + .....................................................................*/ + if (ConnectionNum != 0) { + rc = IPX_Get_Local_Target (net, node, Socket, &send_address[0]); + if (rc!=0) { + return(false); + } + } else { + /*..................................................................... + Otherwise, use the destination node address as the ImmediateAddress, and + just hope there's no network bridge in the path. + .....................................................................*/ + memcpy(send_address,node,6); + } + } + + return (IPX_Send_Packet95(&send_address[0], (unsigned char*)buf, buflen, (unsigned char*)net, (unsigned char*)node)); + + +} +#pragma on (unreferenced) + +/*************************************************************************** + * IPXConnClass::Broadcast -- broadcasts the given packet * + * * + * INPUT: * + * socket desired socket ID number * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +int IPXConnClass::Broadcast(char *buf, int buflen) +{ + + if (Winsock.Get_Connected()){ +#ifdef VIRTUAL_SUBNET_SERVER + char *tempsend = new char [buflen + sizeof (unsigned short)]; + memcpy (tempsend+2, buf, buflen); + *tempsend = 0; + *(tempsend+1) = 0; +#if (0) +char tempbuf[256]; +CommHeaderType *packet = (CommHeaderType *)(&tempsend[2]); +static char pcode [4][18]={ + "PACKET_DATA_ACK", // this is a data packet requiring an ACK + "PACKET_DATA_NOACK", // this is a data packet not requiring an ACK + "PACKET_ACK", // this is an ACK for a packet + "PACKET_COUNT" // for computational purposes +}; + +sprintf (tempbuf, "Sending multicast packet type %d, ID=%d, code=%s, length=%d\n", tempsend[sizeof(CommHeaderType)+2], + packet->PacketID, + pcode[packet->Code], + buflen + sizeof (unsigned short)); +CCDebugString (tempbuf); +#endif //(0) + + Winsock.Write((void*)tempsend, buflen + sizeof (unsigned short)); + delete [] tempsend; +#else // VIRTUAL_SUBNET_SERVER + Winsock.Write((void*)buf, buflen); +#endif // VIRTUAL_SUBNET_SERVER + return(true); + }else{ + return (IPX_Broadcast_Packet95((unsigned char*)buf, buflen)); + } + +} + diff --git a/IPXCONN.H b/IPXCONN.H new file mode 100644 index 0000000..19b31ad --- /dev/null +++ b/IPXCONN.H @@ -0,0 +1,211 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\ipxconn.h_v 1.12 16 Oct 1995 16:45:10 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXCONN.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 9, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This is the Connection Class for IPX communications. It inherits * + * a Queue, PacketBuf, timeout variables from ConnectionClass. It * + * inherits its Send_/Receive_/Get_Packet functions, and the sequenced * + * ACK/Retry logic in Service_Send_Queue & Service_Recieve_Queue from * + * SequencedConnClass. It guarantees order of delivery of packets. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IPXCONN_H +#define IPXCONN_H + + +/* +********************************* Includes ********************************** +*/ +#ifdef SEQ_NET +#include "seqconn.h" +#else +#include "noseqcon.h" +#endif +#include "ipxaddr.h" + + +/* +***************************** Class Declaration ***************************** +*/ +#ifdef SEQ_NET +class IPXConnClass : public SequencedConnClass +#else +class IPXConnClass : public NonSequencedConnClass +#endif +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Various useful enums: + .....................................................................*/ + enum IPXConnTag { + CONN_NAME_MAX = 40, // max # chars allowed for connection name + CONNECTION_NONE = -1, // value of an invalid connection ID + }; + + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + IPXConnClass(int numsend, int numrecieve, int maxlen, + unsigned short magicnum, IPXAddressClass *address, int id, char *name); + virtual ~IPXConnClass () {}; + + /*..................................................................... + Initialization. + .....................................................................*/ + virtual void Init (void); + + /*..................................................................... + The Configure function is for configuring all connections at once. + It's static because it doesn't apply to any specific connection, but + all of them. + .....................................................................*/ + static void Configure(unsigned short socket, + int conn_num, ECBType *listen_ecb, ECBType *send_ecb, + IPXHeaderType *listen_header, IPXHeaderType *send_header, + char *listen_buf, char *send_buf, long handler_rm_ptr, + int maxpacketlen); + + /*..................................................................... + These routines tell IPX to start listening for packets, and to stop + listening for packets. They're static because they affect all + connections at once (there's no way to turn listening on for only one + connection; it's all or nothing). + .....................................................................*/ + static bool Start_Listening (void); + static bool Stop_Listening (void); + + /*..................................................................... + The Destination IPX Address for this connection + .....................................................................*/ + IPXAddressClass Address; + + /*..................................................................... + The "Immediate" (Bridge) address for this connection, and a flag + telling if the address has been precomputed. + .....................................................................*/ + NetNodeType ImmediateAddress; + int Immed_Set; + + /*..................................................................... + Each IPX Connection can have a Name & Unique numerical ID + .....................................................................*/ + int ID; + char Name[CONN_NAME_MAX]; + + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + + /*..................................................................... + This is the overloaded Send routine declared in ConnectionClass, and + used in SequencedConnClass. + .....................................................................*/ + virtual int Send (char *buf, int buflen); + + /*..................................................................... + These are the routines that access IPX. Open_Socket & Close_Socket are + static because they're called by Start_Listening & Stop_Listening. + Send_To & Broadcast are static since they're direct interfaces to IPX, + and there's only one IPX instance running. + .....................................................................*/ + static int Open_Socket(unsigned short socket); + static void Close_Socket(unsigned short socket); + static int Send_To(char *buf, int buflen, IPXAddressClass *address, NetNodeType immed); + static int Broadcast(char *buf, int buflen); + + /*..................................................................... + The socket ID for this connection + .....................................................................*/ + static unsigned short Socket; + + /*..................................................................... + User's local Connection # (0 = not logged in) + .....................................................................*/ + static int ConnectionNum; + + /*..................................................................... + This is a static version of MaxPacketLen, which is the size of the + app's packets, plus our internal CommHeaderType. It's used in the + Start_Listening routine. + .....................................................................*/ + static int PacketLen; + + /*..................................................................... + Variables for Listening (created by the IPXManagerClass, and passed + in via Init). All IPX connections share these buffers. + .....................................................................*/ + static ECBType *ListenECB; + static IPXHeaderType *ListenHeader; + static char *ListenBuf; + + /*..................................................................... + Variables for Sending (created by the IPXManagerClass, and passed + in via Init). All IPX connections share these buffers. + .....................................................................*/ + static ECBType *SendECB; + static IPXHeaderType *SendHeader; + static char *SendBuf; + + /*..................................................................... + This is a REAL-MODE pointer to the event-service-routine for IPX. + If it's 0, IPX will operate in polled mode. Otherwise, the high word + must contain the segment, and the low word must contain the offset. + CS will be the high word value when the routine is called. (Requiring + the segment/offset to be computed by the caller gives the caller + control over CS.) + .....................................................................*/ + static long Handler; + + /*..................................................................... + This status flag tells us if Configure() has been called or not. + .....................................................................*/ + static int Configured; + + /*..................................................................... + This status flag tells us if the socket has been opened or not. + .....................................................................*/ + static int SocketOpen; + + /*..................................................................... + This status flag tells us if Start_Listening() has been called or not. + .....................................................................*/ + static int Listening; +}; + +#endif diff --git a/IPXGCONN.CPP b/IPXGCONN.CPP new file mode 100644 index 0000000..39ee31d --- /dev/null +++ b/IPXGCONN.CPP @@ -0,0 +1,473 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\ipxgconn.cpv 1.9 16 Oct 1995 16:51:00 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXGCONN.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : July 6, 1995 [BRR] * + *-------------------------------------------------------------------------* + * Functions: * + * IPXGlobalConnClass::IPXGlobalConnClass -- class constructor * + * IPXGlobalConnClass::~IPXGlobalConnClass -- class destructor * + * IPXGlobalConnClass::Send_Packet -- adds a packet to the send queue * + * IPXGlobalConnClass::Receive_Packet -- adds packet to the receive queue* + * IPXGlobalConnClass::Get_Packet -- gets a packet from the receive queue* + * IPXGlobalConnClass::Send -- sends a packet * + * IPXGlobalConnClass::Service_Receive_Queue -- services recieve queue * + * IPXGlobalConnClass::Set_Bridge -- Sets up connection to cross a bridge* +* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * IPXGlobalConnClass::IPXGlobalConnClass -- class constructor * + * * + * This routine chains to the parent constructor, but it adjusts the size * + * of the packet by the added bytes in the GlobalHeaderType structure. * + * This forces the parent classes to allocate the proper sized PacketBuf * + * for outgoing packets, and to set MaxPacketLen to the proper value. * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the recieve queue * + * maxlen max length of an application packet * + * product_id unique ID for this product * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +IPXGlobalConnClass::IPXGlobalConnClass (int numsend, int numreceive, int maxlen, + unsigned short product_id) : + IPXConnClass (numsend, numreceive, + maxlen + sizeof(GlobalHeaderType) - sizeof(CommHeaderType), + GLOBAL_MAGICNUM, // magic number for this connection + NULL, // IPX Address (none) + 0, // Connection ID + "") // Connection Name +{ + ProductID = product_id; + IsBridge = 0; + +} /* end of IPXGlobalConnClass */ + + +/*************************************************************************** + * IPXGlobalConnClass::Send_Packet -- adds a packet to the send queue * + * * + * This routine prefixes the given buffer with a GlobalHeaderType and * + * queues the resulting packet into the Send Queue. The packet's * + * MagicNumber, Code, PacketID, destination Address and ProductID are set * + * here. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * address address to send the packet to (NULL = Broadcast) * + * ack_req true = ACK is required for this packet; false = isn't * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXGlobalConnClass::Send_Packet (void * buf, int buflen, + IPXAddressClass *address, int ack_req) +{ + /*------------------------------------------------------------------------ + Store the packet's Magic Number + ------------------------------------------------------------------------*/ + ((GlobalHeaderType *)PacketBuf)->Header.MagicNumber = MagicNum; + + /*------------------------------------------------------------------------ + If this is a ACK-required packet, sent to a specific system, mark it as + ACK-required; otherwise, mark as no-ACK-required. + ------------------------------------------------------------------------*/ + if (ack_req && address != NULL) { + ((GlobalHeaderType *)PacketBuf)->Header.Code = PACKET_DATA_ACK; + } else { + ((GlobalHeaderType *)PacketBuf)->Header.Code = PACKET_DATA_NOACK; + } + + /*------------------------------------------------------------------------ + Fill in the packet ID. This will have very limited meaning; it only + allows us to determine if an ACK packet we receive later goes with this + packet; it doesn't let us detect re-sends of other systems' packets. + ------------------------------------------------------------------------*/ + ((GlobalHeaderType *)PacketBuf)->Header.PacketID = Queue->Send_Total(); + + /*------------------------------------------------------------------------ + Set the product ID for this packet. + ------------------------------------------------------------------------*/ + ((GlobalHeaderType *)PacketBuf)->ProductID = ProductID; + + /*------------------------------------------------------------------------ + Set this packet's destination address. If no address is specified, use + a Broadcast address (which IPXAddressClass's default constructor creates). + ------------------------------------------------------------------------*/ + if (address != NULL) { + ((GlobalHeaderType *)PacketBuf)->Address = (*address); + } else { + ((GlobalHeaderType *)PacketBuf)->Address = IPXAddressClass(); + } + + /*------------------------------------------------------------------------ + Copy the application's data + ------------------------------------------------------------------------*/ + memcpy(PacketBuf + sizeof(GlobalHeaderType), buf, buflen); + + /*------------------------------------------------------------------------ + Queue it + ------------------------------------------------------------------------*/ + return(Queue->Queue_Send(PacketBuf,buflen + sizeof(GlobalHeaderType))); + +} /* end of Send_Packet */ + + +/*************************************************************************** + * IPXGlobalConnClass::Receive_Packet -- adds packet to the receive queue * + * * + * INPUT: * + * buf buffer to process (already includes GlobalHeaderType) * + * buflen length of buffer to process * + * address the address of the sender (the IPX Manager class must * + * extract this from the IPX Header of the received packet.) * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXGlobalConnClass::Receive_Packet (void * buf, int buflen, + IPXAddressClass *address) +{ + GlobalHeaderType *packet; // ptr to this packet + SendQueueType *send_entry; // ptr to send entry header + GlobalHeaderType *entry_data; // ptr to queue entry data + int i; + + /* + --------------------------- Check the magic # ---------------------------- + */ + packet = (GlobalHeaderType *)buf; + if (packet->Header.MagicNumber!=MagicNum) { + return(false); + } + + /*------------------------------------------------------------------------ + Process the packet based on its Code + ------------------------------------------------------------------------*/ + switch (packet->Header.Code) { + /*..................................................................... + DATA: Save the given address in the message buffer (so Get_Message() + can extract it later), and queue this message. + Don't bother checking for a Re-Send; since this queue is receiving data + from multiple systems, the Total_Receive() value for this queue will + have nothing to do with the packet's ID. The application must deal + with this by being able to handle multiple receipts of the same packet. + .....................................................................*/ + case PACKET_DATA_ACK: + case PACKET_DATA_NOACK: + packet->Address = (*address); + Queue->Queue_Receive (buf, buflen); + break; + + /*..................................................................... + ACK: If this ACK is for any of my packets, mark that packet as + acknowledged, then throw this packet away. Otherwise, ignore the ACK + (if we re-sent before we received the other system's first ACK, this + ACK will be a leftover) + .....................................................................*/ + case PACKET_ACK: + for (i = 0; i < Queue->Num_Send(); i++) { + /* + ..................... Get queue entry ptr ....................... + */ + send_entry = Queue->Get_Send(i); + /* + ............. If ptr is valid, get ptr to its data .............. + */ + entry_data = (GlobalHeaderType *)(send_entry->Buffer); + /* + .............. If ACK is for this entry, mark it ................ + */ + if (packet->Header.PacketID==entry_data->Header.PacketID && + entry_data->Header.Code == PACKET_DATA_ACK) { + send_entry->IsACK = 1; + break; + } + } + break; + + /*..................................................................... + Default: ignore the packet + .....................................................................*/ + default: + break; + + } /* end of switch */ + + return(true); +} + + +/*************************************************************************** + * IPXGlobalConnClass::Get_Packet -- gets a packet from the receive queue * + * * + * INPUT: * + * buf location to store buffer * + * buflen filled in with length of 'buf' * + * address filled in with sender's address * + * product_id filled in with sender's ProductID * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXGlobalConnClass::Get_Packet (void * buf, int *buflen, + IPXAddressClass *address, unsigned short *product_id) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + GlobalHeaderType *packet; + int packetlen; // size of received packet + + /* + ------------------------ Return if nothing to do ------------------------- + */ + if (Queue->Num_Receive() == 0) { + return(false); + } + + /* + ------------------ Get ptr to the next available entry ------------------- + */ + rec_entry = Queue->Get_Receive(0); + + /* + ------------------------ Read it if it's un-read ------------------------- + */ + if (rec_entry!=NULL && rec_entry->IsRead==0) { + /* + ........................... Mark as read .............................. + */ + rec_entry->IsRead = 1; + + /* + .......................... Copy data packet ........................... + */ + packet = (GlobalHeaderType *)(rec_entry->Buffer); + packetlen = rec_entry->BufLen - sizeof(GlobalHeaderType); + if (packetlen > 0) + memcpy(buf, rec_entry->Buffer + sizeof(GlobalHeaderType), packetlen); + (*buflen) = packetlen; + (*address) = packet->Address; + (*product_id) = packet->ProductID; + + return(true); + } + + return(false); +} + + +/*************************************************************************** + * IPXGlobalConnClass::Send -- sends a packet * + * * + * This routine gets invoked by NonSequencedConn, when it's processing * + * the Send & Receive Queues. The buffer provided will already have the * + * GlobalHeaderType header embedded in it. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXGlobalConnClass::Send(char *buf, int buflen) +{ + IPXAddressClass *addr; + int rc; + + /*------------------------------------------------------------------------ + Extract the packet's embedded IPX address + ------------------------------------------------------------------------*/ + addr = &(((GlobalHeaderType *)buf)->Address); + + /*------------------------------------------------------------------------ + If it's a broadcast address, broadcast it + ------------------------------------------------------------------------*/ + if (addr->Is_Broadcast()) { + return(Broadcast (buf, buflen)); + } else { + /*------------------------------------------------------------------------ + Otherwise, send it + ------------------------------------------------------------------------*/ + if (IsBridge && !memcmp (addr, BridgeNet, 4)) { + rc = Send_To (buf, buflen, &(((GlobalHeaderType *)buf)->Address), + BridgeNode); + } else { + rc = Send_To (buf, buflen, &(((GlobalHeaderType *)buf)->Address), NULL); + } + return (rc); + } + +} /* end of Send */ + + +/*************************************************************************** + * IPXGlobalConnClass::Service_Receive_Queue -- services the recieve queue * + * * + * This routine is necessary because the Global Connection has to ACK * + * a packet differently from other types of connections; its Send routine * + * assumes that the destination address is embedded within the outgoing * + * packet, so we have to create our ACK Packet using the GlobalHeaderType, * + * not the CommHeaderType. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXGlobalConnClass::Service_Receive_Queue (void) +{ + GlobalHeaderType ackpacket; // ACK packet to send + ReceiveQueueType *rec_entry; // ptr to receive entry header + GlobalHeaderType *packet_hdr; // packet header + + /*------------------------------------------------------------------------ + Get a pointer to the next received entry + ------------------------------------------------------------------------*/ + rec_entry = Queue->Get_Receive(0); + if (rec_entry==NULL) + return(1); + + /*------------------------------------------------------------------------ + If this packet doesn't require an ACK, mark it as ACK'd. + ------------------------------------------------------------------------*/ + packet_hdr = (GlobalHeaderType *)(rec_entry->Buffer); + if (packet_hdr->Header.Code==PACKET_DATA_NOACK) + rec_entry->IsACK = 1; + + /*------------------------------------------------------------------------ + If this packet hasn't been ACK'd, send an ACK: + - Fill in the MagicNum & the Code + - Set the PacketID to the same ID that the sending system used, so the + sending system knows which packet the ACK is for + ------------------------------------------------------------------------*/ + if (rec_entry->IsACK==0) { + ackpacket.Header.MagicNumber = MagicNum; + ackpacket.Header.Code = PACKET_ACK; + ackpacket.Header.PacketID = packet_hdr->Header.PacketID; + ackpacket.Address = packet_hdr->Address; + ackpacket.ProductID = ProductID; + + Send ((char *)&ackpacket, sizeof(GlobalHeaderType)); + + rec_entry->IsACK = 1; + } + + /*------------------------------------------------------------------------ + If this packet has been read by the application, and has been ACK'd, and + there is another packet in the queue behind this one, it means the other + system got the ACK we sent for this packet; remove this packet from the + queue. + ------------------------------------------------------------------------*/ + if (rec_entry!=NULL && rec_entry->IsRead && rec_entry->IsACK && + Queue->Num_Receive() > 1) + Queue->UnQueue_Receive(NULL,NULL,0); + + return(1); + +} /* end of Service_Receive_Queue */ + +/*************************************************************************** + * Set_Bridge -- Sets up connection to cross a bridge * + * * + * This routine is designed to prevent the connection from having to * + * call Get_Local_Target, except the minimum number of times, since that * + * routine is buggy & goes away for long periods sometimes. * + * * + * INPUT: * + * bridge network number of the destination bridge * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 07/06/1995 BRR : Created. * + *=========================================================================*/ +void IPXGlobalConnClass::Set_Bridge(NetNumType bridge) +{ + if (Configured) { + memcpy (BridgeNet, bridge, 4); + memset (BridgeNode, 0xff, 6); + + if (IPX_Get_Local_Target (BridgeNet, BridgeNode, Socket, BridgeNode)==0) { + IsBridge = 1; + } else { + IsBridge = 0; + } + } +} + diff --git a/IPXGCONN.H b/IPXGCONN.H new file mode 100644 index 0000000..1266140 --- /dev/null +++ b/IPXGCONN.H @@ -0,0 +1,161 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\ipxgconn.h_v 1.10 16 Oct 1995 16:47:30 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXGCONN.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 11, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This class is a special type of IPX Connection. It can talk to more * + * than one system at a time. It can Broadcast packets to all systems, * + * or send a packet to one individual system. The packets it sends to * + * individual systems can be DATA_NOACK or DATA_ACK packets, but the * + * packets broadcast have to be DATA_NOACK packets. This class is for * + * only the crudest "Who-are-you" type of network communications. Once * + * the IPX Address of another system is identified, a "real" IPX * + * Connection should be created, & further communications done through it. * + * * + * This means that the packet ID field no longer can be used to detect * + * resends, since the receive queue may recieve a lot more packets than * + * we send out. So, re-sends cannot be detected; the application must be * + * designed so that it can handle multiple copies of the same packet. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IPXGLOBALCONN_H +#define IPXGLOBALCONN_H + + +#include "ipxconn.h" + + +/* +********************************** Defines ********************************** +*/ +/*--------------------------------------------------------------------------- +This is the header for Global Connection messages. It includes the usual +"standard" header that the other connections do; but it also includes an +IPX address field, so the application can get the address of the sender +of this message. This address field must be provided in by the IXP +Connection Manager class, when it calls this class's Receive_Packet function. +---------------------------------------------------------------------------*/ +typedef struct { + CommHeaderType Header; + IPXAddressClass Address; + unsigned short ProductID; +} GlobalHeaderType; + + +/* +***************************** Class Declaration ***************************** +*/ +class IPXGlobalConnClass : public IPXConnClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Here are some useful enums: + .....................................................................*/ + enum GlobalConnectionEnum { + /*.................................................................. + This is the magic number for all Global Connections. Having the + same magic number across products lets us ID different products + on the net. + ..................................................................*/ + GLOBAL_MAGICNUM = 0x1234, + /*.................................................................. + These are the values used for the ProductID field in the Global + Message structure. It also should be the Magic Number used for the + private connections within that product. + This list should be continually updated & kept current. Never ever + ever use an old product ID for your product! + ..................................................................*/ + COMMAND_AND_CONQUER = 0xaa01, + }; + + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + IPXGlobalConnClass (int numsend, int numrecieve, int maxlen, + unsigned short product_id); + virtual ~IPXGlobalConnClass () {}; + + /*..................................................................... + Send/Receive routines. + .....................................................................*/ + virtual int Send_Packet (void * buf, int buflen, + IPXAddressClass *address, int ack_req); + virtual int Receive_Packet (void * buf, int buflen, + IPXAddressClass *address); + virtual int Get_Packet (void * buf, int *buflen, + IPXAddressClass *address, unsigned short *product_id); + + /*..................................................................... + This is for telling the connection it can cross a bridge. + .....................................................................*/ + void Set_Bridge (NetNumType bridge); + + /*..................................................................... + The Product ID for this product. + .....................................................................*/ + unsigned short ProductID; + + /*..................................................................... + This describes the address of a bridge we have to cross. This class + supports crossing only one bridge. Storing the bridge's network number + allows us to obtain its local target address only once, then re-use it. + .....................................................................*/ + NetNumType BridgeNet; + NetNodeType BridgeNode; + int IsBridge; + + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + + /*..................................................................... + This is the overloaded Send routine declared in ConnectionClass, and + used in SequencedConnClass. This special version sends to the address + embedded within the GlobalHeaderType. + .....................................................................*/ + virtual int Send (char *buf, int buflen); + + /*..................................................................... + This routine is overloaded from SequencedConnClass, because the + Global Connection needs to ACK its packets differently from the + other connections. + .....................................................................*/ + virtual int Service_Receive_Queue (void); +}; + +#endif diff --git a/IPXMGR.CPP b/IPXMGR.CPP new file mode 100644 index 0000000..93d8a4e --- /dev/null +++ b/IPXMGR.CPP @@ -0,0 +1,1972 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\ipxmgr.cpv 1.9 16 Oct 1995 16:48:22 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXMGR.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : May 4, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * IPXManagerClass::IPXManagerClass -- class constructor * + * IPXManagerClass::~IPXManagerClass -- class destructor * + * IPXManagerClass::Init -- initialization routine * + * IPXManagerClass::Is_IPX -- tells if IPX is installed or not * + * IPXManagerClass::Set_Timing -- sets timing for all connections * + * IPXManagerClass::Create_Connection -- creates a new connection * + * IPXManagerClass::Delete_Connection -- deletes a connection * + * IPXManagerClass::Num_Connections -- gets the # of connections * + * IPXManagerClass::Connection_ID -- gets the given connection's ID * + * IPXManagerClass::Connection_Name -- gets name for given connection * + * IPXManagerClass::Connection_Address -- retrieves connection's address * + * IPXManagerClass::Connection_Index -- gets given connection's index * + * IPXManagerClass::Send_Global_Message -- sends a Global Message * + * IPXManagerClass::Get_Global_Message -- polls the Global Message queue * + * IPXManagerClass::Send_Private_Message -- Sends a Private Message * + * IPXManagerClass::Get_Private_Message -- Polls Private Message queue * + * IPXManagerClass::Service -- main polling routine for IPX Connections * + * IPXManagerClass::Get_Bad_Connection -- returns bad connection ID * + * IPXManagerClass::Global_Num_Send -- gets # entries in send queue * + * IPXManagerClass::Global_Num_Receive -- gets # entries in recv queue * + * IPXManagerClass::Private_Num_Send -- gets # entries in send queue * + * IPXManagerClass::Private_Num_Receive -- gets # entries in recv queue * + * IPXManagerClass::Set_Bridge -- prepares to cross a bridge * + * IPXManagerClass::Set_Socket -- sets socket ID for all connections * + * IPXManagerClass::Response_Time -- Returns largest Avg Response Time * + * IPXManagerClass::Global_Response_Time -- Returns Avg Response Time * + * IPXManagerClass::Reset_Response_Time -- Reset response time * + * IPXManagerClass::Oldest_Send -- gets ptr to oldest send buf * + * IPXManagerClass::Mono_Debug_Print -- debug output routine * + * IPXManagerClass::Alloc_RealMode_Mem -- allocates real-mode memory * + * IPXManagerClass::Free_RealMode_Mem -- frees real-mode memory * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "ipx95.h" +#include "tcpip.h" + +/*************************************************************************** + * IPXManagerClass::IPXManagerClass -- class constructor * + * * + * INPUT: * + * glb_maxlen Global Channel maximum packet length * + * pvt_maxlen Private Channel maximum packet length * + * socket socket ID to use * + * product_id a unique numerical ID for this product * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * The socket number is byte-swapped, since IPX requires socket ID's * + * to be stored high/low. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +IPXManagerClass::IPXManagerClass (int glb_maxlen, int pvt_maxlen, + int glb_num_packets, int pvt_num_packets, unsigned short socket, + unsigned short product_id) +{ + int i; + + /*------------------------------------------------------------------------ + Initialize data members + ------------------------------------------------------------------------*/ + /*........................................................................ + IPXStatus = 1 if IPX is installed, 0 if not + ........................................................................*/ + if (IPX_SPX_Installed()==0) { + IPXStatus = 0; + } else { + IPXStatus = 1; + } + + /*........................................................................ + Set listening state flag to off + ........................................................................*/ + Listening = 0; + + /*........................................................................ + No memory has been alloc'd yet + ........................................................................*/ + RealMemAllocd = 0; + + /*........................................................................ + Set max packet sizes, for allocating real-mode memory + ........................................................................*/ + Glb_MaxPacketLen = glb_maxlen; + Glb_NumPackets = glb_num_packets; + Pvt_MaxPacketLen = pvt_maxlen; + Pvt_NumPackets = pvt_num_packets; + + /*........................................................................ + Save the app's product ID + ........................................................................*/ + ProductID = product_id; + + /*........................................................................ + Save our socket ID number + ........................................................................*/ + Socket = (unsigned short)( (((unsigned long)socket & 0x00ff) << 8) | + (((unsigned long)socket & 0xff00) >> 8)); + + /*........................................................................ + Get the user's IPX local connection number + ........................................................................*/ + if (IPXStatus) { + ConnectionNum = IPX_Get_Connection_Number(); + } else { + ConnectionNum = 0; + } + + /*........................................................................ + Init connection states + ........................................................................*/ + NumConnections = 0; + CurConnection = 0; + for (i = 0; i < CONNECT_MAX; i++) + Connection[i] = 0; + GlobalChannel = 0; + + SendOverflows = 0; + ReceiveOverflows = 0; + BadConnection = IPXConnClass::CONNECTION_NONE; + + /*........................................................................ + Init timing parameters + ........................................................................*/ + RetryDelta = 2; // 2 ticks between retries + MaxRetries = -1; // disregard # retries + Timeout = 60; // report bad connection after 1 second + +} /* end of IPXManagerClass */ + + +/*************************************************************************** + * IPXManagerClass::~IPXManagerClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +IPXManagerClass::~IPXManagerClass() +{ + int i; + + /*------------------------------------------------------------------------ + Stop all IPX events + ------------------------------------------------------------------------*/ + if (Listening) { + IPXConnClass::Stop_Listening(); + Listening = 0; + } + + /*------------------------------------------------------------------------ + Free all protected-mode memory + ------------------------------------------------------------------------*/ + if (GlobalChannel) { + delete GlobalChannel; + GlobalChannel = 0; + } + for (i = 0; i < NumConnections; i++) { + delete Connection[i]; + Connection[i] = 0; + } + NumConnections = 0; + + /*------------------------------------------------------------------------ + Free all real-mode memory + ------------------------------------------------------------------------*/ + if (RealMemAllocd) { + Free_RealMode_Mem(); + RealMemAllocd = 0; + } + +} /* end of ~IPXManagerClass */ + + +/*************************************************************************** + * IPXManagerClass::Init -- initialization routine * + * * + * This routine allocates memory, + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Init() +{ + int i; + + if (!(GameToPlay == GAME_INTERNET)){ + /* + ----------------------- Error if IPX not installed ----------------------- + */ + if (!IPXStatus) { + return(false); + } + + /*------------------------------------------------------------------------ + Stop Listening + ------------------------------------------------------------------------*/ + if (Listening) { + IPXConnClass::Stop_Listening(); + Listening = 0; + } + + /*------------------------------------------------------------------------ + Free Real-mode memory + ------------------------------------------------------------------------*/ + if (RealMemAllocd) { + Free_RealMode_Mem(); + RealMemAllocd = 0; + } + }else{ + /* + ** Pretend IPX is available for Internet games whether it is or not + */ + IPXStatus = 1; + } + + /*------------------------------------------------------------------------ + Free protected-mode memory + ------------------------------------------------------------------------*/ + if (GlobalChannel) { + delete GlobalChannel; + GlobalChannel = 0; + } + for (i = 0; i < NumConnections; i++) { + delete Connection[i]; + Connection[i] = 0; + } + NumConnections = 0; + + + if (!(GameToPlay == GAME_INTERNET)){ + /*------------------------------------------------------------------------ + Allocate real-mode memory + ------------------------------------------------------------------------*/ + if (!Alloc_RealMode_Mem()) return(false); + RealMemAllocd = 1; + } + + /*------------------------------------------------------------------------ + Allocate the Global Channel + ------------------------------------------------------------------------*/ + GlobalChannel = new IPXGlobalConnClass (Glb_NumPackets, Glb_NumPackets, + Glb_MaxPacketLen, ProductID); + if (!GlobalChannel) + return(false); + GlobalChannel->Init(); + GlobalChannel->Set_Retry_Delta (RetryDelta); + GlobalChannel->Set_Max_Retries (MaxRetries); + GlobalChannel->Set_TimeOut (Timeout); + + /*------------------------------------------------------------------------ + Configure the IPX Connections + ------------------------------------------------------------------------*/ + IPXConnClass::Configure(Socket, ConnectionNum, ListenECB, SendECB, + FirstHeaderBuf, SendHeader, FirstDataBuf, SendBuf, Handler, PacketLen); + + /*------------------------------------------------------------------------ + Start Listening + ------------------------------------------------------------------------*/ + if (!(GameToPlay == GAME_INTERNET)){ + if (!IPXConnClass::Start_Listening()) return(false); + } + + Listening = 1; + + return(true); +} + + +/*************************************************************************** + * IPXManagerClass::Is_IPX -- tells if IPX is installed or not * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = IPX is installed; 0 = isn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Is_IPX(void) +{ + return(IPXStatus); + +} /* end of Is_IPX */ + + +/*************************************************************************** + * IPXManagerClass::Set_Timing -- sets timing for all connections * + * * + * This will set the timing parameters for all existing connections, and * + * all connections created from now on. This allows an application to * + * measure the Response_Time while running, and adjust timing accordingly. * + * * + * INPUT: * + * retrydelta value to set for retry delta * + * maxretries value to set for max # retries * + * timeout value to set for connection timeout * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/02/1995 BR : Created. * + *=========================================================================*/ +void IPXManagerClass::Set_Timing (unsigned long retrydelta, + unsigned long maxretries, unsigned long timeout) +{ + int i; + + RetryDelta = retrydelta; + MaxRetries = maxretries; + Timeout = timeout; + + if (GlobalChannel) { + GlobalChannel->Set_Retry_Delta (RetryDelta); + GlobalChannel->Set_Max_Retries (MaxRetries); + GlobalChannel->Set_TimeOut (Timeout); + } + + for (i = 0; i < NumConnections; i++) { + Connection[i]->Set_Retry_Delta (RetryDelta); + Connection[i]->Set_Max_Retries (MaxRetries); + Connection[i]->Set_TimeOut (Timeout); + } + +} /* end of Set_Timing */ + + +/*************************************************************************** + * IPXManagerClass::Create_Connection -- creates a new connection * + * * + * INPUT: * + * id application-specific numerical ID for this connection * + * node ptr to IPXNodeIDType (name & address) * + * address IPX address for this connection * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * Never create a connection with an 'id' of -1. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +bool IPXManagerClass::Create_Connection(int id, char *name, IPXAddressClass *address) +{ +#if (0) + char temp[80]; + + NetNumType num; + NetNodeType node; + + address->Get_Address (num, node); + + sprintf (temp, "Address:%02x %02x %02x %02x %02x %02x\n", node[0], + node[1], + node[2], + node[3], + node[4], + node[5]); + CCDebugString (temp); + + sprintf (temp, "Network:%02x %02x %02x %02x\n", num[0], + num[1], + num[2], + num[3]); + CCDebugString (temp); +#endif //(0) + + /* + ----------------------- Error if IPX not installed ----------------------- + */ + if (!IPXStatus) + return(false); + + /* + ------------------------- Error if no more room -------------------------- + */ + if (NumConnections==CONNECT_MAX) + return(false); + + /* + ------------------------- Create new connection -------------------------- + */ + Connection[NumConnections] = new IPXConnClass(Pvt_NumPackets, + Pvt_NumPackets, Pvt_MaxPacketLen, ProductID, address, id, name); + if (!Connection[NumConnections]) + return(false); + + Connection[NumConnections]->Init (); + Connection[NumConnections]->Set_Retry_Delta (RetryDelta); + Connection[NumConnections]->Set_Max_Retries (MaxRetries); + Connection[NumConnections]->Set_TimeOut (Timeout); + + NumConnections++; + + return(true); + +} /* end of Create_Connection */ + + +/*************************************************************************** + * IPXManagerClass::Delete_Connection -- deletes a connection * + * * + * INPUT: * + * id ID of connection to delete * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +bool IPXManagerClass::Delete_Connection(int id) +{ + int i,j; + + /* + ----------------------- Error if IPX not installed ----------------------- + */ + if (!IPXStatus) + return(false); + + /* + ------------------- Error if no connections to delete -------------------- + */ + if (NumConnections==0) + return(false); + + /* + ---------------------- Loop through all connections ---------------------- + */ + for (i = 0; i < NumConnections; i++) { + /* + ........................ If a match, delete it ........................ + */ + if (Connection[i]->ID==id) { + delete Connection[i]; + /* + ................ Move array elements back one index ................ + */ + for (j = i; j < NumConnections - 1; j++) { + Connection[j] = Connection[j+1]; + } + /* + ......................... Adjust counters .......................... + */ + NumConnections--; + if (CurConnection >= NumConnections) + CurConnection = 0; + return(true); + } + } + + /* + ---------------------------- No match; error ----------------------------- + */ + return(false); + +} /* end of Delete_Connection */ + + +/*************************************************************************** + * IPXManagerClass::Num_Connections -- gets the # of connections * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # of connections * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Num_Connections(void) +{ +#ifdef VIRTUAL_SUBNET_SERVER + /* + ** If we are connected to the VSS then dont coumt that in the number of connections. + */ + if (Connection_Index (VSS_ID) == IPXConnClass::CONNECTION_NONE){ + return(NumConnections); + }else{ + return(NumConnections -1); + } +#else // VIRTUAL_SUBNET_SERVER + + return(NumConnections); + +#endif //VIRTUAL_SUBNET_SERVER + +} /* end of Num_Connections */ + + +/*************************************************************************** + * IPXManagerClass::Connection_ID -- gets the given connection's ID * + * * + * INPUT: * + * index index of connection to retrieve * + * * + * OUTPUT: * + * ID for that connection, CONNECTION_NONE if invalid index * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Connection_ID(int index) +{ + if (index >= 0 && index < NumConnections) { + return(Connection[index]->ID); + } else { + return(IPXConnClass::CONNECTION_NONE); + } +} + + +/*************************************************************************** + * IPXManagerClass::Connection_Name -- retrieves name for given connection * + * * + * INPUT: * + * id ID of connection to get name of * + * * + * OUTPUT: * + * ptr to connection's name, NULL if not found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +char *IPXManagerClass::Connection_Name(int id) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->ID==id) { + return(Connection[i]->Name); + } + } + + return(NULL); + +} /* end of Connection_Name */ + + +/*************************************************************************** + * IPXManagerClass::Connection_Address -- retrieves connection's address * + * * + * INPUT: * + * id ID of connection to get address for * + * * + * OUTPUT: * + * pointer to IXPAddressClass, NULL if not found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +IPXAddressClass * IPXManagerClass::Connection_Address(int id) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->ID==id) { + return(&Connection[i]->Address); + } + } + + return(NULL); + +} /* end of Connection_Address */ + + +/*************************************************************************** + * IPXManagerClass::Connection_Index -- gets given connection's index * + * * + * INPUT: * + * ID to retrieve index for * + * * + * OUTPUT: * + * index for this connection, CONNECTION_NONE if not found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Connection_Index(int id) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->ID==id) + return(i); + } + + return(IPXConnClass::CONNECTION_NONE); + +} /* end of Connection_Index */ + + +/*************************************************************************** + * IPXManagerClass::Send_Global_Message -- sends a Global Message * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buf * + * ack_req 1 = ACK required; 0 = no ACK required * + * address address to send to; NULL = broadcast * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Send_Global_Message(void *buf, int buflen, + int ack_req, IPXAddressClass *address) +{ + int rc; + + /* + ------------ Error if IPX not installed or not Listening ----------------- + */ + if (!IPXStatus || !Listening) return(false); + + rc = GlobalChannel->Send_Packet (buf, buflen, address, ack_req); + if (!rc) + SendOverflows++; + + return(rc); +} + + +/*************************************************************************** + * IPXManagerClass::Get_Global_Message -- polls the Global Message queue * + * * + * INPUT: * + * buf buffer to store received packet * + * buflen length of data placed in 'buf' * + * address IPX address of sender * + * product_id product ID of sender * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Get_Global_Message(void *buf, int *buflen, + IPXAddressClass *address, unsigned short *product_id) +{ + /* + ------------ Error if IPX not installed or not Listening ----------------- + */ + if (!IPXStatus || !Listening) return(false); + + return(GlobalChannel->Get_Packet (buf, buflen, address, product_id)); +} + + +/*************************************************************************** + * IPXManagerClass::Send_Private_Message -- Sends a Private Message * + * * + * INPUT: * + * buf buffer to send * + * buflen length of 'buf' * + * conn_id connection ID to send to (CONNECTION_NONE = all) * + * ack_req 1 = ACK required; 0 = no ACK required * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Send_Private_Message(void *buf, int buflen, int ack_req, + int conn_id) +{ + int i; // loop counter + int connect_idx; // index of channel to send to, if specified + + /* + ------------ Error if IPX not installed or not Listening ----------------- + */ + if (!IPXStatus || !Listening || (NumConnections==0)) return(false); + + /*------------------------------------------------------------------------ + Send the message to all connections + ------------------------------------------------------------------------*/ + if (conn_id==IPXConnClass::CONNECTION_NONE) { + + /* + ** If this is an internet game and no ack is reqired then we only need to send + ** the packet to the VSS and it will forward it to all the other players. + */ + +#ifdef VIRTUAL_SUBNET_SERVER + if (ack_req || (!Winsock.Get_Connected() || !UseVirtualSubnetServer)){ +#endif //VIRTUAL_SUBNET_SERVER + /*..................................................................... + Check for room in all connections + .....................................................................*/ + for (i = 0; i < NumConnections; i++) { +#ifdef VIRTUAL_SUBNET_SERVER + if (Connection[i]->ID != VSS_ID){ +#endif //VIRTUAL_SUBNET_SERVER + if (Connection[i]->Queue->Num_Send()==Connection[i]->Queue->Max_Send()) { + SendOverflows++; + return(false); + } +#ifdef VIRTUAL_SUBNET_SERVER + } +#endif // VIRTUAL_SUBNET_SERVER + } + + /*..................................................................... + Send packet to all connections + .....................................................................*/ + + for (i = 0; i < NumConnections; i++) { +#ifdef VIRTUAL_SUBNET_SERVER + if (Connection[i]->ID != VSS_ID){ +#endif //VIRTUAL_SUBNET_SERVER + Connection[i]->Send_Packet (buf, buflen, ack_req); +#ifdef VIRTUAL_SUBNET_SERVER + } +#endif //VIRTUAL_SUBNET_SERVER + } + +#ifdef VIRTUAL_SUBNET_SERVER + }else{ + /* + ** Send the packet to the VSS with a 0 header so it gets forwarded to all players. + */ + if (Connection[ Connection_Index(VSS_ID) ]->Queue->Num_Send() == Connection[ Connection_Index(VSS_ID) ]->Queue->Max_Send()) { + SendOverflows++; + return(false); + } + Connection[ Connection_Index(VSS_ID) ]->Send_Packet (buf, buflen, 0); + } +#endif // VIRTUAL_SUBNET_SERVER + + return(true); + + + } else { + + /*------------------------------------------------------------------------ + Send the message to the specified connection + ------------------------------------------------------------------------*/ + connect_idx = Connection_Index (conn_id); + if (connect_idx == IPXConnClass::CONNECTION_NONE) { + SendOverflows++; + return(false); + } + + /*..................................................................... + Check for room in the connection + .....................................................................*/ + if (Connection[connect_idx]->Queue->Num_Send() == + Connection[connect_idx]->Queue->Max_Send()) { + SendOverflows++; + return(false); + } + + /*..................................................................... + Send the packet to that connection + .....................................................................*/ + Connection[connect_idx]->Send_Packet (buf, buflen, ack_req); + return(true); + } +} + + +/*************************************************************************** + * IPXManagerClass::Get_Private_Message -- Polls the Private Message queue * + * * + * INPUT: * + * buf buffer to store incoming packet * + * buflen length of data placed in 'buf' * + * conn_id filled in with connection ID of sender * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Get_Private_Message(void *buf, int *buflen, int *conn_id) +{ + int i; + int rc; + int c_id; +#ifdef VIRTUAL_SUBNET_SERVER + int vss = 0; +#endif // VIRTUAL_SUBNET_SERVER + + /* + ------------ Error if IPX not installed or not Listening ----------------- + */ + if (!IPXStatus || !Listening || (NumConnections==0)) return(false); + +#ifdef VIRTUAL_SUBNET_SERVER + if (Winsock.Get_Connected()){ + vss = (int) UseVirtualSubnetServer; + } +#endif //VIRTUAL_SUBNET_SERVER + + /*------------------------------------------------------------------------ + Safety check: ensure CurConnection is in range. + ------------------------------------------------------------------------*/ +#ifdef VIRTUAL_SUBNET_SERVER + if (CurConnection >= NumConnections - vss) +#else // VIRTUAL_SUBNET_SERVER + if (CurConnection >= NumConnections) +#endif //VIRTUAL_SUBNET_SERVER + CurConnection = 0; + + /*------------------------------------------------------------------------ + Scan all connections for a received packet, starting with 'CurConnection' + ------------------------------------------------------------------------*/ +#ifdef VIRTUAL_SUBNET_SERVER + for (i = 0; i < NumConnections - vss; i++) { +#else // VIRTUAL_SUBNET_SERVER + for (i = 0; i < NumConnections; i++) { +#endif // VIRTUAL_SUBNET_SERVER + /*..................................................................... + Check this connection for a packet + .....................................................................*/ + rc = Connection[CurConnection]->Get_Packet (buf, buflen); + c_id = Connection[CurConnection]->ID; + + /*..................................................................... + Increment CurConnection to the next connection index + .....................................................................*/ + CurConnection++; +#ifdef VIRTUAL_SUBNET_SERVER + if (CurConnection >= NumConnections - vss) +#else // VIRTUAL_SUBNET_SERVER + if (CurConnection >= NumConnections) +#endif // VIRTUAL_SUBNET_SERVER + CurConnection = 0; + + /*..................................................................... + If we got a packet, return the connection ID + .....................................................................*/ + if (rc) { + (*conn_id) = c_id; + return(true); + } + } + + return(false); +} + + +/*************************************************************************** + * IPXManagerClass::Service -- main polling routine for IPX Connections * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Service(void) +{ + + int rc = 1; + int i; + CommHeaderType *packet; + int packetlen; + IPXAddressClass address; + + + +#ifndef NOT_FOR_WIN95 + + unsigned char temp_receive_buffer[1024]; + int recv_length; + + if (Winsock.Get_Connected()){ + + while ((recv_length = Winsock.Read(temp_receive_buffer, 1024))!=0){ + +#ifdef VIRTUAL_SUBNET_SERVER + /* + ** Get a pointer to the data header and swap the bit mask + */ + CurHeaderBuf = (IPXHEADER*)&temp_receive_buffer[0]; //NULL; + unsigned short *swapptr = (unsigned short*)CurHeaderBuf; + *swapptr = ntohs (*swapptr); + + CurDataBuf = (char*)&temp_receive_buffer[2]; + + /*..................................................................... + Compute the length of the packet (byte-swap the length in the IPX hdr) + .....................................................................*/ + packetlen = recv_length-2; +#else //VIRTUAL_SUBNET_SERVER + CurHeaderBuf = NULL; + CurDataBuf = (char*)&temp_receive_buffer[0]; + + /*..................................................................... + Compute the length of the packet (byte-swap the length in the IPX hdr) + .....................................................................*/ + packetlen = recv_length; + +#endif //VIRTUAL_SUBNET_SERVER + + + /*..................................................................... + Extract the sender's address from the IPX header + .....................................................................*/ + address.Set_Address (CurHeaderBuf); + + /*..................................................................... + Examine the Magic Number of the received packet to determine if this + packet goes into the Global Queue, or into one of the Private Queues + .....................................................................*/ + packet = (CommHeaderType *)CurDataBuf; +#if (0) +char tempbuf[256]; + +static char pcode [4][18]={ + "PACKET_DATA_ACK", // this is a data packet requiring an ACK + "PACKET_DATA_NOACK", // this is a data packet not requiring an ACK + "PACKET_ACK", // this is an ACK for a packet + "PACKET_COUNT" // for computational purposes +}; + +sprintf (tempbuf, "Received packet type %d, ID=%d, code=%s, length=%d \n", CurDataBuf[sizeof(CommHeaderType)], + packet->PacketID, + pcode[packet->Code], + recv_length); +CCDebugString (tempbuf); +#endif //(0) + if (packet->MagicNumber == GlobalChannel->Magic_Num()) { + /*.................................................................. + Put the packet in the Global Queue + ..................................................................*/ + if (!GlobalChannel->Receive_Packet (packet, packetlen, &address)) + ReceiveOverflows++; + } else { + if (packet->MagicNumber == ProductID) { + /*.................................................................. + Find the Private Queue that this packet is for + ..................................................................*/ + for (i = 0; i < NumConnections; i++) { +#ifdef VIRTUAL_SUBNET_SERVER + if (Connection[i]->Address == address && Connection[i]->ID != VSS_ID) { +#else //VIRTUAL_SUBNET_SERVER + if (Connection[i]->Address == address) { +#endif //VIRTUAL_SUBNET_SERVER + if (!Connection[i]->Receive_Packet (packet, packetlen)) + ReceiveOverflows++; + break; + } + } + } + } + + } + }else{ + + while (IPX_Get_Outstanding_Buffer95(&temp_receive_buffer[0])){ + + CurHeaderBuf = (IPXHEADER*)&temp_receive_buffer[0]; + CurDataBuf = (char*)&temp_receive_buffer[sizeof(IPXHeaderType)]; + + /*..................................................................... + Compute the length of the packet (byte-swap the length in the IPX hdr) + .....................................................................*/ + packetlen = ((CurHeaderBuf->Length & 0xff) << 8) | + (CurHeaderBuf->Length >> 8); + packetlen -= sizeof(IPXHeaderType); + + /*..................................................................... + Extract the sender's address from the IPX header + .....................................................................*/ + address.Set_Address (CurHeaderBuf); + + /*..................................................................... + Examine the Magic Number of the received packet to determine if this + packet goes into the Global Queue, or into one of the Private Queues + .....................................................................*/ + packet = (CommHeaderType *)CurDataBuf; + + if (packet->MagicNumber == GlobalChannel->Magic_Num()) { + /*.................................................................. + Put the packet in the Global Queue + ..................................................................*/ + if (!GlobalChannel->Receive_Packet (packet, packetlen, &address)) + ReceiveOverflows++; + } else { + if (packet->MagicNumber == ProductID) { + /*.................................................................. + Find the Private Queue that this packet is for + ..................................................................*/ + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Address == address) { + if (!Connection[i]->Receive_Packet (packet, packetlen)) + ReceiveOverflows++; + break; + } + } + } + } + + } + } + + //IPX_Start_Listening95(); + + +#else //NOT_FOR_WIN95 + + + /* + ------------ Error if IPX not installed or not Listening ----------------- + */ + if (!IPXStatus || !Listening) + return(false); + + /*------------------------------------------------------------------------ + Loop until there are no more packets to process. + ------------------------------------------------------------------------*/ + for (;;) { + /*..................................................................... + Check the BufferFlags for the "current" buffer; if it's empty, + break; out of the loop. + .....................................................................*/ + if (BufferFlags[CurIndex]==0) + break; + + /*..................................................................... + Compute the length of the packet (byte-swap the length in the IPX hdr) + .....................................................................*/ + packetlen = ((CurHeaderBuf->Length & 0xff) << 8) | + (CurHeaderBuf->Length >> 8); + packetlen -= sizeof(IPXHeaderType); + + /*..................................................................... + Extract the sender's address from the IPX header + .....................................................................*/ + address.Set_Address (CurHeaderBuf); + + /*..................................................................... + Examine the Magic Number of the received packet to determine if this + packet goes into the Global Queue, or into one of the Private Queues + .....................................................................*/ + packet = (CommHeaderType *)CurDataBuf; + if (packet->MagicNumber == GlobalChannel->Magic_Num()) { + /*.................................................................. + Put the packet in the Global Queue + ..................................................................*/ + if (!GlobalChannel->Receive_Packet (packet, packetlen, &address)) + ReceiveOverflows++; + } else { + if (packet->MagicNumber == ProductID) { + /*.................................................................. + Find the Private Queue that this packet is for + ..................................................................*/ + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Address == address) { + if (!Connection[i]->Receive_Packet (packet, packetlen)) + ReceiveOverflows++; + break; + } + } + } + } + + /*..................................................................... + Set the current BufferFlags to 0 (since we've cleaned out this buffer) + .....................................................................*/ + BufferFlags[CurIndex] = 0; + + /*..................................................................... + Go to the next packet buffer + .....................................................................*/ + CurIndex++; + CurHeaderBuf = (IPXHeaderType *)(((char *)CurHeaderBuf) + FullPacketLen); + CurDataBuf = ((char *)CurDataBuf) + FullPacketLen; + if (CurIndex >= NumBufs) { + CurHeaderBuf = FirstHeaderBuf; + CurDataBuf = FirstDataBuf; + CurIndex = 0; + } + + } + +#endif //NOT_FOR_WIN95 + + /*------------------------------------------------------------------------ + Service all connections. If a connection reports that it's gone "bad", + report an error to the caller. If it's the Global Channel, un-queue the + send entry that's holding things up. This will keep the Global Channel + from being clogged by one un-ACK'd outgoing packet. + ------------------------------------------------------------------------*/ + if (GlobalChannel) { + if (!GlobalChannel->Service()) { + GlobalChannel->Queue->UnQueue_Send (NULL, NULL, 0); + rc = 0; + } + } + for (i = 0; i < NumConnections; i++) { + if (!Connection[i]->Service()) { +#ifdef VIRTUAL_SUBNET_SERVER + if (Connection[i]->ID != VSS_ID){ +#endif //VIRTUAL_SUBNET_SERVER + rc = 0; + BadConnection = Connection[i]->ID; +#ifdef VIRTUAL_SUBNET_SERVER + } +#endif //VIRTUAL_SUBNET_SERVER + } + } + + if (rc) + BadConnection = IPXConnClass::CONNECTION_NONE; + + return(rc); + + +} /* end of Service */ + + +/*************************************************************************** + * IPXManagerClass::Get_Bad_Connection -- returns bad connection ID * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * ID of bad connection; CONNECTION_NONE if none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/04/1995 BRR : Created. * + *=========================================================================*/ +int IPXManagerClass::Get_Bad_Connection(void) +{ + return(BadConnection); + +} /* end of Get_Bad_Connection */ + + +/*************************************************************************** + * IPXManagerClass::Global_Num_Send -- reports # entries in send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # entries in the Global Send Queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Global_Num_Send(void) +{ + /* + ------------ Error if IPX not installed or not Listening ----------------- + */ + if (!IPXStatus || !Listening) + return(0); + + return(GlobalChannel->Queue->Num_Send()); + +} /* end of Global_Num_Send */ + + +/*************************************************************************** + * IPXManagerClass::Global_Num_Receive -- reports # entries in recv queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # entries in the Global Receive Queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Global_Num_Receive(void) +{ + /* + ------------ Error if IPX not installed or not Listening ----------------- + */ + if (!IPXStatus || !Listening) + return(0); + + return(GlobalChannel->Queue->Num_Receive()); + +} /* end of Global_Num_Receive */ + + +/*************************************************************************** + * IPXManagerClass::Private_Num_Send -- reports # entries in send queue * + * * + * INPUT: * + * # entries in the Private Send Queue * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Private_Num_Send(int id) +{ + int i; + int maxnum; + + /* + ------------ Error if IPX not installed or not Listening ----------------- + */ + if (!IPXStatus || !Listening || (NumConnections==0)) return(false); + + /*------------------------------------------------------------------------ + If connection ID specified, return that connection's # of packets + ------------------------------------------------------------------------*/ + if (id != IPXConnClass::CONNECTION_NONE) { + i = Connection_Index(id); + if (i != IPXConnClass::CONNECTION_NONE) { + return(Connection[i]->Queue->Num_Send()); + } else { + return(false); + } + } else { + + /*------------------------------------------------------------------------ + Otherwise, return the max # of all connections + ------------------------------------------------------------------------*/ + maxnum = 0; + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Queue->Num_Send() > maxnum) { + maxnum = Connection[i]->Queue->Num_Send(); + } + } + return(maxnum); + } +} + + +/*************************************************************************** + * IPXManagerClass::Private_Num_Receive -- reports # entries in recv queue * + * * + * INPUT: * + * id ID of connection to query * + * * + * OUTPUT: * + * # entries in the Private Receive Queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Private_Num_Receive(int id) +{ + int i; + int maxnum; + + /* + ------------ Error if IPX not installed or not Listening ----------------- + */ + if (!IPXStatus || !Listening || (NumConnections==0)) + return(0); + + /*------------------------------------------------------------------------ + If connection ID specified, return that connection's # of packets + ------------------------------------------------------------------------*/ + if (id != IPXConnClass::CONNECTION_NONE) { + i = Connection_Index(id); + if (i != IPXConnClass::CONNECTION_NONE) { + return(Connection[i]->Queue->Num_Receive()); + } else { + return(0); + } + } else { + + /*------------------------------------------------------------------------ + Otherwise, return the max # of all connections + ------------------------------------------------------------------------*/ + maxnum = 0; + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Queue->Num_Receive() > maxnum) + maxnum = Connection[i]->Queue->Num_Receive(); + } + return(maxnum); + } +} + + +/*************************************************************************** + * IPXManagerClass::Set_Socket -- sets socket ID for all connections * + * * + * INPUT: * + * socket new socket ID to use * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Do not call this function after communications have started; you * + * must call it before calling Init(). * + * The socket number is byte-swapped, since IPX requires socket ID's * + * to be stored high/low. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +void IPXManagerClass::Set_Socket(unsigned short socket) +{ + Socket = (unsigned short)( (((unsigned long)socket & 0x00ff) << 8) | + (((unsigned long)socket & 0xff00) >> 8)); + +} /* end of Set_Socket */ + + +/*************************************************************************** + * IPXManagerClass::Response_Time -- Returns largest Avg Response Time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * largest avg response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/04/1995 BRR : Created. * + *=========================================================================*/ +unsigned long IPXManagerClass::Response_Time(void) +{ + unsigned long resp; + unsigned long maxresp = 0; + int i; +#ifdef VIRTUAL_SUBNET_SERVER + int vss=0; + + if (Winsock.Get_Connected()){ + vss = (int) UseVirtualSubnetServer; + } + + for (i = 0; i < NumConnections - vss; i++) { +#else //VIRTUAL_SUBNET_SERVER + for (i = 0; i < NumConnections; i++) { +#endif // VIRTUAL_SUBNET_SERVER + resp = Connection[i]->Queue->Avg_Response_Time(); + if (resp > maxresp) + maxresp = resp; + } + + return(maxresp); +} + + +/*************************************************************************** + * IPXManagerClass::Global_Response_Time -- Returns Avg Response Time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * avg global channel response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/04/1995 BRR : Created. * + *=========================================================================*/ +unsigned long IPXManagerClass::Global_Response_Time(void) +{ + if (GlobalChannel) { + return (GlobalChannel->Queue->Avg_Response_Time()); + } else { + return (0); + } +} + + +/*************************************************************************** + * IPXManagerClass::Reset_Response_Time -- Reset response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/04/1995 BRR : Created. * + *=========================================================================*/ +void IPXManagerClass::Reset_Response_Time(void) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + Connection[i]->Queue->Reset_Response_Time(); + } + + if (GlobalChannel) + GlobalChannel->Queue->Reset_Response_Time(); + +} /* end of Reset_Response_Time */ + + +/*************************************************************************** + * IPXManagerClass::Oldest_Send -- gets ptr to oldest send buf * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * buf ptr * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/04/1995 BRR : Created. * + *=========================================================================*/ +void * IPXManagerClass::Oldest_Send(void) +{ + int i,j; + unsigned long time; + unsigned long mintime = 0xffffffff; + SendQueueType *send_entry; // ptr to send entry header + CommHeaderType *packet; + void *buf = NULL; + + for (i = 0; i < NumConnections; i++) { + + send_entry = NULL; + + for (j = 0; j < Connection[i]->Queue->Num_Send(); j++) { + send_entry = Connection[i]->Queue->Get_Send(j); + if (send_entry) { + packet = (CommHeaderType *)send_entry->Buffer; + if (packet->Code == ConnectionClass::PACKET_DATA_ACK && send_entry->IsACK == 0) { + break; + } else { + send_entry = NULL; + } + } + } + + if (send_entry!=NULL) { + + time = send_entry->FirstTime; + + if (time < mintime) { + mintime = time; + buf = send_entry->Buffer; + } + } + } + + return(buf); + +} /* end of Oldest_Send */ + + +/*************************************************************************** + * IPXManagerClass::Set_Bridge -- prepares to cross a bridge * + * * + * This routine is designed to prevent the connection from having to * + * call Get_Local_Target, except the minimum number of times, since that * + * routine is buggy & goes away for long periods sometimes. * + * * + * INPUT: * + * bridge network number of the destination bridge * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 07/06/1995 BRR : Created. * + *=========================================================================*/ +void IPXManagerClass::Set_Bridge(NetNumType bridge) +{ + if (GlobalChannel) { + GlobalChannel->Set_Bridge(bridge); + } +} + + +/*************************************************************************** + * IPXManagerClass::Configure_Debug -- sets up special debug values * + * * + * Mono_Debug_Print2() can look into a packet to pull out a particular * + * ID, and can print both that ID and a string corresponding to * + * that ID. This routine configures these values so it can find * + * and decode the ID. This ID is used in addition to the normal * + * CommHeaderType values. * + * * + * INPUT: * + * index connection index to configure (-1 = Global Channel) * + * offset ID's byte offset into packet * + * size size of ID, in bytes; 0 if none * + * names ptr to array of names; use ID as an index into this * + * maxnames max # in the names array; 0 if none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Names shouldn't be longer than 12 characters. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void IPXManagerClass::Configure_Debug(int index, int offset, int size, + char **names, int maxnames) +{ + if (index == -1) { + GlobalChannel->Queue->Configure_Debug (offset, size, names, maxnames); + } else { + if (Connection[index]) { + Connection[index]->Queue->Configure_Debug (offset, size, names, maxnames); + } + } +} + + +/*************************************************************************** + * IPXManagerClass::Mono_Debug_Print -- debug output routine * + * * + * INPUT: * + * index index of connection to display (-1 = Global Channel) * + * refresh 1 = complete screen refresh * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +void IPXManagerClass::Mono_Debug_Print(int index, int refresh) +{ +#ifdef WWLIB32_H + char txt[80]; + int i; + + if (index == -1) + GlobalChannel->Queue->Mono_Debug_Print (refresh); + else if (Connection[index]) + Connection[index]->Queue->Mono_Debug_Print (refresh); + + if (refresh) { + Mono_Set_Cursor (20,1); + Mono_Printf ("IPX Queue:"); + + Mono_Set_Cursor (9,2); + Mono_Printf ("Average Response Time:"); + + Mono_Set_Cursor (43,1); + Mono_Printf ("Send Overflows:"); + + Mono_Set_Cursor (40,2); + Mono_Printf ("Receive Overflows:"); + + } + + Mono_Set_Cursor (32,1); + Mono_Printf ("%d",index); + + Mono_Set_Cursor (32,2); + if (index == -1) { + Mono_Printf ("%d ", GlobalChannel->Queue->Avg_Response_Time()); + } else { + Mono_Printf ("%d ", Connection[index]->Queue->Avg_Response_Time()); + } + + Mono_Set_Cursor (59,1); + Mono_Printf ("%d ", SendOverflows); + + Mono_Set_Cursor (59,2); + Mono_Printf ("%d ", ReceiveOverflows); + + for (i = 0; i < NumBufs; i++) { + if (BufferFlags[i]) { + txt[i] = 'X'; + } else { + txt[i] = '_'; + } + } + txt[i] = 0; + Mono_Set_Cursor ((80-NumBufs)/2,3); + Mono_Printf ("%s",txt); + +#else + index = index; + refresh = refresh; +#endif +} /* end of Mono_Debug_Print */ + + +/*************************************************************************** + * IPXManagerClass::Alloc_RealMode_Mem -- allocates real-mode memory * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Alloc_RealMode_Mem(void) +{ +#ifdef NOT_FOR_WIN95 + union REGS regs; + struct SREGS sregs; + int size; // required size of allocation + unsigned char *realmode; // start addresses of real-mode data + int realmodelen; // length of real-mode data + unsigned long func_val; + char *p; // for parsing buffer + int i; + + /*------------------------------------------------------------------------ + Compute # of buffers we need to allocate, & the max size of each one + ------------------------------------------------------------------------*/ + NumBufs = Glb_NumPackets + (Pvt_NumPackets * CONNECT_MAX); + + PacketLen = Glb_MaxPacketLen + sizeof (GlobalHeaderType); + if (Pvt_MaxPacketLen + sizeof (CommHeaderType) > PacketLen) + PacketLen = Pvt_MaxPacketLen + sizeof (CommHeaderType); + + FullPacketLen = PacketLen + sizeof(IPXHeaderType); + + /*------------------------------------------------------------------------ + Compute the size of everything we'll ever need, allocate it in one big + chunk. The memory is used as follows: + - Real-mode assembly IPX callback routine, plus its data, + (which includes the ListenECB) + - Array of IPX Packet buffers (IPXHeader plus data buffer) + - SendECB: ECB for sending + - SendHeader: IPX Header for sending + - SendBuf: Packet buffer for sending + - BufferFlags: 1 byte for each incoming packet buffer; 1=in use, 0=free + ------------------------------------------------------------------------*/ + realmode = (unsigned char *)Get_RM_IPX_Address(); + realmodelen = Get_RM_IPX_Size(); + size = realmodelen + // assembly routine & its data + (FullPacketLen * NumBufs) + // array of packet buffers + sizeof(ECBType) + // SendECB + FullPacketLen + // SendHeader & SendBuf + NumBufs; // BufferFlags + if (size > 65535) + return(false); + + /*------------------------------------------------------------------------ + Allocate DOS memory for the ECB, IPXHeader & packet buffers: + AX = 0x100 + BX = # paragraphs to allocate + - if Success, AX = real-mode segment, DX = selector + - if Failure, carry flag is set + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call + regs.x.ebx = ((size + 15) >> 4); // # paragraphs to allocate + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + /*........................................................................ + If the carry flag is set, DPMI is indicating an error. + ........................................................................*/ + if (regs.x.cflag) { + return(false); + } + + /*........................................................................ + Save the values of the returned segment & selector + ........................................................................*/ + Selector = regs.w.dx; + Segment = regs.w.ax; + RealMemSize = size; + RealModeData = (RealModeDataType *)(((long)Segment) << 4); + + /*------------------------------------------------------------------------ + Lock the memory (since we're servicing interrupts with it) + AX = 0x600 + BX:CX = starting linear address of memory to lock + SI:DI = size of region to lock (in bytes) + - If Failure, carry flag is set. + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_LOCK_MEM; // DPMI function to call + regs.x.ebx = ((long)RealModeData & 0xffff0000) >> 16; + regs.x.ecx = ((long)RealModeData & 0x0000ffff); + regs.x.esi = ((long)RealMemSize & 0xffff0000) >> 16; + regs.x.edi = ((long)RealMemSize & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI + /*........................................................................ + If the carry flag is set, DPMI is indicating an error. + ........................................................................*/ + if (regs.x.cflag) { + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = Selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // free the memory + return(false); + } + + /*------------------------------------------------------------------------ + Copy the Real-mode code into our memory buffer + ------------------------------------------------------------------------*/ + p = (char *)(((long)Segment) << 4); + memcpy (p,realmode,realmodelen); + p += realmodelen; + + /*------------------------------------------------------------------------ + Compute & save the entry point for the real-mode packet handler + ------------------------------------------------------------------------*/ + func_val = (unsigned long)RealModeData; + Handler = (((func_val & 0xffff0) << 12) | + ((func_val & 0x000f) + RealModeData->FuncOffset)); + + /*------------------------------------------------------------------------ + Fill in buffer pointers + ------------------------------------------------------------------------*/ + ListenECB = &(RealModeData->ListenECB); + + FirstHeaderBuf = (IPXHeaderType *)p; + FirstDataBuf = (((char *)FirstHeaderBuf) + sizeof(IPXHeaderType)); + CurIndex = 0; + CurHeaderBuf = FirstHeaderBuf; + CurDataBuf = FirstDataBuf; + p += FullPacketLen * NumBufs; + + SendECB = (ECBType *)p; + p += sizeof (ECBType); + + SendHeader = (IPXHeaderType *)p; + p += sizeof (IPXHeaderType); + + SendBuf = (char *)p; + p += PacketLen; + + BufferFlags = (char *)p; + + /*------------------------------------------------------------------------ + Fill in the real-mode routine's data (The ECB will be filled in when we + command IPX to Listen). + ------------------------------------------------------------------------*/ + RealModeData->NumBufs = (short)NumBufs; + RealModeData->BufferFlags = (char *) + ((((long)BufferFlags & 0xffff0) << 12) | + ((long)BufferFlags & 0x0000f)); + RealModeData->PacketSize = (short)FullPacketLen; + RealModeData->FirstPacketBuf = (IPXHeaderType *) + ((((long)FirstHeaderBuf & 0xffff0) << 12) | + ((long)FirstHeaderBuf & 0x0000f)); + RealModeData->CurIndex = 0; + RealModeData->CurPacketBuf = RealModeData->FirstPacketBuf; + RealModeData->Semaphore = 0; + RealModeData->ReEntrantCount = 0; + + /*------------------------------------------------------------------------ + Init state of all buffers to empty + ------------------------------------------------------------------------*/ + for (i = 0; i < NumBufs; i++) + BufferFlags[i] = 0; + + /*------------------------------------------------------------------------ + Check the start & end markers in the real-mode memory area + ------------------------------------------------------------------------*/ + if (RealModeData->Marker1 != 0x1111 || + RealModeData->Marker2 != 0x2222) { + Free_RealMode_Mem(); + return(false); + } else { + return(true); + } +#else //NOT_FOR_WIN95 + + return (TRUE); + +#endif //NOT_FOR_WIN95 +} + + +/*************************************************************************** + * IPXManagerClass::Free_RealMode_Mem -- frees real-mode memory * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Free_RealMode_Mem(void) +{ +#ifdef NOT_FOR_WIN95 + union REGS regs; + struct SREGS sregs; + int rc = 1; + + /*------------------------------------------------------------------------ + Unlock the memory + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_UNLOCK_MEM; // DPMI function to call + regs.x.ebx = ((long)RealModeData & 0xffff0000) >> 16; + regs.x.ecx = ((long)RealModeData & 0x0000ffff); + regs.x.esi = ((long)RealMemSize & 0xffff0000) >> 16; + regs.x.edi = ((long)RealMemSize & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI + /*........................................................................ + If the carry flag is set, DPMI is indicating an error. + ........................................................................*/ + if (regs.x.cflag) { + rc = 0; + } + + /*------------------------------------------------------------------------ + Free DOS memory + ------------------------------------------------------------------------*/ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = Selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // free the memory + + return(rc); + +#else // NOT_FOR_WIN95 + + return (1); + +#endif // NOT_FOR_WIN95 +} /* end of Free_RealMode_Mem */ + + diff --git a/IPXMGR.H b/IPXMGR.H new file mode 100644 index 0000000..87305e4 --- /dev/null +++ b/IPXMGR.H @@ -0,0 +1,395 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\ipxmgr.h_v 1.10 16 Oct 1995 16:47:34 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXMGR.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 3, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This is the Connection Manager for IPX network communications. It * + * creates, manages, & orchestrates multiple IPX connections, as well as * + * the "global" connection ("Global Channel"), which can talk to any * + * system on the net. * + * * + * Use the Global Channel to query systems for their names, ID's, & * + * IPX addresses. Then, create a Private Connection with each system * + * that joins your game, and use the Private Channel to send game packets * + * (the private channel will perform somewhat faster, & gives you better * + * control than the Global Channel; it can detect retries, and the Global * + * Channel can't). * + * * + * HOW THIS CLASS WORKS: * + * This class has to set up an IPX Event Service Routine in low (DOS) * + * memory. So, it uses DPMI to allocate & lock a chunk of DOS memory; * + * this memory is used for all incoming packet buffers, the outgoing * + * packet buffer, and the actual code for the event handler. The real- * + * mode handler code & this class share a portion of memory that's mapped * + * into a "RealModeDataType" structure. As packets come in, the handler * + * points IPX to the next available packet buffer & restarts listening; * + * it sets a flag to tell this class that a packet is present at that * + * buffer slot. This class must read all the packets & determine which * + * connection they go with (the Global Channel, or one of the Private * + * Channels). This parsing is done in the Service routine for this class. * + * * + * Constructor: Just inits some variables, checks to see if IPX is there * + * Destructor: Complete shutdown; stops IPX listening, frees all memory * + * Init: Should only be called once (but can be called more); * + * allocates all memory, creates the Global Channel * + * connection, starts IPX listening. By not placing this * + * step in the constructor, the app can control when * + * listening actually starts; also, you don't get a bunch * + * of allocations just by declaring an IPXManagerClass * + * instance. You have to call Init() for the allocations * + * to occur. * + * Connection utilities: Create & manage Private Connections. Each * + * connection has its own IPX address, numerical ID, and * + * character name (presumably the name of the other * + * player). * + * Send/Get_Global_Message: adds a packet to the Global Connection queue, * + * or reads from the queue. The caller should check the * + * ProductID value from returned packets to be sure it's * + * talking to the right product. * + * Send/Get_Private_Message: adds a packet to a Private Connection queue, * + * or reads from the queue * + * Service: Checks the Real-Mode-Memory packet array to see if any * + * new packets have come in; if they have, it parses them * + * & distributes them to the right connection queue. The * + * queue's Service routine handles ACK'ing or Resending * + * packets. * + * * + * Here's a memory map of the Real-Mode memory block. 'N' is the number * + * of packet buffers allocated in low memory: * + * * + * ---------------------------------- * + * | Shared-memory data | * + * |--------------------------------| * + * | Real-mode event handler code | * + * |--------------------------------| * + * | IPX Header & Packet Buffer 0 | * + * |--------------------------------| * + * | IPX Header & Packet Buffer 1 | * + * |--------------------------------| * + * | IPX Header & Packet Buffer 2 | * + * |--------------------------------| * + * | . . . | * + * |--------------------------------| * + * | IPX Header & Packet Buffer N | * + * |--------------------------------| * + * | Send Event Control Block | * + * |--------------------------------| * + * | Send IPX Header | * + * |--------------------------------| * + * | Send Packet Buffer | * + * |--------------------------------| * + * | Flags Array [N] | * + * ---------------------------------- * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IPXMANAGER_H +#define IPXMANAGER_H + + +/* +********************************* Includes ********************************** +*/ +#include "ipxconn.h" +#include "ipxgconn.h" +#include "ipxaddr.h" +#include "connmgr.h" + +/* +********************************** Defines ********************************** +*/ +/*--------------------------------------------------------------------------- +This is Virgin Interactive Entertainment's registered socket ID. +---------------------------------------------------------------------------*/ +#define VIRGIN_SOCKET 0x8813 + +/*--------------------------------------------------------------------------- +This is the maximum number of IPX connections supported. Just change this +value to support more. +---------------------------------------------------------------------------*/ +#define CONNECT_MAX 6 + +/*--------------------------------------------------------------------------- +These routines report the location & length of the real-mode routine, as +it's stored in protected-mode memory. +---------------------------------------------------------------------------*/ +extern "C" { + void *Get_RM_IPX_Address(void); + long Get_RM_IPX_Size(void); +} + +/* +***************************** Class Declaration ***************************** +*/ +class IPXManagerClass : public ConnManClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + IPXManagerClass (int glb_maxlen, int pvt_maxlen, int glb_num_packets, + int pvt_num_packets, unsigned short socket, unsigned short product_id); + virtual ~IPXManagerClass (); // stop listening + + /*..................................................................... + Initialization routines. + .....................................................................*/ + int Init (void); + int Is_IPX(void); + virtual void Set_Timing (unsigned long retrydelta, unsigned long maxretries, + unsigned long timeout); + void Set_Bridge(NetNumType bridge); + + /*..................................................................... + These routines control creation of the "Connections" (data queues) for + each remote system. + .....................................................................*/ + bool Create_Connection(int id, char *name, IPXAddressClass *address); + bool Delete_Connection(int id); + virtual int Num_Connections(void); + virtual int Connection_ID(int index); + char *Connection_Name(int id); + IPXAddressClass * Connection_Address(int id); + virtual int Connection_Index(int id); + + /*..................................................................... + This is how the application sends & receives messages. + .....................................................................*/ + int Send_Global_Message (void *buf, int buflen, int ack_req = 0, + IPXAddressClass *address = NULL); + int Get_Global_Message (void *buf, int *buflen, IPXAddressClass *address, + unsigned short *product_id); + + virtual int Send_Private_Message (void *buf, int buflen, + int ack_req = 1, int conn_id = CONNECTION_NONE); + virtual int Get_Private_Message (void *buf, int *buflen, int *conn_id); + + /*..................................................................... + The main polling routine; should be called as often as possible. + .....................................................................*/ + virtual int Service (void); + + /*..................................................................... + This routine reports which connection has an error on it. + .....................................................................*/ + int Get_Bad_Connection(void); + + /*..................................................................... + Queue utility routines. The application can determine how many + messages are in the send/receive queues. + .....................................................................*/ + virtual int Global_Num_Send(void); + virtual int Global_Num_Receive(void); + virtual int Private_Num_Send(int id = CONNECTION_NONE); + virtual int Private_Num_Receive(int id = CONNECTION_NONE); + + /*..................................................................... + This routine changes the socket ID assigned the IPX Manager when it + was constructed. Do not call this function after calling Init()! + The Socket ID should be known by both ends of the communications before + any packets are sent. + .....................................................................*/ + void Set_Socket(unsigned short socket); + + /*..................................................................... + Routines to return the largest average queue response time, and to + reset the response time for all queues. + .....................................................................*/ + virtual unsigned long Response_Time(void); + unsigned long Global_Response_Time(void); + virtual void Reset_Response_Time(void); + + /*..................................................................... + This routine returns a pointer to the oldest non-ACK'd buffer I've sent. + .....................................................................*/ + void * Oldest_Send(void); + + /*..................................................................... + Debug routines + .....................................................................*/ + virtual void Configure_Debug(int index, int offset, int size, + char **names, int maxnames); + virtual void Mono_Debug_Print(int index, int refresh = 0); + + /* + --------------------------- Private Interface ---------------------------- + */ + private: + /*..................................................................... + These routines allocate & free the DOS Real-mode memory block. + .....................................................................*/ + int Alloc_RealMode_Mem(void); + int Free_RealMode_Mem(void); + + /*..................................................................... + Misc variables + .....................................................................*/ + unsigned int IPXStatus : 1; // 0 = no IPX, 1 = IPX found + unsigned int Listening : 1; // 1 = Listening is on + unsigned int RealMemAllocd : 1; // 1 = Real-mode memory has been alloc'd + + /*..................................................................... + Packet Sizes, used for allocating real-mode memory + .....................................................................*/ + int Glb_MaxPacketLen; // Global Channel maximum packet size + int Glb_NumPackets; // # Global send/receive packets + int Pvt_MaxPacketLen; // Private Channel maximum packet size + int Pvt_NumPackets; // # Private send/receive packets + + /*..................................................................... + The ProductID is used in the Global Channel's packet header, and it's + used for the Private Channels' Magic Number. + .....................................................................*/ + unsigned short ProductID; // product ID + + /*..................................................................... + The Socket ID, and local Novell Connection Number + .....................................................................*/ + unsigned short Socket; // Our socket ID for sending/receiving + int ConnectionNum; // local connection #, 0=not logged in + + /*..................................................................... + Array of connection queues + .....................................................................*/ + IPXConnClass * Connection[CONNECT_MAX]; // array of connection object ptrs + int NumConnections; // # connection objects in use + IPXGlobalConnClass *GlobalChannel; // the Global Channel + + /*..................................................................... + Current queue for polling for received packets + .....................................................................*/ + int CurConnection; + + /*..................................................................... + Timing parameters for all connections + .....................................................................*/ + unsigned long RetryDelta; + unsigned long MaxRetries; + unsigned long Timeout; + + /*--------------------------------------------------------------------- + Real-mode memory pointers and such + ---------------------------------------------------------------------*/ + /*..................................................................... + This is a structure that mirrors data in real-mode memory: + .....................................................................*/ + typedef struct { + short Marker1; // the byte ID marker + ECBType ListenECB; // the Listening ECB + short NumBufs; // # of buffers we're giving to the handler + char *BufferFlags; // array of buffer-avail flags + short PacketSize; // size of packet including IPX header + IPXHeaderType *FirstPacketBuf; // ptr to 1st packet buffer + short CurIndex; // handler's current packet index + IPXHeaderType *CurPacketBuf; // handler's current packet buf + short FuncOffset; // contains offset of code + char Semaphore; // prevents re-entrancy + short ReEntrantCount; // times we've been called re-entrantly + short StackPtr; // real-mode stack pointer + short StackSeg; // real-mode stack segment + short StackPtr_int; // internal stack pointer + short StackSeg_int; // internal stack segment + short StackCheck; // stack check value (0x1234) + short Stack[256]; // actual stack space + short StackSpace; // label for top of stack + short Marker2; // the byte ID marker + } RealModeDataType; + + /*..................................................................... + The number & size of packet buffers in low memory + .....................................................................*/ + int NumBufs; // # packet buffers allocated + int PacketLen; // size of packet without IPX header + int FullPacketLen; // size of packet including IPX header + + /*..................................................................... + Selector & Segment of the DOS allocation; + Size of the allocation; + Ptr to the real-mode assembly data area + .....................................................................*/ + unsigned short Selector; // selector of DOS allocation pointer + unsigned short Segment; // real-mode segment of DOS allocation + int RealMemSize; // size of real mode memory allocated + RealModeDataType *RealModeData; // assembly routine & its data + + /*..................................................................... + This is a real-mode pointer to the address of the real-mode assembly + entry point. + .....................................................................*/ + long Handler; + + /*..................................................................... + Event Control Block for listening; contained within the real-mode + assembly routine's data area + .....................................................................*/ + ECBType *ListenECB; // ECB for listening + + /*..................................................................... + ptr to the 1st header & data buffers in the packet buffer array + .....................................................................*/ + IPXHeaderType *FirstHeaderBuf; // array of packet headers & buffers + char *FirstDataBuf; // 1st data buffer area + + /*..................................................................... + Current packet index & ptrs for parsing packets + .....................................................................*/ + int CurIndex; // Current packet index, for reading + IPXHeaderType *CurHeaderBuf; // Current packet ptr, for reading + char *CurDataBuf; // Current actual data ptr + + /*..................................................................... + ECB, header, & buffer for sending + .....................................................................*/ + ECBType *SendECB; // ECB for sending + IPXHeaderType *SendHeader; // Header for sending + char *SendBuf; // buffer for sending + + /*..................................................................... + Flags indicating whether a buffer contains data or not (1 = full) + The IPXManager must clear this flag; the real-mode routine will set it. + .....................................................................*/ + char *BufferFlags; // array of rx-buffer-avail flags + + /*..................................................................... + Various Statistics + .....................................................................*/ + int SendOverflows; + int ReceiveOverflows; + int BadConnection; +}; + +#endif +/*************************** end of ipxmgr.h *******************************/ diff --git a/IPXPROT.ASM b/IPXPROT.ASM new file mode 100644 index 0000000..8ec1156 --- /dev/null +++ b/IPXPROT.ASM @@ -0,0 +1,113 @@ +; +; Command & Conquer(tm) +; Copyright 2025 Electronic Arts Inc. +; +; 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 3 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 General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . +; + + +;!!!!!!!!!!!!!!!!!!! lock the allocation + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : VQLIB * +;* * +;* File Name : HANDLER.ASM * +;* * +;* Programmer : Bill Randolph * +;* * +;* Start Date : April 7, 1995 * +;* * +;* Last Update : April 7, 1995 [BRR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* IPXHandler -- callback routine for IPX * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT +LOCALS ?? + + +;******************************** Includes ********************************* + + +;******************************** Defines *********************************** + + +;****************************** Declarations ******************************** +global C Get_RM_IPX_Address:NEAR +global C Get_RM_IPX_Size:NEAR + +;********************************* Data ************************************ + DATASEG + +LABEL RealBinStart BYTE +include "obj\ipxreal.ibn" +LABEL RealBinEnd BYTE + +;********************************* Data ************************************ + CODESEG + +;*************************************************************************** +;* Get_RM_IPX_Address -- Return address of real mode code for copy. * +;* * +;* INPUT: * +;* none * +;* * +;* OUTPUT: * +;* VOID * to the address of the real mode IPX code * +;* * +;* PROTO: * +;* VOID *Get_RM_IPX_Address(VOID); * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_IPX_Address C Near + + mov eax, OFFSET RealBinStart + ret + + ENDP + +;*************************************************************************** +;* Get_RM_IPX_Size -- return size of real mode IPX code. * +;* * +;* INPUT: * +;* none * +;* * +;* OUTPUT: * +;* LONG size of the real mode IPX code * +;* * +;* PROTO: * +;* LONG Get_RM_IPX_Size(VOID); * +;* * +;* HISTORY: * +;* 07/06/1994 SKB : Created. * +;*=========================================================================* + PROC Get_RM_IPX_Size C Near + + mov eax, OFFSET RealBinEnd - OFFSET RealBinStart + ret + + ENDP + + END + +;************************** End of handler.asm ***************************** diff --git a/IPXREAL.ASM b/IPXREAL.ASM new file mode 100644 index 0000000..10bb56b --- /dev/null +++ b/IPXREAL.ASM @@ -0,0 +1,319 @@ +; +; Command & Conquer(tm) +; Copyright 2025 Electronic Arts Inc. +; +; 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 3 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 General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . +; + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : VQLIB * +;* * +;* File Name : HANDLER.ASM * +;* * +;* Programmer : Bill Randolph * +;* * +;* Start Date : April 7, 1995 * +;* * +;* Last Update : April 7, 1995 [BRR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* IPXHandler -- callback routine for IPX * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************* Model & Processor Directives ************************ +IDEAL +MODEL LARGE +P386N +LOCALS ?? + + +;******************************** Includes ********************************* + + +;******************************** Defines *********************************** + + +;****************************** Declarations ******************************** +global C IPXHandler:FAR + + +;********************************* Code ************************************ + CODESEG + + +;--------------------------------------------------------------------------- +; The markers let the application verify that it's mapping this memory +; correctly. +;--------------------------------------------------------------------------- +Marker1 DW 1111h ; placeholder to find data start + +;--------------------------------------------------------------------------- +; This is the IPX Event Control Block: +;--------------------------------------------------------------------------- +ECB_LinkAddress DD ? +ECB_EventServiceRoutine DD ? ; Event Handler ptr +ECB_InUse DB ? ; 0 = event is complete +ECB_CompletionCode DB ? ; 0 = OK, IPX error otherwise +ECB_SocketNumber DW ? ; socket to listen/send on +ECB_ConnectionID DW ? +ECB_RestOfWorkspace DW ? +ECB_DriverWorkSpace DB 12 DUP (?) +ECB_ImmediateAddress DB 6 DUP (?) ; bridge address +ECB_PacketCount DW ? ; # data areas (2) +ECB_HeaderAddress DD ? ; ptr to IPX header buffer +ECB_HeaderLength DW ? ; length of IPX header buffer +ECB_PacketAddress DD ? ; ptr to packet buffer +ECB_PacketLength DW ? ; length of packet buffer + +;--------------------------------------------------------------------------- +; The rest of the variables are for telling IPX which buffer to store the +; next incoming packet in. They must be initialized by the application. +;--------------------------------------------------------------------------- +NumBufs DW 0 ; # buffers provided by app +BufferFlags DD 0 ; array of in-use flags (1 = in use) +PacketSize DW 0 ; total size of 1 buf (incl IPX hdr) +FirstPacketBuf DD 0 ; ptr to 1st packet buffer +CurIndex DW 0 ; current packet/flag index +CurPacketBuf DD 0 ; ptr to current packet buf +FuncOffset DW StartLabel ; offset of our routine + +;--------------------------------------------------------------------------- +; These values are for preventing re-entrancy; they're currently not used. +;--------------------------------------------------------------------------- +Semaphore DB 0 ; prevents re-entrancy +ReEntrantCount DW 0 ; times we've been called re-entrantly + +;--------------------------------------------------------------------------- +; Local stack space +;--------------------------------------------------------------------------- +StackPtr DW 0 ; saved copy of stack ptr +StackSeg DW 0 ; saved copy of stack seg +StackPtr_int DW 0 ; our internal stack ptr +StackSeg_int DW 0 ; our internal stack seg +StackCheck DW 1234h ; check for stack overflow + DW 256 DUP (0) ; stack storage space +StackSpace DW 0 ; label for our stack space + +;--------------------------------------------------------------------------- +; These bytes mark the end of the real-mode data area +;--------------------------------------------------------------------------- +Marker2 DW 2222h ; placeholder to find data end + + +;*************************************************************************** +;* IPXHandler -- IPX callback routine * +;* * +;* This routine is assembled as a stand-alone executable, then loaded * +;* into low DOS memory by a protected-mode application. * +;* * +;* INPUT: * +;* none. * +;* * +;* OUTPUT: * +;* none. * +;* * +;* WARNINGS: * +;* none. * +;* * +;* HISTORY: * +;* 04/07/1995 BRR : Created. * +;*=========================================================================* + label StartLabel + PROC IPXHandler C FAR USES + + ;................................................................... + ; Turn off interrupts; make sure memory copies go forward + ;................................................................... + pushf + cli + cld + + ;................................................................... + ; Set up segment registers to point DS to CS + ;................................................................... + push ds + push ax + mov ax,cs + mov ds,ax + + ;................................................................... + ; Set up our local stack; save SS & SP first. + ;................................................................... + mov [StackSeg],ss + mov [StackPtr],sp + mov [StackPtr_int], OFFSET StackSpace + mov [StackSeg_int], SEG StackSpace + lss sp, [DWORD PTR StackPtr_int] + + + ;................................................................... + ; Save all registers + ;................................................................... + pushad + push es + + ;................................................................... + ; If we've been called re-entrantly, just exit + ;................................................................... + cmp [Semaphore],0 + jz ??Start_Handler + add [ReEntrantCount],1 + jmp ??Exit_Handler + +??Start_Handler: + ;................................................................... + ; Set our semaphore + ;................................................................... + mov [Semaphore],1 + + ;------------------------------------------------------------------- + ; Set 'CurIndex' to the index of the next-available receive buffer, + ; and 'CurPacketBuf to the next-available packet buffer + ;------------------------------------------------------------------- + ;................................................................... + ; Get 'CurIndex' & increment it. Wrap to 0 if we reach 'NumBufs' + ; Since I'm treating 'CurPacketBuf' as a long integer (and not as + ; a segment:offset), the entire data area can't be larger than 64K. + ;................................................................... + mov dx,[CurIndex] ; DX = CurIndex + mov eax,[CurPacketBuf] ; EAX = current packet buffer addr + inc dx ; DX = next index + add ax,[PacketSize] ; EAX = next buffer ptr + cmp dx,[NumBufs] ; see if DX is past # buffers + jb ??Get_Flag + mov dx,0 ; wrap to 1st index + mov eax,[FirstPacketBuf] ; wrap to 1st packet buffer + +??Get_Flag: + ;................................................................... + ; Get the next buffer-in-use flag; if it's 0, load [CurIndex] with + ; the value of SI (the next index). If it's 1, skip the updating of + ; the index, flag & buffer ptr. + ; DX = new CurIndex + ; EAX = new CurPacketBuf + ;................................................................... + les di,[BufferFlags] ; ES:DI = BufferFlags address + mov bx,di ; BX = DI + new CurIndex + add bx,dx + + cmp [BYTE PTR es:bx],0 ; compare next flag to 0 (avail) + jne ??Set_ECB ; if not avail, skip setting new values + + ;................................................................... + ; The next buffer is available; so, set this buffer's In-Use flag + ; to 1, and move on to the next buffer. Do not set this buffer's + ; flag to 1 until we move on to the next buffer, to prevent the + ; application from reading the currently-in-use packet buffer. + ; DX = new CurIndex + ; EAX = new CurPacketBuf + ; ES:DI = BufferFlags address + ;................................................................... + mov bx,di ; BX = DI + old CurIndex + add bx,[CurIndex] + mov [BYTE PTR es:bx],1 ; set old BufferFlags value to in-use + + mov [CurIndex],dx ; save new index + mov [CurPacketBuf],eax ; save new packet address + + ;------------------------------------------------------------------- + ; Set up the Event Control Block to tell IPX to start listening. + ; The following entries are filled in by the app, and should be left + ; alone: + ; - EventServiceRoutine + ; - SocketNumber + ; The rest should be re-initialized. Note that EBX is now pointing + ; to an unavailable buffer if the next buffer was already in use; + ; so it must be reloaded with the correct buffer address from + ; [CurPacketBuf]. + ;------------------------------------------------------------------- +??Set_ECB: + mov [ECB_LinkAddress],0 ; default + mov [ECB_InUse],0 ; default + mov [ECB_CompletionCode],0 ; default + mov [ECB_ConnectionID],0 ; default + mov [ECB_RestOfWorkspace],0 ; default + mov [ECB_DriverWorkSpace],0 ; default + mov [ECB_ImmediateAddress],0 ; default + mov [ECB_PacketCount],2 ; use 2 data areas + mov ebx,[CurPacketBuf] ; get current buffer address + mov [ECB_HeaderAddress],ebx ; set header address + mov [ECB_HeaderLength],30 ; size of IPX header + add ebx,30 ; point to past the header + mov [ECB_PacketAddress],ebx ; set packet data address + mov ax,[PacketSize] ; get size of one buffer + sub ax,30 ; remove size of IPX header + mov [ECB_PacketLength],ax ; set size of packet data + + ;------------------------------------------------------------------- + ; Clear the IPX header for this packet + ;------------------------------------------------------------------- + les di,[ECB_HeaderAddress] ; ES:DI = IPX Listen Header + mov cx,30 ; (30 bytes = size of header) + mov al,0 + rep stosb ; clear to 0's + + ;------------------------------------------------------------------- + ; Command IPX to start listening again. + ;------------------------------------------------------------------- + mov bx,4 ; IPX code for Listen + mov ax,ds ; ES = segment of ListenECB + mov es,ax + mov ax,OFFSET ECB_LinkAddress + mov si,ax ; ES:SI = address of ECB + int 07ah ; call IPX interrupt + + ;................................................................... + ; Clear our semaphore + ;................................................................... + mov [Semaphore],0 + +??Exit_Handler: + ;................................................................... + ; Pop values from our local stack + ;................................................................... + pop es + popad + + ;................................................................... + ; Check our stack-check value; if the stack has overflowed, generate + ; a debugger break. + ;................................................................... + cmp [StackCheck],1234h + je ??Restore_Stack + int 3 + + ;................................................................... + ; Restore the stack to its previous value + ;................................................................... +??Restore_Stack: + lss sp, [DWORD PTR StackPtr] + + ;................................................................... + ; Pop the rest of the registers + ;................................................................... + pop ax + pop ds + + popf + + ret + +ENDP IPXHandler + +END + +;************************** End of handler.asm ***************************** diff --git a/JAP.BAT b/JAP.BAT new file mode 100644 index 0000000..0700cef --- /dev/null +++ b/JAP.BAT @@ -0,0 +1,5 @@ +@echo off +pushd +cd ..\run +conquer -CDf:\projects\c&c\cdjapan\cd1;f:\projects\c&c\cdjapan\cd1\install %1 %2 %3 %4 %5 +popd diff --git a/JSHELL.CPP b/JSHELL.CPP new file mode 100644 index 0000000..91cdb0d --- /dev/null +++ b/JSHELL.CPP @@ -0,0 +1,429 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\jshell.cpv 2.18 16 Oct 1995 16:51:12 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : JSHELL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 2, 1994 * + * * + * Last Update : May 11, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Build_Translucent_Table -- Creates a translucent control table. * + * Translucent_Table_Size -- Determines the size of a translucent table. * + * Conquer_Build_Translucent_Table -- Builds fading table for shadow colors only. * + * Load_Alloc_Data -- Allocates a buffer and loads the file into it. * + * Load_Uncompress -- Loads and uncompresses data to a buffer. * + * Fatal -- General purpose fatal error handler. * + * Set_Window -- Sets the window dimensions to that specified. * + * Small_Icon -- Create a small icon from a big one. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "wwfile.h" + + +/*********************************************************************************************** + * Small_Icon -- Create a small icon from a big one. * + * * + * This routine will extract the specified icon from the icon data file and convert that * + * incon into a small (3x3) representation. Typicall use of this mini-icon is for the radar * + * map. * + * * + * INPUT: iconptr -- Pointer to the icon data file. * + * * + * iconnum -- The embedded icon number to convert into a small image. * + * * + * OUTPUT: Returns with a pointer to the small icon imagery. This is exactly 9 bytes long. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/11/1995 JLB : Created. * + *=============================================================================================*/ +void * Small_Icon(void const * iconptr, int iconnum) +{ + static unsigned char _icon[9]; + IControl_Type const * iptr = (IControl_Type const *)iconptr; + unsigned char * data; + + if (iconptr) { + iconnum = iptr->Map[iconnum]; + data = &iptr->Icons[iconnum*(24*24)]; + + for (int index = 0; index < 9; index++) { + int _offsets[9] = { + 4+4*24, + 12+4*24, + 20+4*24, + 4+12*24, + 12+12*24, + 20+12*24, + 4+20*24, + 12+20*24, + 20+20*24 + }; + _icon[index] = data[_offsets[index]]; + } + } + + return(_icon); +} + + +/*********************************************************************************************** + * Set_Window -- Sets the window dimensions to that specified. * + * * + * Use this routine to set the windows dimensions to the coordinates and dimensions * + * specified. * + * * + * INPUT: x -- Window X pixel position. * + * * + * y -- Window Y pixel position. * + * * + * w -- Window width in pixels. * + * * + * h -- Window height in pixels. * + * * + * OUTPUT: none * + * * + * WARNINGS: The X and width values are truncated to an even 8 pixel boundary. This is * + * the same as stripping off the lower 3 bits. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void Set_Window(int window, int x, int y, int w, int h) +{ + WindowList[window][WINDOWWIDTH] = w >> 3; + WindowList[window][WINDOWHEIGHT] = h; + WindowList[window][WINDOWX] = x >> 3; + WindowList[window][WINDOWY] = y; +} + + +/*********************************************************************************************** + * Fatal -- General purpose fatal error handler. * + * * + * This is a very simple general purpose fatal error handler. It goes directly to text * + * mode, prints the error, and then aborts with a failure code. * + * * + * INPUT: message -- The text message to display. * + * * + * ... -- Any optional parameters that are used in formatting the message. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine never returns. The game exits immediately. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void Fatal(char const *message, ...) +{ + va_list va; + + va_start(va, message); + Prog_End(); + vfprintf(stderr, message, va); + Mono_Printf(message); + exit(EXIT_FAILURE); +} + + +#ifdef NEVER +void File_Fatal(char const *message) +{ + Prog_End(); + perror(message); + exit(EXIT_FAILURE); +} +#endif + + + +/*********************************************************************************************** + * Load_Uncompress -- Loads and uncompresses data to a buffer. * + * * + * This is the C++ counterpart to the Load_Uncompress function. It will load the file * + * specified into the graphic buffer indicated and uncompress it. * + * * + * INPUT: file -- The file to load and uncompress. * + * * + * uncomp_buff -- The graphic buffer that initial loading will use. * + * * + * dest_buff -- The buffer that will hold the uncompressed data. * + * * + * reserved_data -- This is an optional pointer to a buffer that will hold any * + * reserved data the compressed file may contain. This is * + * typically a palette. * + * * + * OUTPUT: Returns with the size of the uncompressed data in the destination buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +long Load_Uncompress(FileClass &file, BuffType &uncomp_buff, BuffType &dest_buff, void *reserved_data) +{ + unsigned short size; + void *sptr = uncomp_buff.Get_Buffer(); + void *dptr = dest_buff.Get_Buffer(); + int opened = false; + CompHeaderType header; + + /* + ** The file must be opened in order to be read from. If the file + ** isn't opened, then open it. Record this fact so that it can be + ** restored to its closed state at the end. + */ + if (!file.Is_Open()) { + if (!file.Open()) { + return(0); + } + opened = true; + } + + /* + ** Read in the size of the file (supposedly). + */ + file.Read(&size, sizeof(size)); + + /* + ** Read in the header block. This block contains the compression type + ** and skip data (among other things). + */ + file.Read(&header, sizeof(header)); + size -= sizeof(header); + + /* + ** If there are skip bytes then they must be processed. Either read + ** them into the buffer provided or skip past them. No check is made + ** to ensure that the reserved data buffer is big enough (watch out!). + */ + if (header.Skip) { + size -= header.Skip; + if (reserved_data) { + file.Read(reserved_data, header.Skip); + } else { + file.Seek(header.Skip, SEEK_CUR); + } + header.Skip = 0; + } + + /* + ** Determine where is the proper place to load the data. If both buffers + ** specified are identical, then the data should be loaded at the end of + ** the buffer and decompressed at the beginning. + */ + if (uncomp_buff.Get_Buffer() == dest_buff.Get_Buffer()) { + sptr = Add_Long_To_Pointer(sptr, uncomp_buff.Get_Size()-(size+sizeof(header))); + } + + /* + ** Read in the bulk of the data. + */ + Mem_Copy(&header, sptr, sizeof(header)); + file.Read(Add_Long_To_Pointer(sptr, sizeof(header)), size); + + /* + ** Decompress the data. + */ + size = (unsigned int) Uncompress_Data(sptr, dptr); + + /* + ** Close the file if necessary. + */ + if (opened) { + file.Close(); + } + return((long)size); +} + + +/*********************************************************************************************** + * Load_Alloc_Data -- Allocates a buffer and loads the file into it. * + * * + * This is the C++ replacement for the Load_Alloc_Data function. It will allocate the * + * memory big enough to hold the file and then read the file into it. * + * * + * INPUT: file -- The file to read. * + * * + * mem -- The memory system to use for allocation. * + * * + * OUTPUT: Returns with a pointer to the allocated and filled memory block. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void * Load_Alloc_Data(FileClass &file) +{ + void *ptr = 0; + long size = file.Size(); + + ptr = new char [size]; + if (ptr) { + file.Read(ptr, size); + } + return(ptr); +} + + +/*********************************************************************************************** + * Translucent_Table_Size -- Determines the size of a translucent table. * + * * + * Use this routine to determine how big the translucent table needs * + * to be given the specified number of colors. This value is typically * + * used when allocating the buffer for the translucent table. * + * * + * INPUT: count -- The number of colors that are translucent. * + * * + * OUTPUT: Returns the size of the translucent table. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/02/1994 JLB : Created. * + *=============================================================================================*/ +long Translucent_Table_Size(int count) +{ + return(256L + (256L * count)); +} + + +/*********************************************************************************************** + * Build_Translucent_Table -- Creates a translucent control table. * + * * + * The table created by this routine is used by Draw_Shape (GHOST) to * + * achieve a translucent affect. The original color of the shape will * + * show through. This differs from the fading effect, since that * + * affect only alters the background color toward a single destination * + * color. * + * * + * INPUT: palette -- Pointer to the control palette. * + * * + * control -- Pointer to array of structures that control how * + * the translucent table will be built. * + * * + * count -- The number of entries in the control array. * + * * + * buffer -- Pointer to buffer to place the translucent table. * + * If NULL is passed in, then the buffer will be * + * allocated. * + * * + * OUTPUT: Returns with pointer to the translucent table. * + * * + * WARNINGS: This routine is exceedingly slow. Use sparingly. * + * * + * HISTORY: * + * 04/02/1994 JLB : Created. * + *=============================================================================================*/ +void *Build_Translucent_Table(void const *palette, TLucentType const *control, int count, void *buffer) +{ + unsigned char const *table; // Remap table pointer. + int index; // Working color index. + + if (count && control && palette) { + if (!buffer) { + buffer = new char [Translucent_Table_Size(count)]; + } + + if (buffer) { + memset(buffer, -1, 256); + table = (unsigned char*)Add_Long_To_Pointer((void*)buffer, 256); + + /* + ** Build the individual remap tables for each translucent color. + */ + for (index = 0; index < count; index++) { + ((unsigned char*)buffer)[control[index].SourceColor] = index; + Build_Fading_Table(palette, (void*)table, control[index].DestColor, control[index].Fading); + table = (unsigned char*)Add_Long_To_Pointer((void*)table, 256); + } + } + } + return(buffer); +} + + +/*********************************************************************************************** + * Conquer_Build_Translucent_Table -- Builds fading table for shadow colors only. * + * * + * This routine will build a translucent (fading) table to remap colors into the shadow * + * color region of the palette. Shadow colors are not affected by this translucent table. * + * This means that a shape can be overlapped any number of times and the imagery will * + * remain deterministic (and constant). * + * * + * INPUT: palette -- Pointer to the palette to base the translucent process on. * + * * + * control -- Pointer to special control structure that specifies the * + * target color, and percentage of fade. * + * * + * count -- The number of colors to be remapped (entries in the control array). * + * * + * buffer -- Pointer to the staging buffer that will hold the translucent table * + * data. If this parameter is NULL, then an appropriate sized table * + * will be allocated. * + * * + * OUTPUT: Returns with a pointer to the translucent table data. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1994 JLB : Created. * + *=============================================================================================*/ +void *Conquer_Build_Translucent_Table(void const *palette, TLucentType const *control, int count, void *buffer) +{ + unsigned char const *table; // Remap table pointer. + int index; // Working color index. + + if (count && control && palette) { + if (!buffer) { + buffer = new char [Translucent_Table_Size(count)]; + } + + if (buffer) { + memset(buffer, -1, 256); + table = (unsigned char*)Add_Long_To_Pointer((void*)buffer, 256); + + /* + ** Build the individual remap tables for each translucent color. + */ + for (index = 0; index < count; index++) { + ((unsigned char*)buffer)[control[index].SourceColor] = index; + Conquer_Build_Fading_Table(palette, (void*)table, control[index].DestColor, control[index].Fading); + table = (unsigned char*)Add_Long_To_Pointer((void*)table, 256); + } + } + } + return(buffer); +} + + diff --git a/JSHELL.H b/JSHELL.H new file mode 100644 index 0000000..275f844 --- /dev/null +++ b/JSHELL.H @@ -0,0 +1,229 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\jshell.h_v 2.16 16 Oct 1995 16:45:06 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : JSHELL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/13/95 * + * * + * Last Update : March 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef JSHELL_H +#define JSHELL_H + +/* +** Interface class to the keyboard. This insulates the game from library vagaries. Most +** notable being the return values are declared as "int" in the library whereas C&C +** expects it to be of KeyNumType. +*/ +class Keyboard +{ + public: + static KeyNumType Get(void) {return (KeyNumType)Get_Key_Num();}; + static KeyNumType Check(void) {return (KeyNumType)Check_Key_Num();}; + static KeyASCIIType To_ASCII(KeyNumType key) {return (KeyASCIIType)KN_To_KA(key);}; + static void Clear(void) {Clear_KeyBuffer();}; + static void Stuff(KeyNumType key) {Stuff_Key_Num(key);}; + static int Down(KeyNumType key) {return Key_Down(key);}; + static int Mouse_X(void) {return Get_Mouse_X();}; + static int Mouse_Y(void) {return Get_Mouse_Y();}; +}; + + +#ifdef NEVER +inline void * operator delete(void * data) +{ + Free(data); +} + +inline void * operator delete[] (void * data) +{ + Free(data); +} +#endif + + +/* +** These templates allow enumeration types to have simple bitwise +** arithmatic performed. The operators must be instatiated for the +** enumerated types desired. +*/ +template inline T operator ++(T & a) +{ + a = (T)((int)a + (int)1); + return(a); +} +template inline T operator ++(T & a, int) +{ + T aa = a; + a = (T)((int)a + (int)1); + return(aa); +} +template inline T operator --(T & a) +{ + a = (T)((int)a - (int)1); + return(a); +} +template inline T operator --(T & a, int) +{ + T aa = a; + a = (T)((int)a - (int)1); + return(aa); +} +template inline T operator |(T t1, T t2) +{ + return((T)((int)t1 | (int)t2)); +} +template inline T operator &(T t1, T t2) +{ + return((T)((int)t1 & (int)t2)); +} +template inline T operator ~(T t1) +{ + return((T)(~(int)t1)); +} + + +/* +** The shape flags are likely to be "or"ed together and other such bitwise +** manipulations. These instatiated operator templates allow this. +*/ +inline ShapeFlags_Type operator |(ShapeFlags_Type, ShapeFlags_Type); +inline ShapeFlags_Type operator &(ShapeFlags_Type, ShapeFlags_Type); +inline ShapeFlags_Type operator ~(ShapeFlags_Type); + + +void Set_Bit(void * array, int bit, int value); +#pragma aux Set_Bit parm [esi] [ecx] [eax] \ + modify [esi ebx] = \ + "mov ebx,ecx" \ + "shr ebx,5" \ + "and ecx,01Fh" \ + "btr [esi+ebx*4],ecx" \ + "or eax,eax" \ + "jz ok" \ + "bts [esi+ebx*4],ecx" \ + "ok:" + +int Get_Bit(void const * array, int bit); +#pragma aux Get_Bit parm [esi] [eax] \ + modify [esi ebx] \ + value [eax] = \ + "mov ebx,eax" \ + "shr ebx,5" \ + "and eax,01Fh" \ + "bt [esi+ebx*4],eax" \ + "setc al" + +int First_True_Bit(void const * array); +#pragma aux First_True_Bit parm [esi] \ + modify [esi ebx] \ + value [eax] = \ + "mov eax,-32" \ + "again:" \ + "add eax,32" \ + "mov ebx,[esi]" \ + "add esi,4" \ + "bsf ebx,ebx" \ + "jz again" \ + "add eax,ebx" + +int First_False_Bit(void const * array); +#pragma aux First_False_Bit parm [esi] \ + modify [esi ebx] \ + value [eax] = \ + "mov eax,-32" \ + "again:" \ + "add eax,32" \ + "mov ebx,[esi]" \ + "not ebx" \ + "add esi,4" \ + "bsf ebx,ebx" \ + "jz again" \ + "add eax,ebx" + +extern int Bound(int original, int min, int max); +#pragma aux Bound parm [eax] [ebx] [ecx] \ + modify [eax] \ + value [eax] = \ + "cmp ebx,ecx" \ + "jl okorder" \ + "xchg ebx,ecx" \ + "okorder: cmp eax,ebx" \ + "jg okmin" \ + "mov eax,ebx" \ + "okmin: cmp eax,ecx" \ + "jl okmax" \ + "mov eax,ecx" \ + "okmax:" + +#ifdef NEVER +extern unsigned Bound(unsigned original, unsigned min, unsigned max); +#pragma aux Bound parm [eax] [ebx] [ecx] \ + modify [eax] \ + value [eax] = \ + "cmp ebx,ecx" \ + "jb okorder" \ + "xchg ebx,ecx" \ + "okorder: cmp eax,ebx" \ + "ja okmin" \ + "mov eax,ebx" \ + "okmin: cmp eax,ecx" \ + "jb okmax" \ + "mov eax,ecx" \ + "okmax:" +#endif + + +unsigned Fixed_To_Cardinal(unsigned base, unsigned fixed); +#pragma aux Fixed_To_Cardinal parm [eax] [edx] \ + modify [edx] \ + value [eax] = \ + "mul edx" \ + "add eax,080h" \ + "test eax,0FF000000h" \ + "jz ok" \ + "mov eax,000FFFFFFh" \ + "ok:" \ + "shr eax,8" + + +unsigned Cardinal_To_Fixed(unsigned base, unsigned cardinal); +#pragma aux Cardinal_To_Fixed parm [ebx] [eax] \ + modify [edx] \ + value [eax] = \ + "or ebx,ebx" \ + "jz fini" \ + "shl eax,8" \ + "xor edx,edx" \ + "div ebx" \ + "fini:" + +#endif diff --git a/KEYFBUFF.ASM b/KEYFBUFF.ASM new file mode 100644 index 0000000..7e73b04 --- /dev/null +++ b/KEYFBUFF.ASM @@ -0,0 +1,4854 @@ +; +; Command & Conquer(tm) +; Copyright 2025 Electronic Arts Inc. +; +; 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 3 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 General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . +; + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Command & Conquer * +;* * +;* File Name : KEYFBUFF.ASM * +;* * +;* Programmer : David R. Dettmer * +;* * +;* Start Date : March 3, 1995 * +;* * +;* Last Update : * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Buffer_Frame_To_Page -- Copies a linear buffer to a virtual viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +;********************** Model & Processor Directives *********************** +IDEAL +P386 +MODEL USE32 FLAT +jumps + +;******************************** Includes ********************************* +INCLUDE "gbuffer.inc" +include "profile.inc" + + +;******************************** Equates ********************************** + +TRUE equ 1 ; Boolean 'true' value +FALSE equ 0 ; Boolean 'false' value + +;*=========================================================================*/ +;* The following are defines used to control what functions are linked * +;* in for Buffer_Frame_To_Page. * +;*=========================================================================*/ +;USE_NORMAL EQU TRUE +;USE_HORZ_REV EQU TRUE +;USE_VERT_REV EQU TRUE +;USE_SCALING EQU TRUE + + +FLAG_NORMAL EQU 0 +FLAG_TRANS EQU 1 +FLAG_GHOST EQU 2 +FLAG_FADING EQU 4 +FLAG_PREDATOR EQU 8 + +FLAG_MASK EQU 0Fh + + +SHAPE_NORMAL EQU 0000h ; Standard shape +;SHAPE_HORZ_REV EQU 0001h ; Flipped horizontally +;SHAPE_VERT_REV EQU 0002h ; Flipped vertically +;SHAPE_SCALING EQU 0004h ; Scaled (WORD scale_x, WORD scale_y) +;SHAPE_VIEWPORT_REL EQU 0010h ; Coords are window-relative +;SHAPE_WIN_REL EQU 0010h ; Coordinates are window relative instead of absolute. +SHAPE_CENTER EQU 0020h ; Coords are based on shape's center pt +SHAPE_TRANS EQU 0040h ; has transparency + +SHAPE_FADING EQU 0100h ; Fading effect (VOID * fading_table, + ; WORD fading_num) +SHAPE_PREDATOR EQU 0200h ; Transparent warping effect +;SHAPE_COMPACT EQU 0400h ; Never use this bit +;SHAPE_PRIORITY EQU 0800h ; Use priority system when drawing +SHAPE_GHOST EQU 1000h ; Shape is drawn ghosted +;SHAPE_SHADOW EQU 2000h +SHAPE_PARTIAL EQU 4000h +;SHAPE_COLOR EQU 8000h ; Remap the shape's colors + ; (VOID * color_table) + + +; +;.......................... Shadow Effect .................................. +; +SHADOW_COL EQU 00FFh ; magic number for shadows + +;......................... Priority System ................................. +; +CLEAR_UNUSED_BITS EQU 0007h ; and with 0000-0111 to clear + ; non-walkable high bit and + ; scaling id bits +NON_WALKABLE_BIT EQU 0080h ; and with 1000-0000 to clear all + ; but non-walkable bit +; +;......................... Predator Effect ................................. +; +;PRED_MASK EQU 0007h ; mask used for predator pixel puts +PRED_MASK EQU 000Eh ; mask used for predator pixel puts + + +;--------------------------------------------------------------------------- +; +; Use a macro to make code a little cleaner. +; The parameter varname is optional. +; Syntax to use macro is : +; WANT equ expression +; USE func [,varname] +; If the 'varname' is defined, a table declaration is created like: +; GLOBAL TableName:DWORD +; Then, the table entry is created: +; If WANT is true, the table entry is created for the given function: +; varname DD func +; If WANT is not TRUE, a Not_Supported entry is put in the table: +; varname DD Not_Supported +; The resulting tables look like: +; +; GLOBAL ExampTable:DWORD +; ExampTable DD routine1 +; DD routine2 +; DD routine3 +; ... +; Thus, each table is an array of function pointers. +; +;--------------------------------------------------------------------------- +MACRO USE func, varname + IF WANT + varname DD func + ELSE + varname DD Not_Supported + ENDIF +ENDM + +; IFNB +; GLOBAL varname:DWORD +; ENDIF + +;--------------------------------------------------------------------------- + + +DATASEG + +;--------------------------------------------------------------------------- +; Predator effect variables +;--------------------------------------------------------------------------- +; make table for negative offset and use the used space for other variables + +BFPredNegTable DW -1, -3, -2, -5, -2, -4, -3, -1 + ; 8 words below calculated + DW 0, 0, 0, 0, 0, 0, 0, 0 ; index ffffff00 + DD 0, 0, 0, 0 ; index ffffff10 +BFPredOffset DD 0, 0, 0, 0 ; index ffffff20 + DD 0, 0, 0, 0 ; index ffffff30 + ; partially faded predator effect value +BFPartialPred DD 0, 0, 0, 0 ; index ffffff40 +BFPartialCount DD 0, 0, 0, 0 ; index ffffff50 + DD 0, 0, 0, 0 ; index ffffff60 + DD 0, 0, 0, 0 ; index ffffff70 + DD 0, 0, 0, 0 ; index ffffff80 + DD 0, 0, 0, 0 ; index ffffff90 + DD 0, 0, 0, 0 ; index ffffffa0 + DD 0, 0, 0, 0 ; index ffffffb0 + DD 0, 0, 0, 0 ; index ffffffc0 + DD 0, 0, 0, 0 ; index ffffffd0 + DD 0, 0, 0, 0 ; index ffffffe0 + DD 0, 0, 0, 0 ; index fffffff0 +BFPredTable DW 1, 3, 2, 5, 2, 3, 4, 1 +;BFPredTable DB 1, 3, 2, 5, 4, 3, 2, 1 + + + + + + + global C BigShapeBufferStart:dword + global C UseBigShapeBuffer:dword + global C TheaterShapeBufferStart:dword + global C IsTheaterShape:dword + global C Single_Line_Trans_Entry:near + global C Next_Line:near + global C MMX_Done:near + global C MMXAvailable:dword + global EndNewShapeJumpTable:byte + global NewShapeJumpTable:dword + + +;********************************************************************************** +; +; Jump tables for new line header system +; +; Each line of shape data now has a header byte which describes the data on the line. +; + +; +; Header byte control bits +; +BLIT_TRANSPARENT =1 +BLIT_GHOST =2 +BLIT_FADING =4 +BLIT_PREDATOR =8 +BLIT_SKIP =16 +BLIT_ALL =BLIT_TRANSPARENT or BLIT_GHOST or BLIT_FADING or BLIT_PREDATOR or BLIT_SKIP + + + struc ShapeHeaderType + + draw_flags dd ? + shape_data dd ? + shape_buffer dd ? + + ends + +; +; Global definitions for routines that draw a single line of a shape +; + global Short_Single_Line_Copy:near + global Single_Line_Trans:near + global Single_Line_Ghost:near + global Single_Line_Ghost_Trans:near + global Single_Line_Fading:near + global Single_Line_Fading_Trans:near + global Single_Line_Ghost_Fading:near + global Single_Line_Ghost_Fading_Trans:near + global Single_Line_Predator:near + global Single_Line_Predator_Trans:near + global Single_Line_Predator_Ghost:near + global Single_Line_Predator_Ghost_Trans:near + global Single_Line_Predator_Fading:near + global Single_Line_Predator_Fading_Trans:near + global Single_Line_Predator_Ghost_Fading:near + global Single_Line_Predator_Ghost_Fading_Trans:near + global Single_Line_Skip:near + + global Single_Line_Single_Fade:near + global Single_Line_Single_Fade_Trans:near + + + +label NewShapeJumpTable dword + +; +; Jumptable for shape line drawing with no flags set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy +label CriticalFadeRedirections dword + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with transparent flags set +; + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with ghost flags set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with ghost and transparent flags set +; + + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + + + + +; +; Jumptable for shape line drawing routines with fading flag set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade + dd Single_Line_Fading + dd Single_Line_Fading + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Fading + dd Single_Line_Fading + dd Single_Line_Fading + dd Single_Line_Fading + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with fading and transparent flags set +; + + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade_Trans + dd Single_Line_Fading + dd Single_Line_Fading_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Fading + dd Single_Line_Fading_Trans + dd Single_Line_Fading + dd Single_Line_Fading_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with fading and ghost flags set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade + dd Single_Line_Ghost_Fading + dd Single_Line_Ghost_Fading + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Single_Line_Fading + dd Single_Line_Fading + dd Single_Line_Ghost_Fading + dd Single_Line_Ghost_Fading + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + + +; +; Jumptable for shape line drawing routines with fading, transparent and ghost flags set +; + + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade_Trans + dd Single_Line_Ghost_Fading + dd Single_Line_Ghost_Fading_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Single_Line_Fading + dd Single_Line_Fading_Trans + dd Single_Line_Ghost_Fading + dd Single_Line_Ghost_Fading_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + + + + + +; +; Jumptable for shape line drawing with predator flag set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with transparent and predator flags set +; + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with ghost and predator flags set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Ghost + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Ghost + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with ghost and transparent and predator flags set +; + + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Ghost_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Ghost_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + + + + +; +; Jumptable for shape line drawing routines with fading and predator flags set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade + dd Single_Line_Fading + dd Single_Line_Fading + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with fading and transparent and predator flags set +; + + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade_Trans + dd Single_Line_Fading + dd Single_Line_Fading_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading_Trans + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +; +; Jumptable for shape line drawing routines with fading and ghost and predator flags set +; + + dd Short_Single_Line_Copy + dd Short_Single_Line_Copy + dd Single_Line_Ghost + dd Single_Line_Ghost + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade + dd Single_Line_Ghost_Fading + dd Single_Line_Ghost_Fading + dd Single_Line_Predator + dd Single_Line_Predator + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Ghost_Fading + dd Single_Line_Predator_Ghost_Fading + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + + + + + + +; +; Jumptable for shape line drawing routines with all flags set +; + +label AllFlagsJumpTable dword + dd Short_Single_Line_Copy + dd Single_Line_Trans + dd Single_Line_Ghost + dd Single_Line_Ghost_Trans + dd Single_Line_Single_Fade + dd Single_Line_Single_Fade_Trans + dd Single_Line_Ghost_Fading + dd Single_Line_Ghost_Fading_Trans + dd Single_Line_Predator + dd Single_Line_Predator_Trans + dd Single_Line_Predator_Ghost + dd Single_Line_Predator_Ghost_Trans + dd Single_Line_Predator_Fading + dd Single_Line_Predator_Fading_Trans + dd Single_Line_Predator_Ghost_Fading + dd Single_Line_Predator_Ghost_Fading_Trans + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + dd Single_Line_Skip + + +label EndNewShapeJumpTable byte + +CODESEG + +;--------------------------------------------------------------------------- +; Code Segment Tables: +; This code uses the USE macro to set up tables of function addresses. +; The tables have the following format: +; Tables defined are: +; BufferFrameTable +;--------------------------------------------------------------------------- + +WANT equ +USE BF_Copy, BufferFrameTable + +WANT equ +USE BF_Trans + +WANT equ +USE BF_Ghost + +WANT equ +USE BF_Ghost_Trans + +WANT equ +USE BF_Fading + +WANT equ +USE BF_Fading_Trans + +WANT equ +USE BF_Ghost_Fading + +WANT equ +USE BF_Ghost_Fading_Trans + +WANT equ +USE BF_Predator + +WANT equ +USE BF_Predator_Trans + +WANT equ +USE BF_Predator_Ghost + +WANT equ +USE BF_Predator_Ghost_Trans + +WANT equ +USE BF_Predator_Fading + +WANT equ +USE BF_Predator_Fading_Trans + +WANT equ +USE BF_Predator_Ghost_Fading + +WANT equ +USE BF_Predator_Ghost_Fading_Trans + + + + + +;--------------------------------------------------------------------------- + + + + +;********************************************************************************************* +;* Set_Shape_Header -- create the line header bytes for a shape * +;* * +;* INPUT: Shape width * +;* Shape height * +;* ptr to raw shape data * +;* ptr to shape headers * +;* shape flags * +;* ptr to translucency table * +;* IsTranslucent * +;* * +;* OUTPUT: none * +;* * +;* Warnings: * +;* * +;* HISTORY: * +;* 11/29/95 10:09AM ST : Created. * +;*===========================================================================================* + + proc Setup_Shape_Header C near + + ARG pixel_width :DWORD ; width of rectangle to blit + ARG pixel_height :DWORD ; height of rectangle to blit + ARG src :DWORD ; this is a member function + ARG headers :DWORD + ARG flags :DWORD + ARG Translucent :DWORD + ARG IsTranslucent :DWORD + LOCAL trans_count :DWORD + + pushad + + + mov esi,[src] ;ptr to raw shape data + mov edi,[headers] ;ptr to headers we are going to set up + mov eax,[flags] + and eax,SHAPE_TRANS or SHAPE_FADING or SHAPE_PREDATOR or SHAPE_GHOST + mov [(ShapeHeaderType edi).draw_flags],eax ;save old flags in header + add edi,size ShapeHeaderType + mov edx,[pixel_height] ;number of shape lines to scan + +??outer_loop: mov ecx,[pixel_width] ;number of pixels in shape line + xor bl,bl ;flag the we dont know anything about this line yet + mov [trans_count],0 ;we havnt scanned any transparent pixels yet + +; +; Scan one shape line to see what kind of data it contains +; +??inner_loop: xor eax,eax + mov al,[esi] + inc esi + +; +; Check for transparent pixel +; + test al,al + jnz ??not_transp + test [flags],SHAPE_TRANS + jz ??not_transp + or bl,BLIT_TRANSPARENT ;flag that pixel is transparent + inc [trans_count] ;keep count of the number of transparent pixels on the line + jmp ??end_lp + +; +; Check for predator effect on this line +; +??not_transp: test [flags],SHAPE_PREDATOR + jz ??not_pred + or bl,BLIT_PREDATOR + +; +; Check for ghost effects +; +??not_pred: test [flags],SHAPE_GHOST + jz ??not_ghost + push edi + mov edi,[IsTranslucent] + cmp [byte edi+eax],-1 + pop edi + jz ??not_ghost + or bl,BLIT_GHOST + +; +; Check if fading is required +; +??not_ghost: test [flags],SHAPE_FADING + jz ??end_lp + or bl,BLIT_FADING + +??end_lp: dec ecx + jnz ??inner_loop + + +; +; Interpret the info we have collected and decide which routine will be +; used to draw this line +; + xor bh,bh + + test bl,BLIT_TRANSPARENT + jz ??no_transparencies + or bh,BLIT_TRANSPARENT + mov ecx,[pixel_width] + cmp ecx,[trans_count] + jnz ??not_all_trans + +; all pixels in the line were transparent so we dont need to draw it at all + mov bh,BLIT_SKIP + jmp ??got_line_type + +??not_all_trans: +??no_transparencies: + mov al,bl + and al,BLIT_PREDATOR + or bh,al + mov al,bl + and al,BLIT_GHOST + or bh,al + mov al,bl + and al,BLIT_FADING + or bh,al + +; +; Save the line header and do the next line +; +??got_line_type:mov [edi],bh + inc edi + + dec edx + jnz ??outer_loop + + + popad + ret + + endp Setup_Shape_Header + + + + +;************************************************************** +; +; Macro to fetch the header of the next line and jump to the appropriate routine +; + macro next_line + + add edi , [ dest_adjust_width ] ;add in dest modulo + dec edx ;line counter + jz ??real_out ;return + mov ecx,[save_ecx] ;ecx is pixel count + mov eax,[header_pointer] ;ptr to next header byte + mov al,[eax] + inc [header_pointer] + and eax,BLIT_ALL ;Make sure we dont jump to some spurious address + ; if the header is wrong then its better to draw with the wrong + ; shape routine than to die + shl eax,2 + add eax,[ShapeJumpTableAddress] ;get the address to jump to + jmp [dword eax] ;do the jump + + endm + + + + + + +;*************************************************************************** +;* VVC::TOPAGE -- Copies a linear buffer to a virtual viewport * +;* * +;* INPUT: WORD x_pixel - x pixel on viewport to copy from * +;* WORD y_pixel - y pixel on viewport to copy from * +;* WORD pixel_width - the width of copy region * +;* WORD pixel_height - the height of copy region * +;* BYTE * src - buffer to copy from * +;* VVPC * dest - virtual viewport to copy to * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed * +;* the boundaries. In the event that no adjustment is * +;* possible this routine will abort. If the size of the * +;* region to copy exceeds the size passed in for the buffer * +;* the routine will automatically abort. * +;* * +;* HISTORY: * +;* 06/15/1994 PWG : Created. * +;*=========================================================================* + GLOBAL C Buffer_Frame_To_Page:NEAR + PROC Buffer_Frame_To_Page C near + USES eax,ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* define the arguements that our function takes. + ;*=================================================================== + ARG x_pixel :DWORD ; x pixel position in source + ARG y_pixel :DWORD ; y pixel position in source + ARG pixel_width :DWORD ; width of rectangle to blit + ARG pixel_height:DWORD ; height of rectangle to blit + ARG src :DWORD ; this is a member function + ARG dest :DWORD ; what are we blitting to + + ARG flags :DWORD ; flags passed + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + LOCAL IsTranslucent :DWORD ; ptr to the is_translucent table + LOCAL Translucent :DWORD ; ptr to the actual translucent table + LOCAL FadingTable :DWORD ; extracted fading table pointer + LOCAL FadingNum :DWORD ; get the number of times to fade + + LOCAL StashECX :DWORD ; temp variable for ECX register + + LOCAL jflags :DWORD ; flags used to goto correct buff frame routine + LOCAL BufferFrameRout :DWORD ; ptr to the buffer frame routine + + LOCAL jmp_loc :DWORD ; calculated jump location + LOCAL loop_cnt :DWORD + + LOCAL x1_pixel :DWORD + LOCAL y1_pixel :DWORD + LOCAL scr_x :DWORD + LOCAL scr_y :DWORD + LOCAL dest_adjust_width :DWORD + LOCAL scr_adjust_width :DWORD + LOCAL header_pointer :DWORD + LOCAL use_old_draw :DWORD + LOCAL save_ecx :DWORD + LOCAL ShapeJumpTableAddress :DWORD + LOCAL shape_buffer_start :DWORD + + prologue + cmp [ src ] , 0 + jz ??real_out + + ; + ; Save the line attributes pointers and + ; Modify the src pointer to point to the actual image + ; + cmp [UseBigShapeBuffer],0 + jz ??do_args ;just use the old shape drawing system + + mov edi,[src] + mov [header_pointer],edi + + mov eax,[BigShapeBufferStart] + cmp [(ShapeHeaderType edi).shape_buffer],0 + jz ??is_ordinary_shape + mov eax,[TheaterShapeBufferStart] +??is_ordinary_shape: + mov [shape_buffer_start],eax + + mov edi,[(ShapeHeaderType edi).shape_data] + add edi,[shape_buffer_start] + mov [src],edi + mov [use_old_draw],0 + + + ;==================================================================== + ; Pull off optional arguments: + ; EDI is used as an offset from the 'flags' parameter, to point + ; to the optional argument currently being processed. + ;==================================================================== +??do_args: + mov edi , 4 ; optional params start past flags + mov [ jflags ] , 0 ; clear jump flags + +??check_centering: + ;------------------------------------------------------------------- + ; See if we need to center the frame + ;------------------------------------------------------------------- + test [ flags ] , SHAPE_CENTER ; does this need to be centered? + je ??check_trans ; if not the skip over this stuff + + mov eax , [ pixel_width ] + mov ebx , [ pixel_height ] + sar eax , 1 + sar ebx , 1 + sub [ x_pixel ] , eax + sub [ y_pixel ] , ebx + +??check_trans: + test [ flags ] , SHAPE_TRANS + jz ??check_ghost + + or [ jflags ] , FLAG_TRANS + + ;-------------------------------------------------------------------- + ; SHAPE_GHOST: DWORD is_translucent tbl + ;-------------------------------------------------------------------- +??check_ghost: + test [ flags ] , SHAPE_GHOST ; are we ghosting this shape + jz ??check_fading + + mov eax , [ flags + edi ] + or [ jflags ] , FLAG_GHOST + mov [ IsTranslucent ] , eax ; save ptr to is_trans. tbl + add eax , 0100h ; add 256 for first table + add edi , 4 ; next argument + mov [ Translucent ] , eax ; save ptr to translucent tbl + + + +??check_fading: + ;______________________________________________________________________ + ; If this is the first time through for this shape then + ; set up the shape header + ;______________________________________________________________________ + pushad + + cmp [UseBigShapeBuffer],0 + jz ??new_shape + + mov edi,[header_pointer] + cmp [(ShapeHeaderType edi).draw_flags],-1 + jz ??setup_headers + mov eax,[flags] ;Redo the shape headers if this shape was + and eax,SHAPE_TRANS or SHAPE_FADING or SHAPE_PREDATOR or SHAPE_GHOST ;initially set up with different flags + cmp eax,[(ShapeHeaderType edi).draw_flags] + jz ??no_header_setup +??new_shape: + mov [use_old_draw],1 + jmp ??no_header_setup + +??setup_headers: + push [IsTranslucent] + push [Translucent] + push [flags] + push [header_pointer] + push [src] + push [pixel_height] + push [pixel_width] + call Setup_Shape_Header + add esp,7*4 + mov [ShapeJumpTableAddress],offset AllFlagsJumpTable + jmp ??headers_set +??no_header_setup: + + xor eax,eax + test [flags],SHAPE_PREDATOR + jz ??not_shape_predator + or al,BLIT_PREDATOR + +??not_shape_predator: + test [flags],SHAPE_FADING + jz ??not_shape_fading + or al,BLIT_FADING + +??not_shape_fading: + + test [flags],SHAPE_TRANS + jz ??not_shape_transparent + or al,BLIT_TRANSPARENT + +??not_shape_transparent: + + test [flags],SHAPE_GHOST + jz ??not_shape_ghost + or al,BLIT_GHOST + +??not_shape_ghost: + + shl eax,7 + add eax,offset NewShapeJumpTable + mov [ShapeJumpTableAddress],eax + + +??headers_set: + popad + + ;-------------------------------------------------------------------- + ; SHAPE_FADING: DWORD fade_table[256], DWORD fade_count + ;-------------------------------------------------------------------- + test [ flags ] , SHAPE_FADING ; are we fading this shape + jz ??check_predator + + mov eax , [ flags + edi ] + mov [ FadingTable ] , eax ; save address of fading tbl + mov eax , [ flags + edi + 4 ] ; get fade num + or [ jflags ] , FLAG_FADING + and eax , 03fh ; no need for more than 63 + add edi , 8 ; next argument + cmp eax , 0 ; check if it's 0 + jnz ??set_fading ; if not, store fade num + + and [ flags ] , NOT SHAPE_FADING ; otherwise, don't fade + +??set_fading: + mov [ FadingNum ] , eax + + mov ebx,[ShapeJumpTableAddress] + mov [dword ebx+CriticalFadeRedirections-NewShapeJumpTable],offset Single_Line_Single_Fade + mov [dword ebx+CriticalFadeRedirections-NewShapeJumpTable+4],offset Single_Line_Single_Fade_Trans + cmp eax,1 + jz ??single_fade + mov [dword ebx+CriticalFadeRedirections-NewShapeJumpTable],offset Single_Line_Fading + mov [dword ebx+CriticalFadeRedirections-NewShapeJumpTable+4],offset Single_Line_Fading_Trans + +??single_fade: + + ;-------------------------------------------------------------------- + ; SHAPE_PREDATOR: DWORD init_pred_lookup_offset (0-7) + ;-------------------------------------------------------------------- +??check_predator: + test [ flags ] , SHAPE_PREDATOR ; is predator effect on + jz ??check_partial + + mov eax , [ flags + edi ] ; pull the partial value + or [ jflags ] , FLAG_PREDATOR + shl eax , 1 + cmp eax , 0 + jge ??check_range + + neg eax + mov ebx , -1 + and eax , PRED_MASK ; keep entries within bounds + mov bl , al + mov eax , ebx ; will be ffffff00-ffffff07 + jmp ??pred_cont + +??check_range: + and eax , PRED_MASK ; keep entries within bounds + +??pred_cont: + add edi , 4 ; next argument + mov [ BFPredOffset ] , eax + mov [ BFPartialCount ] , 0 ; clear the partial count + mov [ BFPartialPred ] , 100h ; init partial to off + +??pred_neg_init: + mov esi , [ dest ] ; get ptr to dest + mov ebx, 7 * 2 + +??pred_loop: + movzx eax , [ WORD PTR BFPredNegTable + ebx ] + add eax , [ (GraphicViewPort esi) . GVPWidth ] ; add width + add eax , [ (GraphicViewPort esi) . GVPXAdd ] ; add x add + add eax , [ (GraphicViewPort esi) . GVPPitch ] ; extra pitch of DD surface ST - 9/29/95 1:08PM + mov [ WORD PTR BFPredNegTable + 16 + ebx ] , ax + dec ebx + dec ebx + jge ??pred_loop + + ;-------------------------------------------------------------------- + ; SHAPE_PARTIAL: DWORD partial_pred_value (0-255) + ;-------------------------------------------------------------------- +??check_partial: + test [ flags ] , SHAPE_PARTIAL ; is this a partial pred? + jz ??setupfunc + + mov eax , [ flags + edi ] ; pull the partial value + add edi , 4 ; next argument + and eax , 0ffh ; make sure 0-255 + mov [ BFPartialPred ] , eax ; store it off + +??setupfunc: + mov ebx , [ jflags ] ; load flags value + and ebx , FLAG_MASK ; clip high bits + add ebx , ebx ; mult by 4 to get DWORD offset + add ebx , ebx + mov ebx , [ BufferFrameTable + ebx ] ; get table value + mov [ BufferFrameRout ] , ebx ; store it in the function pointer + +; Clip dest Rectangle against source Window boundaries. + + mov [ scr_x ] , 0 + mov [ scr_y ] , 0 + mov esi , [ dest ] ; get ptr to dest + xor ecx , ecx + xor edx , edx + mov edi , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov ebx , [ x_pixel ] + mov eax , [ x_pixel ] + add ebx , [ pixel_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[ ( GraphicViewPort esi) . GVPHeight ] ; get height into register + mov ebx , [ y_pixel ] + mov eax , [ y_pixel ] + add ebx , [ pixel_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz ??real_out + + or al , dl + jz ??do_blit + + mov [use_old_draw],1 + test cl , 1000b + jz ??dest_left_ok + + mov eax , [ x_pixel ] + neg eax + mov [ x_pixel ] , 0 + mov [ scr_x ] , eax + +??dest_left_ok: + test cl , 0010b + jz ??dest_bottom_ok + + mov eax , [ y_pixel ] + neg eax + mov [ y_pixel ] , 0 + mov [ scr_y ] , eax + +??dest_bottom_ok: + test dl , 0100b + jz ??dest_right_ok + + mov eax , [ (GraphicViewPort esi) . GVPWidth ] ; get width into register + mov [ x1_pixel ] , eax + +??dest_right_ok: + test dl , 0001b + jz ??do_blit + + mov eax , [ (GraphicViewPort esi) . GVPHeight ] ; get width into register + mov [ y1_pixel ] , eax + +??do_blit: + cld + mov eax , [ (GraphicViewPort esi) . GVPXAdd ] + add eax , [ (GraphicViewPort esi) . GVPPitch ] + add eax , [ (GraphicViewPort esi) . GVPWidth ] + mov edi , [ (GraphicViewPort esi) . GVPOffset ] + + mov ecx , eax + mul [ y_pixel ] + add edi , [ x_pixel ] + add edi , eax + + add ecx , [ x_pixel ] + sub ecx , [ x1_pixel ] + mov [ dest_adjust_width ] , ecx + + mov esi , [ src ] + mov eax , [ pixel_width ] + sub eax , [ x1_pixel ] + add eax , [ x_pixel ] + mov [ scr_adjust_width ] , eax + + mov eax , [ scr_y ] + mul [ pixel_width ] + add eax , [ scr_x ] + add esi , eax + +; +; If the shape needs to be clipped then we cant handle it with the new header systen +; so draw it with the old shape drawer +; + cmp [use_old_draw],0 + jnz ??use_old_stuff + + add [header_pointer],size ShapeHeaderType + mov edx,[pixel_height] + mov ecx,[pixel_width] + mov eax,[header_pointer] + mov al,[eax] + mov [save_ecx],ecx + inc [header_pointer] + and eax,BLIT_ALL + shl eax,2 + add eax,[ShapeJumpTableAddress] + jmp [dword eax] + + +??use_old_stuff: + mov edx , [ y1_pixel ] + mov eax , [ x1_pixel ] + + sub edx , [ y_pixel ] + jle ??real_out + + sub eax , [ x_pixel ] + jle ??real_out + + jmp [ BufferFrameRout ] ; buffer frame to viewport routine + +??real_out: + + cmp [MMXAvailable],0 + jz ??no_mmx_cleanup + call MMX_Done + +??no_mmx_cleanup: + epilogue + + ret + + +; ******************************************************************** +; Forward bitblit only +; the inner loop is so efficient that +; the optimal consept no longer apply because +; the optimal byte have to by a number greather than 9 bytes +; ******************************************************************** +global BF_Copy:near + +BF_Copy: + prologue + cmp eax , 10 + jl ??forward_loop_bytes + +??forward_loop_dword: + mov ecx , edi + mov ebx , eax + neg ecx + and ecx , 3 + sub ebx , ecx + rep movsb + mov ecx , ebx + shr ecx , 2 + rep movsd + mov ecx , ebx + and ecx , 3 + rep movsb + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??forward_loop_dword + + ret + +??forward_loop_bytes: + mov ecx , eax + rep movsb + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx ; decrement the height + jnz ??forward_loop_bytes + epilogue + + ret + + +;******************************************************************** +;******************************************************************** + + segment code page public use32 'code' ; Need stricter segment alignment + ; for pentium optimisations + + +; +; Expand the 'next_line' macro so we can jump to it +; +; +Next_Line: next_line + + + +;***************************************************************************** +; Draw a single line with transparent pixels +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Trans: + prologue + +Single_Line_Trans_Entry: + +??slt_mask_map_lp: ; Pentium pipeline usage + ;Pipe Cycles + mov al,[esi] ;U 1 + inc esi ;Vee 1 + + test al,al ;U 1 + jz ??slt_skip ;Vee 1/5 + +??slt_not_trans:mov [edi],al ;u 1 + + inc edi ;vee 1 + dec ecx ;u 1 + + jnz ??slt_mask_map_lp ;vee (maybe) 1 + +??slt_end_line: epilogue + next_line + + align 32 + +??slt_skip: inc edi + dec ecx + jz ??slt_skip_end_line + + mov al,[esi] + inc esi + test al,al + jz ??slt_skip2 + mov [edi],al + inc edi + dec ecx + jnz ??slt_mask_map_lp + + epilogue + next_line + + align 32 + +??slt_skip2: inc edi + dec ecx + jz ??slt_end_line + +; +; If we have hit two transparent pixels in a row then we go into +; the transparent optimised bit +; +??slt_round_again: + rept 64 + mov al,[esi] ; ;pipe 1 + inc esi ;1 ;pipe 2 + test al,al ; ;pipe 1 + jnz ??slt_not_trans;pipe 2 (not pairable in 1) + ;2 + inc edi ; ;pipe 1 + dec ecx ;3 ;pipe 2 + jz ??slt_end_line ;4 ;pipe 1 (not pairable) + endm ; best case is 4 cycles per iteration + jmp ??slt_round_again + + + +??slt_skip_end_line: + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with no transparent pixels +; +; 11/29/95 10:21AM - ST +; +; We have to align the destination for cards that dont bankswitch correctly +; when you write non-aligned data. +; + align 32 +Long_Single_Line_Copy: + prologue + + rept 3 + test edi,3 + jz ??LSLC_aligned + movsb + dec ecx + endm + +??LSLC_aligned: + mov ebx,ecx + + shr ecx,2 + rep movsd + and ebx,3 + jz ??out + movsb + dec bl + jz ??out + movsb + dec bl + jz ??out + movsb +??out: epilogue + next_line + + + +;***************************************************************************** +; Draw a single short line with no transparent pixels +; +; 11/29/95 10:21AM - ST +; + align 32 +Short_Single_Line_Copy: + prologue + cmp ecx,16 + jge Long_Single_Line_Copy + mov ebx,ecx + rep movsb + mov ecx,ebx + epilogue + next_line + + +;***************************************************************************** +; Skip a line of source that is all transparent +; +; 11/29/95 10:21AM - ST +; + + align 32 +Single_Line_Skip: + prologue + add esi,ecx + add edi,ecx + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with ghosting +; +; 11/29/95 10:21AM - ST +; + align 32 +Single_Line_Ghost: + + prologue + xor eax,eax +??slg_loop: mov al,[esi] + mov ebx,[IsTranslucent] + inc esi + mov bh,[eax+ebx] + cmp bh,-1 + jz ??slg_store_pixel + + and ebx,0ff00h + mov al,[edi] + add ebx,[Translucent] + mov al,[eax+ebx] + +??slg_store_pixel: + mov [edi],al + + inc edi + dec ecx + jnz ??slg_loop + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with transparent pixels and ghosting +; +; 11/29/95 10:21AM - ST +; + align 32 +Single_Line_Ghost_Trans: + prologue + xor eax,eax +; cmp ecx,3 +; ja ??slgt4 + +??slgt_loop: mov al,[esi] + inc esi + test al,al + jz ??slgt_transparent + +??slgt_not_transparent: + mov ebx,[IsTranslucent] + mov bh,[eax+ebx] + cmp bh,-1 + jz ??slgt_store_pixel + + and ebx,0ff00h + mov al,[edi] + add ebx,[Translucent] + mov al,[eax+ebx] + +??slgt_store_pixel: + mov [edi],al + inc edi + dec ecx + jnz ??slgt_loop + epilogue + next_line + + + align 32 + +??slgt_transparent: + inc edi ;1 + dec ecx ;2 + jz ??slgt_out ;1 (not pairable) + +??slgt_round_again: + rept 64 + mov al,[esi] ; ;pipe 1 + inc esi ;1 ;pipe 2 + test al,al ; ;pipe 1 + jnz ??slgt_not_transparent ;pipe 2 (not pairable in 1) + ;2 + inc edi ; ;pipe 1 + dec ecx ;3 ;pipe 2 + jz ??slgt_out ;4 ;pipe 1 (not pairable) + endm ; best case is 4 cycles per iteration + jmp ??slgt_round_again + +??slgt_out: epilogue + next_line + + + +; +; Optimised video memory access version +; + align 32 + +??slgt4: push edx + mov edx,[edi] + + rept 4 + local slgt4_store1 + local slgt4_trans1 + mov al,[esi] + inc esi + test al,al + jz slgt4_trans1 + + mov ebx,[IsTranslucent] + mov bh,[eax+ebx] + cmp bh,-1 + jz slgt4_store1 + + and ebx,0ff00h + mov al,dl + add ebx,[Translucent] + mov al,[eax+ebx] + +slgt4_store1: mov dl,al + +slgt4_trans1: ror edx,8 + endm + mov [edi],edx + pop edx + lea edi,[edi+4] + lea ecx,[ecx+0fffffffch] + cmp ecx,3 + ja ??slgt4 + test ecx,ecx + jnz ??slgt_loop + + epilogue + next_line + + + + + + + + + + +;***************************************************************************** +; Draw a single line with fading (colour remapping) +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Fading: + prologue + xor eax,eax + mov ebx,[FadingTable] + push ebp + mov ebp,[FadingNum] + push ebp + +??slf_loop: mov al,[esi] + inc esi + + mov ebp,[esp] + +??slf_fade_loop:mov al,[ebx+eax] + dec ebp + jnz ??slf_fade_loop + + mov [edi],al + inc edi + + dec ecx + jnz ??slf_loop + add esp,4 + pop ebp + epilogue + next_line + + +;***************************************************************************** +; Draw a single line with transparent pixels and fading (colour remapping) +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Fading_Trans: + prologue + xor eax,eax + mov ebx,[FadingTable] + push ebp + mov ebp,[FadingNum] + push ebp + +??slft_loop: mov al,[esi] + inc esi + test al,al + jz ??slft_transparent + + mov ebp,[esp] + +??slft_fade_loop: + mov al,[ebx+eax] + dec ebp + jnz ??slft_fade_loop + + mov [edi],al +??slft_transparent: + inc edi + + dec ecx + jnz ??slft_loop + add esp,4 + pop ebp + epilogue + next_line + + + + + +;***************************************************************************** +; Draw a single line with a single fade level (colour remap) +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Single_Fade: + prologue + xor eax,eax + mov ebx,[FadingTable] + +??slsf_loop: mov al,[esi] + mov al,[ebx+eax] + mov [edi],al + inc esi + inc edi + + dec ecx + jnz ??slsf_loop + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with transparent pixels and a single fade level (colour remap) +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Single_Fade_Trans: + prologue + xor eax,eax + mov ebx,[FadingTable] + +??slsft_loop: mov al,[esi] + inc esi + test al,al + jz ??slsft_transparent + mov al,[ebx+eax] + mov [edi],al + inc edi + dec ecx + jnz ??slsft_loop + epilogue + next_line + + align 32 + +??slsft_transparent: + inc edi + + dec ecx + jz ??slsft_next_line + mov al,[esi] + inc esi + test al,al + jz ??slsft_transparent + mov al,[ebx+eax] + mov [edi],al + inc edi + dec ecx + jnz ??slsft_loop + +??slsft_next_line: + epilogue + next_line + + + + + +;***************************************************************************** +; Draw a single line with ghosting and fading (colour remapping) +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Ghost_Fading: + + prologue + mov [StashECX],ecx + +??SLGF_loop: xor eax,eax + mov al,[esi] + mov ebx,[IsTranslucent] + mov bh,[eax+ebx] + cmp bh,-1 + jz ??slgf_do_fading + + and ebx,0ff00h + + mov al,[edi] + add ebx,[Translucent] + mov al,[ebx+eax] + +??slgf_do_fading: + mov ebx,[FadingTable] + mov ecx,[FadingNum] + +??slgf_fade_loop: + mov al,[eax+ebx] + dec ecx + jnz ??slgf_fade_loop + + mov [edi],al + inc esi + inc edi + + dec [StashECX] + jnz ??SLGF_loop + epilogue + next_line + + +;***************************************************************************** +; Draw a single line with transparent pixels, ghosting and fading +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Ghost_Fading_Trans: + prologue + mov [StashECX],ecx + xor eax,eax + +; cmp ecx,3 +; ja ??slgft4 + +??SLGFT_loop: mov al,[esi] + inc esi + test al,al + jz ??slgft_trans_pixel + mov ebx,[IsTranslucent] + mov bh,[eax+ebx] + cmp bh,-1 + jz ??slgft_do_fading + + and ebx,0ff00h + + mov al,[edi] + add ebx,[Translucent] + mov al,[ebx+eax] + +??slgft_do_fading: + mov ebx,[FadingTable] + mov ecx,[FadingNum] + +??slgft_fade_loop: + mov al,[eax+ebx] + dec ecx + jnz ??slgft_fade_loop + + mov [edi],al +??slgft_trans_pixel: + inc edi + + dec [StashECX] + jnz ??SLGFT_loop + epilogue + next_line + + + align 32 + +??slgft4: push edx + mov edx,[edi] + + rept 4 + local slgft4_fade + local slgft4_fade_lp + local slgft4_trans + mov al,[esi] + inc esi + test al,al + jz slgft4_trans + mov ebx,[IsTranslucent] + mov bh,[eax+ebx] + cmp bh,-1 + jz slgft4_fade + + and ebx,0ff00h + + mov al,dl + add ebx,[Translucent] + mov al,[ebx+eax] + +slgft4_fade: mov ebx,[FadingTable] + mov ecx,[FadingNum] + +slgft4_fade_lp: mov al,[eax+ebx] + dec ecx + jnz slgft4_fade_lp + + mov dl,al + +slgft4_trans: ror edx,8 + endm + mov [edi],edx + pop edx + lea edi,[edi+4] + sub [StashECX],4 + jz ??slgft4_out + cmp [StashECX],3 + ja ??slgft4 + jmp ??SLGFT_loop + +??slgft4_out: epilogue + next_line + + +;***************************************************************************** +; Draw a single line with predator effect +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Predator: + + prologue + +??slp_loop: mov al,[esi] + + mov ebx,[BFPartialCount] + add ebx,[BFPartialPred] + or bh,bh + jnz ??slp_get_pred + + mov [BFPartialCount] , ebx + jmp ??slp_skip_pixel + +??slp_get_pred: xor bh , bh + mov eax,[BFPredOffset] + mov [BFPartialCount] , ebx + add [BYTE BFPredOffset],2 + mov eax,[DWORD BFPredTable+eax] + and [BYTE BFPredOffset],PRED_MASK + and eax,0ffffh + + mov al,[edi+eax] + mov [edi],al + +??slp_skip_pixel: + inc esi + inc edi + + dec ecx + jnz ??slp_loop + + epilogue + next_line + + + + +;***************************************************************************** +; Draw a single line with transparent pixels and predator effect +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Predator_Trans: + + prologue + +??slpt_loop: mov al,[esi] + inc esi + test al,al + jz ??slpt_skip_pixel + + mov ebx,[BFPartialCount] + add ebx,[BFPartialPred] + or bh,bh + jnz ??slpt_get_pred + + mov [BFPartialCount] , ebx + jmp ??slpt_skip_pixel + +??slpt_get_pred:xor bh , bh + mov eax,[BFPredOffset] + mov [BFPartialCount] , ebx + add [BYTE BFPredOffset],2 + mov eax,[DWORD BFPredTable+eax] + and [BYTE PTR BFPredOffset ] , PRED_MASK + and eax,0ffffh + + mov al,[edi+eax] + mov [edi],al + +??slpt_skip_pixel: + inc edi + + dec ecx + jnz ??slpt_loop + + epilogue + next_line + + +;***************************************************************************** +; Draw a single line with predator and ghosting +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Predator_Ghost: + + prologue + +??slpg_loop: mov al,[esi] + mov ebx,[BFPartialCount] + add ebx,[BFPartialPred] + test bh,bh + jnz ??slpg_get_pred ; is this a predator pixel? + + mov [BFPartialCount],ebx + jmp ??slpg_check_ghost + +??slpg_get_pred: + xor bh,bh + mov eax,[BFPredOffset] + mov [BFPartialCount],ebx + add [BYTE BFPredOffset],2 + mov eax,[DWORD BFPredTable+eax ] + and [BYTE BFPredOffset],PRED_MASK + and eax,0ffffh + mov al,[edi+eax] + +??slpg_check_ghost: + mov ebx,[IsTranslucent] + mov bh,[ebx+eax] + cmp bh,0ffh + je ??slpg_store_pixel + + xor eax,eax + and ebx,0FF00h + + mov al,[edi] + add ebx,[Translucent] + + mov al,[ebx+eax] + +??slpg_store_pixel: + mov [edi],al + inc esi + inc edi + + dec ecx + jnz ??slpg_loop + + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with transparent pixels, predator and ghosting +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Predator_Ghost_Trans: + prologue + +??slpgt_loop: mov al,[esi] + inc esi + test al,al + jz ??slpgt_transparent + + mov ebx,[BFPartialCount] + add ebx,[BFPartialPred] + test bh,bh + jnz ??slpgt_get_pred ; is this a predator pixel? + + mov [BFPartialCount],ebx + jmp ??slpgt_check_ghost + +??slpgt_get_pred: + xor bh,bh + mov eax,[BFPredOffset] + mov [BFPartialCount],ebx + add [BYTE BFPredOffset],2 + mov eax,[DWORD BFPredTable+eax ] + and [BYTE BFPredOffset],PRED_MASK + and eax,0ffffh + mov al,[edi+eax] + +??slpgt_check_ghost: + mov ebx,[IsTranslucent] + mov bh,[ebx+eax] + cmp bh,0ffh + je ??slpgt_store_pixel + + xor eax,eax + and ebx,0FF00h + + mov al,[edi] + add ebx,[Translucent] + + mov al,[ebx+eax] + +??slpgt_store_pixel: + mov [edi],al +??slpgt_transparent: + inc edi + + dec ecx + jnz ??slpgt_loop + + pop ecx + epilogue + next_line + + +;***************************************************************************** +; Draw a single line with predator and fading +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Predator_Fading: + + prologue + mov [StashECX],ecx + +??slpf_loop: mov al,[esi] + mov ebx,[BFPartialCount] + inc esi + add ebx,[BFPartialPred] + test bh,bh + jnz ??slpf_get_pred + + mov [BFPartialCount],ebx + jmp ??slpf_do_fading + +??slpf_get_pred:xor bh,bh + mov eax,[BFPredOffset] + mov [BFPartialCount],ebx + and [BYTE BFPredOffset],2 + mov eax,[DWORD BFPredTable+eax] + and [BYTE BFPredOffset],PRED_MASK + + and eax,0ffffh + mov al,[eax+edi] + +??slpf_do_fading: + and eax,255 + mov ebx,[FadingTable] + mov ecx,[FadingNum] + +??slpf_fade_loop: + mov al,[eax+ebx] + dec ecx + jnz ??slpf_fade_loop + + mov [edi],al + inc edi + + dec [StashECX] + jnz ??slpf_loop + + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with transparent pixels, fading and predator +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Predator_Fading_Trans: + prologue + mov [StashECX],ecx + +??slpft_loop: mov al,[esi] + inc esi + test al,al + jz ??slpft_transparent + mov ebx,[BFPartialCount] + add ebx,[BFPartialPred] + test bh,bh + jnz ??slpft_get_pred + + mov [BFPartialCount],ebx + jmp ??slpft_do_fading + +??slpft_get_pred: + xor bh,bh + mov eax,[BFPredOffset] + mov [BFPartialCount],ebx + and [BYTE BFPredOffset],2 + mov eax,[DWORD BFPredTable+eax] + and [BYTE BFPredOffset],PRED_MASK + + and eax,0ffffh + mov al,[eax+edi] + +??slpft_do_fading: + and eax,255 + mov ebx,[FadingTable] + mov ecx,[FadingNum] + +??slpft_fade_loop: + mov al,[eax+ebx] + dec ecx + jnz ??slpft_fade_loop + + mov [edi],al +??slpft_transparent: + inc edi + + dec [StashECX] + jnz ??slpft_loop + + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with predator, ghosting and fading +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Predator_Ghost_Fading: + + prologue + mov [StashECX],ecx + +??slpgf_loop: mov al,[esi] + mov ebx,[BFPartialCount] + inc esi + add ebx,[BFPartialPred] + test bh , bh + jnz ??slpgf_get_pred ; is this a predator pixel? + + mov [BFPartialCount],ebx + jmp ??slpgf_check_ghost + +??slpgf_get_pred: + xor bh,bh + mov eax,[BFPredOffset] + mov [BFPartialCount],ebx + add [BYTE BFPredOffset],2 + mov eax,[DWORD BFPredTable+eax] + and [BYTE BFPredOffset],PRED_MASK + and eax,0ffffh + + mov al,[edi+eax] + +??slpgf_check_ghost: + and eax,255 + mov ebx,[IsTranslucent] + mov bh,[ebx+eax] + cmp bh,0ffh + je ??slpgf_do_fading + + and ebx , 0FF00h + + mov al,[edi] + add ebx,[Translucent] + + mov al,[ebx+eax] + +??slpgf_do_fading: + xor eax,eax + mov ebx,[FadingTable] + mov ecx,[FadingNum] + +??slpgf_fade_loop: + mov al,[ebx+eax] + dec ecx + jnz ??slpgf_fade_loop + +??slpgf_store_pixel: + mov [edi],al + inc edi + + dec [StashECX] + jnz ??slpgf_loop + + epilogue + next_line + + + +;***************************************************************************** +; Draw a single line with transparent pixels, predator, ghosting and fading +; +; 11/29/95 10:21AM - ST +; + align 32 + +Single_Line_Predator_Ghost_Fading_Trans: + + prologue + mov [StashECX],ecx + +??slpgft_loop: mov al,[esi] + inc esi + test al,al + jz ??slpgft_transparent + + mov ebx,[BFPartialCount] + add ebx,[BFPartialPred] + test bh , bh + jnz ??slpgft_get_pred ; is this a predator pixel? + + mov [BFPartialCount],ebx + jmp ??slpgft_check_ghost + +??slpgft_get_pred: + xor bh,bh + mov eax,[BFPredOffset] + mov [BFPartialCount],ebx + add [BYTE BFPredOffset],2 + mov eax,[DWORD BFPredTable+eax] + and [BYTE BFPredOffset],PRED_MASK + and eax,0ffffh + + mov al,[edi+eax] + +??slpgft_check_ghost: + and eax,255 + mov ebx,[IsTranslucent] + mov bh,[ebx+eax] + cmp bh,0ffh + je ??slpgft_do_fading + + and ebx , 0FF00h + + mov al,[edi] + add ebx,[Translucent] + + mov al,[ebx+eax] + +??slpgft_do_fading: + xor eax,eax + mov ebx,[FadingTable] + mov ecx,[FadingNum] + +??slpgft_fade_loop: + mov al,[ebx+eax] + dec ecx + jnz ??slpgft_fade_loop + +??slpgft_store_pixel: + mov [edi],al +??slpgft_transparent: + inc edi + + dec [StashECX] + jnz ??slpgft_loop + + epilogue + next_line + + + + + ends ;end of strict alignment segment + + codeseg + + + +global BF_Trans:near + +BF_Trans: + + prologue +; calc the code location to skip to 10 bytes per REPT below!!!! + mov ecx , eax + and ecx , 01fh + lea ecx , [ ecx + ecx * 4 ] ; quick multiply by 5 + neg ecx + shr eax , 5 + lea ecx , [ ??trans_reference + ecx * 2 ] ; next multiply by 2 + mov [ loop_cnt ] , eax + mov [ jmp_loc ] , ecx + +??trans_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +; the following code should NOT be changed without changing the calculation +; above!!!!!! + +??trans_line: + + REPT 32 + local trans_pixel + mov bl , [ esi ] + inc esi + test bl , bl + jz trans_pixel + + mov [ edi ] , bl + + trans_pixel: + inc edi + ENDM + +??trans_reference: + dec ecx + jge ??trans_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??trans_loop + epilogue + + ret + +;******************************************************************** +;******************************************************************** + +global BF_Ghost:near +BF_Ghost: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??ghost_reference - ??ghost_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??ghost_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??ghost_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +??ghost_line: + + REPT 32 + local store_pixel + mov al , [ esi ] + inc esi + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je store_pixel + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al + + store_pixel: + mov [ edi ] , al + inc edi + + ENDM + +??ghost_reference: + dec ecx + jge ??ghost_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??ghost_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Ghost_Trans:near +BF_Ghost_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??ghost_t_reference - ??ghost_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??ghost_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??ghost_t_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +??ghost_t_line: + + REPT 32 + local transp_pixel + local store_pixel + mov al , [ esi ] + inc esi + test al , al + jz transp_pixel + + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je store_pixel + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al + + store_pixel: + mov [ edi ] , al + + transp_pixel: + inc edi + + ENDM + +??ghost_t_reference: + dec ecx + jge ??ghost_t_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??ghost_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Fading:near +BF_Fading: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??fading_reference - ??fading_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??fading_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??fading_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + mov ebx , [ FadingTable ] ; run color through fading table + jmp [ jmp_loc ] + +??fading_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??fading_line: + + REPT 32 + local fade_loop + mov al , [ esi ] + inc esi + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + mov [ edi ] , al + inc edi + + ENDM + +??fading_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??fading_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??fading_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Fading_Trans:near +BF_Fading_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??fading_t_reference - ??fading_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??fading_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??fading_t_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + mov ebx , [ FadingTable ] ; run color through fading table + jmp [ jmp_loc ] + +??fading_t_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??fading_t_line: + + REPT 32 + local transp_pixel + local fade_loop + mov al , [ esi ] + inc esi + test al , al + jz transp_pixel + + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + mov [ edi ] , al + + transp_pixel: + inc edi + + ENDM + +??fading_t_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??fading_t_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??fading_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Ghost_Fading:near +BF_Ghost_Fading: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??ghost_f_reference - ??ghost_f_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??ghost_f_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??ghost_f_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +??ghost_f_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??ghost_f_line: + + REPT 32 + local store_pixel + local do_fading + local fade_loop + mov al , [ esi ] + inc esi + mov ebx , [ IsTranslucent ] ; is it a lucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je do_fading + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the lation table + ; ((_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (_color * 256) of the lation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al +; DRD jmp store_pixel + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + store_pixel: + mov [ edi ] , al + inc edi + + ENDM + +??ghost_f_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??ghost_f_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??ghost_f_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Ghost_Fading_Trans:near +BF_Ghost_Fading_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??ghost_f_t_reference - ??ghost_f_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??ghost_f_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??ghost_f_t_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +??ghost_f_t_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??ghost_f_t_line: + + REPT 32 + local transp_pixel + local store_pixel + local do_fading + local fade_loop + mov al , [ esi ] + inc esi + test al , al + jz transp_pixel + + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je do_fading + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al +; DRD jmp store_pixel + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + store_pixel: + mov [ edi ] , al + + transp_pixel: + inc edi + + ENDM + +??ghost_f_t_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??ghost_f_t_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??ghost_f_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Predator:near +BF_Predator: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_reference - ??predator_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +??predator_line: + + REPT 32 + local get_pred + local skip_pixel + mov al , [ esi ] + inc esi + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp skip_pixel + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + +; xor bh , bh +; mov eax , [ BFPredValue ] ; pick up a color offset a pseudo- +; ; random amount from the current +; mov [ BFPartialCount ] , ebx +; mov al , [ edi + eax ] ; viewport address + + mov [ edi ] , al + + skip_pixel: + inc edi + + ENDM + +??predator_reference: + dec ecx + jge ??predator_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Predator_Trans:near +BF_Predator_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_t_reference - ??predator_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_t_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +??predator_t_line: + + REPT 32 + local trans_pixel + local get_pred + local store_pixel + mov al , [ esi ] + inc esi + test al , al + jz trans_pixel + + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp store_pixel + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + store_pixel: + mov [ edi ] , al + + trans_pixel: + inc edi + + ENDM + +??predator_t_reference: + dec ecx + jge ??predator_t_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Predator_Ghost:near +BF_Predator_Ghost: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_g_reference - ??predator_g_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_g_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_g_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +??predator_g_line: + + REPT 32 + local get_pred + local check_ghost + local store_pixel + mov al , [ esi ] + mov ebx , [ BFPartialCount ] + inc esi + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp check_ghost + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + check_ghost: + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je store_pixel + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al + + store_pixel: + mov [ edi ] , al + inc edi + + ENDM + +??predator_g_reference: + dec ecx + jge ??predator_g_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_g_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Predator_Ghost_Trans:near +BF_Predator_Ghost_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_g_t_reference - ??predator_g_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_g_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_g_t_loop: + mov ecx , [ loop_cnt ] + jmp [ jmp_loc ] + +??predator_g_t_line: + + REPT 32 + local trans_pixel + local get_pred + local check_ghost + local store_pixel + mov al , [ esi ] + inc esi + test al , al + jz trans_pixel + + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp check_ghost + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + check_ghost: + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je store_pixel + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al + + store_pixel: + mov [ edi ] , al + + trans_pixel: + inc edi + + ENDM + +??predator_g_t_reference: + dec ecx + jge ??predator_g_t_line + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_g_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Predator_Fading:near +BF_Predator_Fading: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_f_reference - ??predator_f_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_f_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_f_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +??predator_f_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??predator_f_line: + + REPT 32 + local get_pred + local do_fading + local fade_loop + mov al , [ esi ] + mov ebx , [ BFPartialCount ] + inc esi + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp do_fading + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + mov [ edi ] , al + inc edi + + ENDM + +??predator_f_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??predator_f_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_f_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Predator_Fading_Trans:near +BF_Predator_Fading_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_f_t_reference - ??predator_f_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_f_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_f_t_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +??predator_f_t_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??predator_f_t_line: + + REPT 32 + local trans_pixel + local get_pred + local do_fading + local fade_loop + mov al , [ esi ] + inc esi + test al , al + jz trans_pixel + + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp do_fading + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + mov [ edi ] , al + + trans_pixel: + inc edi + + ENDM + +??predator_f_t_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??predator_f_t_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_f_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Predator_Ghost_Fading:near +BF_Predator_Ghost_Fading: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_g_f_reference - ??predator_g_f_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_g_f_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_g_f_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +??predator_g_f_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??predator_g_f_line: + + REPT 32 + local get_pred + local check_ghost + local store_pixel + local do_fading + local fade_loop + mov al , [ esi ] + mov ebx , [ BFPartialCount ] + inc esi + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp check_ghost + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + check_ghost: + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je do_fading + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al +; DRD jmp store_pixel + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + store_pixel: + mov [ edi ] , al + inc edi + + ENDM + +??predator_g_f_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??predator_g_f_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_g_f_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +global BF_Predator_Ghost_Fading_Trans:near +BF_Predator_Ghost_Fading_Trans: + + prologue + mov ebx , eax ; width + + ; NOTE: the below calculation assumes a group of instructions is + ; less than 256 bytes + + ; get length of the 32 groups of instructions + + lea ecx , [ ??predator_g_f_t_reference - ??predator_g_f_t_line ] + + shr ebx , 5 ; width / 32 + shr ecx , 5 ; length of instructions / 32 + and eax , 01fh ; mod of width / 32 + mul cl ; calc offset to start of group + neg eax ; inverse of width + mov [ loop_cnt ] , ebx ; save width / 32 + lea ecx , [ ??predator_g_f_t_reference + eax ] + mov eax , 0 + mov [ jmp_loc ] , ecx + +??predator_g_f_t_loop: + mov ecx , [ loop_cnt ] + mov [ StashECX ] , ecx ; preserve ecx for later + jmp [ jmp_loc ] + +??predator_g_f_t_line_begin: + mov [ StashECX ] , ecx ; preserve ecx for later + +??predator_g_f_t_line: + + REPT 32 + local trans_pixel + local get_pred + local check_ghost + local store_pixel + local do_fading + local fade_loop + mov al , [ esi ] + inc esi + test al , al + jz trans_pixel + + mov ebx , [ BFPartialCount ] + add ebx , [ BFPartialPred ] + or bh , bh + jnz get_pred ; is this a predator pixel? + + mov [ BFPartialCount ] , ebx + jmp check_ghost + + get_pred: + xor bh , bh + mov eax, [ BFPredOffset ] + mov [ BFPartialCount ] , ebx + add [ BYTE PTR BFPredOffset ] , 2 + movzx eax , [ WORD PTR BFPredTable + eax ] + and [ BYTE PTR BFPredOffset ] , PRED_MASK + ; pick up a color offset a pseudo- + ; random amount from the current + movzx eax , [ BYTE PTR edi + eax ] ; viewport address + + check_ghost: + mov ebx , [ IsTranslucent ] ; is it a translucent color? + mov bh , [ BYTE PTR ebx + eax ] + cmp bh , 0ffh + je do_fading + + and ebx , 0FF00h ; clear all of ebx except bh + ; we have the index to the translation table + ; ((trans_colour * 256) + dest colour) + mov al , [ edi ] ; mov pixel at destination to al + add ebx , [ Translucent ] ; get the ptr to it! + ; Add the (trans_color * 256) of the translation equ. + mov al , [ BYTE PTR ebx + eax ] ; get new pixel in al +; DRD jmp store_pixel + + do_fading: + mov ebx , [ FadingTable ] ; run color through fading table + mov ecx , [ FadingNum ] + + fade_loop: + mov al, [BYTE PTR ebx + eax] + dec ecx + jnz fade_loop + + store_pixel: + mov [ edi ] , al + + trans_pixel: + inc edi + + ENDM + +??predator_g_f_t_reference: + mov ecx , [ StashECX ] ; restore ecx for main draw loop + dec ecx + jge ??predator_g_f_t_line_begin + + add esi , [ scr_adjust_width ] + add edi , [ dest_adjust_width ] + dec edx + jnz ??predator_g_f_t_loop + + epilogue + ret + + +;******************************************************************** +;******************************************************************** + +Not_Supported: + ret + + ENDP Buffer_Frame_To_Page +END + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood Library * +;* * +;* File Name : KEYFBUFF.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 16, 1992 * +;* * +;* Last Update : October 2, 1994 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* BUFFER_FRAME_TO_LOGICPAGE -- * +;* Normal_Draw -- Function that writes a normal pixel line * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + IDEAL + P386 +IDEAL_MODE EQU 1 + INCLUDE "wwlib.i" + + ;------------------------------------------------------------------- + ; Extern all the library variables that this module requires + ;------------------------------------------------------------------- + + EXTRN C MaskPage:WORD + EXTRN C BackGroundPage:WORD + + ;------------------------------------------------------------------- + ; Define all the equates that this module requires + ;------------------------------------------------------------------- + +WIN_X EQU 0 ; offset for the x coordinate +WIN_Y EQU 2 ; offset for the y coordinate +WIN_WIDTH EQU 4 ; offset for the window width +WIN_HEIGHT EQU 6 ; offset for the window height +BYTESPERROW EQU 320 ; number of bytes per row + +FLAG_NORMAL EQU 0 ; flag for normal draw + +FLAG_GHOST EQU 1 ; This flag enables the ghost +FLAG_PRIORITY_TRANS EQU 2 ; flag for priority and transparent +FLAG_TRANS EQU 4 ; flag for transparent draw +FLAG_PRIORITY EQU 8 ; flag for priority draw + + ; fx on the above flags + +FLAG_MASK EQU 15 ; used to and of uneeded bits + +SHAPE_NORMAL EQU 0000h ; Standard shape. +;SHAPE_HORZ_REV EQU 0001h ; Flipped horizontally. +;SHAPE_VERT_REV EQU 0002h ; Flipped vertically. +;SHAPE_SCALING EQU 0004h ; Scaled (WORD scale_x, WORD scale_y) + +SHAPE_WIN_REL EQU 0010h ; Coordinates are window relative instead of absolute. +SHAPE_CENTER EQU 0020h ; Coordinates are based on shape's center point. +SHAPE_TRANS EQU 0040h ; has transparency + + +;SHAPE_FADING EQU 0100h ; Fading effect active (VOID * fading_table, WORD fading_num). +;SHAPE_PREDATOR EQU 0200h ; Transparent warping effect. +;SHAPE_COMPACT EQU 0400h ; Never use this bit. +SHAPE_PRIORITY EQU 0800h ; Use priority system when drawing. + +SHAPE_GHOST EQU 1000h ; Transluscent table process. +;SHAPE_SHADOW EQU 2000h ; ???? +;SHAPE_PARTIAL EQU 4000h ; ???? +;SHAPE_COLOR EQU 8000h ; Remap the shape's colors (VOID * color_table). + + +; MBL MOD 12.1.92 + +CLEAR_NON_WALK_BIT_AND_SCALE_BITS EQU 7 ; Makes it one AND per pixel in Priority_Trans display +CLEAR_NON_WALK_BIT EQU 7fh ; and with 0111-1111 to clear non-walkable high bit +CLEAR_SCALE_BITS EQU 87h ; and with 1000-0111 to clear scaling id bits +NON_WALKABLE_BIT EQU 80h ; and with 1000-0000 to clear all but non-walkable bit + +; END MBL MOD + + + CODESEG + + ; 1 = GHOST (all odd entrys are prefixed with Ghost_) + ; 2 = BLAAAH + ; 4 = Trans (prfx) + ; 8 = Prior (prfx) + + +;--------------------------------------------------------------------------- +; Define the table of different line draw types +;--------------------------------------------------------------------------- + +LineTable DW WSA_Normal_Draw ;0 + DW Ghost_Normal_Draw ;1 + DW 0 ;2 + DW 0 ;3 + + DW Transparent_Draw ;4 + DW Ghost_Transparent_Draw ;5 + DW 0 ;6 + DW 0 ;7 + + DW Priority_Draw ;8 + DW Ghost_Priority_Draw ;9 + DW 0 ;10 + DW 0 ;11 + + DW Priority_Transparent_Draw ;12 + DW Ghost_Priority_Transparent_Draw ;13 + DW 0 ;14 + DW 0 ;15 + + + +;*************************************************************************** +;* BUFFER_FRAME_TO_LOGICPAGE -- * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/16/1992 PWG : Created. * +;*=========================================================================* + PUBLIC C Buffer_Frame_To_LogicPage + PROC C Buffer_Frame_To_LogicPage FAR USES ax bx ecx dx ds esi es edi + + ;------------------------------------------------------------------- + ; Define the arguements that our program takes. + ;------------------------------------------------------------------- + + ARG x_pixel:WORD ; x pixel position to draw at + ARG y_pixel:WORD ; y pixel position to draw at + ARG pixel_w:WORD ; pixel width of draw region + ARG pixel_h:WORD ; pixel height of draw region + ARG win:WORD ; window to clip around + ARG flags:WORD ; flags that this routine will take + ARG buffer:DWORD ; pointer to the buffer with data + ARG args:WORD + + ;------------------------------------------------------------------- + ; Define the local variables that our program uses + ;------------------------------------------------------------------- + + LOCAL IsTranslucent:DWORD ; ptr to the is_translucent table + LOCAL Translucent:DWORD ; ptr to the actual translucent table + + LOCAL win_x1:WORD ; clip window left x pixel position + LOCAL win_x2:WORD ; clip window right x pixel position + LOCAL win_y1:WORD ; clip window top y pixel position + LOCAL win_y2:WORD ; clip window bottom y pixel position + LOCAL clipleft:WORD ; number of pixels to clip on left + LOCAL clipright:WORD ; number of pixels to clip on right + LOCAL nextline:WORD ; offset to the next line + LOCAL putmiddle:WORD ; routine to call to put the middle + LOCAL maskpage:WORD ; location of the depth masks + LOCAL background:WORD ; location of the background data + LOCAL jflags:WORD ; location of the background data + + LOCAL priority:BYTE ; the priority level of the back + + push fs + + xor ecx,ecx + + ;-------------------------------------------------------------------- + ; Check to see if we have supplied any GHOST tables. + ;-------------------------------------------------------------------- + push di + + mov di,6 + mov [jflags],0 + +??ghost: + test [flags],SHAPE_GHOST ; are we ghosting this shape + jz short ??no_ghost ; if not then skip and do more + + or [jflags],FLAG_GHOST + + les ax,[DWORD PTR buffer + di] + + ; get the "are we really translucent?" table + mov [WORD PTR IsTranslucent],ax + mov [WORD PTR IsTranslucent + 2],es + add ax,0100h ; add to offset for tables + + ; get the "ok we are translucent!!" table + mov [WORD PTR Translucent],ax + mov [WORD PTR Translucent + 2],es + + add di,4 + +??no_ghost: + + pop di + + ;------------------------------------------------------------------- + ; See if we need to center the frame + ;------------------------------------------------------------------- + test [flags],SHAPE_CENTER ; does this need to be centered? + je short ??no_centering ; if not the skip over this stuff + + mov ax,[pixel_w] + mov bx,[pixel_h] + sar ax,1 + sar bx,1 + sub [x_pixel],ax + sub [y_pixel],bx + +??no_centering: + mov ax,[flags] + and ax,SHAPE_PRIORITY+SHAPE_TRANS + cmp ax,SHAPE_PRIORITY+SHAPE_TRANS + jne short ??test_trans + + or [jflags],FLAG_PRIORITY_TRANS + jmp short ??priority + + ;------------------------------------------------------------------- + ; Get the trans information if we need to get it + ;------------------------------------------------------------------- +??test_trans: + test [flags],SHAPE_TRANS ; does this draw use transparencies? + je short ??test_priority ; if not the skip over this junk + + or [jflags],FLAG_TRANS + +??test_priority: + ;------------------------------------------------------------------- + ; Get the priority information if we need to get it + ;------------------------------------------------------------------- + test [flags],SHAPE_PRIORITY ; does this draw use priorities? + je short ??no_priority ; if not the skip over this junk + + or [jflags],FLAG_PRIORITY + +??priority: + mov ax,[BackGroundPage] ; get the background page from ds + mov [background],ax ; and store it on the stack + mov ax,[MaskPage] ; get the mask page from ds + mov [maskpage],ax ; and store it on the stack + mov ax,[WORD PTR buffer + 4]; get the priority level from args + mov [priority],al ; and store it in a local + + ;------------------------------------------------------------------- + ; Get the draw routine that we are going to draw with + ;------------------------------------------------------------------- +??no_priority: +; mov bx,[flags] ; load in the current flags byte +; and bx,FLAG_MASK ; prevent lockup on bad value + mov bx,[jflags] ; load in the jump table flags + shl bx,1 + mov ax,[WORD PTR LineTable + bx] ; get the offset of the skip table + mov [putmiddle],ax ; store off the new offset + + ;------------------------------------------------------------------- + ; Get a pointer to the logic page to where we will draw our buffer + ;------------------------------------------------------------------- + push [LogicPage] ; push the current logic page + call FAR PTR Get_Page ; get the physical page address + add sp,2 ; pull the parameter from the stack + mov es,dx ; store the address in the dest + + ;-------------------------------------------------------------------- + ; Point DI to the beginning of the window that we need to look at. + ; that way we can access all of the info through di. + ;-------------------------------------------------------------------- + mov si,OFFSET WindowList ; get the offset of the window list + mov cl,4 ; shift 3 times = multiply by 16 + mov ax,[win] ; get the window number we are using + shl ax,cl ; each window is 8 words long + add si,ax ; add that into the offset of window + + ;-------------------------------------------------------------------- + ; Place all the clipping values on the stack so our function will + ; be truly re-entrant and will not need to shadow these values. + ;-------------------------------------------------------------------- + mov cl,3 ; to convert x to pixel mult by 8 + mov ax,[si + WIN_X] ; get the left clip position + shl ax,cl ; convert to a pixel x position + mov [win_x1],ax ; store the left edge of window + mov [win_x2],ax + + mov ax,[si + WIN_WIDTH] ; get the width of the window + shl ax,cl ; convert to a pixel width + add [win_x2],ax ; add to get the right window edge + + mov ax,[si + WIN_Y] ; get the win y coordinate to clip + mov [win_y1],ax ; and save it onto the stack + + add ax,[si + WIN_HEIGHT] ; calculate the bottom win y coord + mov [win_y2],ax ; and save it onto the stack + + test [flags],SHAPE_WIN_REL ; is this window relative? + je short ??get_buffer ; if not the skip over + + mov ax,[win_x1] ; get left edge of window + add [x_pixel],ax ; add to x pixel position + mov ax,[win_y1] ; get top edge of window + add [y_pixel],ax ; add to y pixel position + + ;-------------------------------------------------------------------- + ; Get a pointer to the source buffer so we can handle the clipping + ;-------------------------------------------------------------------- +??get_buffer: + lds si,[buffer] ; get a pointer to the buffer + + ;-------------------------------------------------------------------- + ; Check the top of our shape and clip any lines that are necessary + ;-------------------------------------------------------------------- + mov ax,[y_pixel] ; get the y_pixel draw position + sub ax,[win_y1] ; subtract out the window y top + jns short ??check_bottom ; skip if y below window top + add ax,[pixel_h] ; add in the height of the region + jg short ??clip_top ; if positive then clip top lines + +??jump_exit: + jmp ??exit ; otherwise completely clipped + +??clip_top: + xchg [pixel_h],ax + sub ax,[pixel_h] + add [y_pixel],ax + mul [pixel_w] ; convert to number of bytes to skip + add si,ax ; skip past the necessary bytes + + ;-------------------------------------------------------------------- + ; Check the bottom of our shape and clip it if necessary + ;-------------------------------------------------------------------- +??check_bottom: + mov ax,[win_y2] ; get the bottom y of the window + sub ax,[y_pixel] ; subtract of the y to draw at + js ??jump_exit ; if its signed then nothing to draw + jz ??jump_exit ; if its zero then nothing to draw + + cmp ax,[pixel_h] ; if more room to draw then height + jae short ??clip_x_left ; then go check the left clip + mov [pixel_h],ax ; clip all but amount that will fit + +??clip_x_left: + mov [clipleft],0 ; clear clip on left of region + mov ax,[x_pixel] ; get the pixel x of draw region + sub ax,[win_x1] ; pull out the window coordinate + jns short ??clip_x_right + neg ax ; negate to get amnt to skip in buf + mov [clipleft],ax ; store it in the left clip info + add [x_pixel],ax ; move to the edge of the window + sub [pixel_w],ax ; pull it out of the pixel width + +??clip_x_right: + mov [clipright],0 ; clear clip on right of region + mov ax,[win_x2] ; get the window x of clip region + sub ax,[x_pixel] ; subtract the draw edge of region + js ??jump_exit ; if its negative then get out + jz ??jump_exit ; if its zero then get out + + cmp ax,[pixel_w] ; is space available larger than w + jae short ??draw_prep ; if so then go get drawing + + + xchg [pixel_w],ax ; amt to draw in pixel_w (wid in ax) + sub ax,[pixel_w] ; pull out the amount to draw + mov [clipright],ax ; this is the amount to clip on right + +??draw_prep: + push si ; save off source pos in buffer + push ds ; both offset and segment + mov ax,@data + mov ds,ax + mov bx,[y_pixel] + shl bx,1 ; shift left by 1 for word table look + lds si,[YTable] ; get the address of the ytable + mov di,[ds:si+bx] ; look up the multiplied value + pop ds ; restore source pos in buffer + pop si ; both offset and segment + + add di,[x_pixel] ; add in the x pixel position + mov [nextline],di ; save it off in the next line + + ;-------------------------------------------------------------------- + ; Now determine the type of the shape and process it in the proper + ; way. + ;-------------------------------------------------------------------- + mov dx,[pixel_h] + + ; Check to see if the WSA is the screen width and there is no + ; clipping. In this case, then a special single call to the + ; line processing routine is possible. + mov ax,[clipleft] + add ax,[clipright] + jne short ??top_of_loop + cmp [pixel_w],BYTESPERROW + jne short ??top_of_loop + + ;------------------------------------ + ; The width of the WSA is the screen width, so just process as + ; one large WSA line. + mov ax,BYTESPERROW + imul dx + mov cx,ax + call [putmiddle] + jmp short ??exit + + ;------------------------------------ + ; Process line by line. +??top_of_loop: + add si,[clipleft] ; skip whats necessary on left edge + mov cx,[pixel_w] ; get the width we need to draw + + ; Copy the source to the destination as appropriate. This routine can + ; trash AX, BX, CX, and DI. It must properly modify SI to point one byte past + ; the end of the data. + call [putmiddle] + + add si,[clipright] ; skip past the left clip + add [nextline],BYTESPERROW + mov di,[nextline] + + dec dx + jnz ??top_of_loop + +??exit: + pop fs + ret + ENDP + + +;*************************************************************************** +;* NORMAL_DRAW -- Function that writes a normal pixel line * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;*=========================================================================* + + PROC NOLANGUAGE WSA_Normal_Draw NEAR + + IF 1 + ; This version is marginally faster than the later version. + mov ax,cx + shr cx,2 + rep movsd + and ax,011b + mov cx,ax + shr cx,1 + rep movsw + adc cx,cx + rep movsb + ret + + ELSE + + shr cx,1 ; convert to words (odd pix in carry) + rep movsw ; write out the needed words + adc cx,0 ; add the carry into cx + rep movsb ; write out the odd byte if any + ret + ENDIF + + ENDP + + +;*************************************************************************** +;* TRANSPARENT_DRAW -- Function that writes a transparent pixel line * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;* 10/02/1994 JLB : Optimized for 250% speed improvement. * +;*=========================================================================* + PROC NOLANGUAGE Transparent_Draw NEAR + + IF 1 + ; Preserve DX since it is used as a scratch register. + push dx + +??loop: + ; Swap DS:SI and ES:DI back in preparation for the REP SCASB + ; instruction. + xchg di,si + mov dx,es + mov ax,ds + mov ds,dx + mov es,ax + + ; Remember the bytes remaining in order to calculate the position + ; of the scan when it stops. + mov bx,cx + + ; Scan looking for a non-zero value in the source buffer. + xor al,al + repe scasb + + ; When the loop ends, if the EQ flag is set then the scanning is + ; complete. Jump to the end of the routine in order to fixup the + ; pointers. + je short ??fini + + ; Advance the destination pointer by the amount necessary to match + ; the source movement. DS:SI points to where data should be written. + add si,bx + inc cx ; SCASB leaves CX one too low, fix it. + dec di ; SCASB leaves DI one byte too far, fix it. + sub si,cx + + ; Scan for the duration of non-zero pixels. This yields a count which + ; is used to copy the source data to the destination. Preserve DI. + mov dx,di + mov bx,cx + repne scasb + mov di,dx + + ; Set BX to equal the number of bytes to copy from source to dest. + inc cx ; SCASB leaves CX one too low, fix it. + sub bx,cx + + ; Move the data from ES:DI to DS:SI for BX bytes. + xchg cx,bx ; Make CX=bytes to move, BX=bytes remaining. + + ; Swap DS:SI and ES:DI in preparation for the REP MOV instruction. + xchg di,si + mov dx,es + mov ax,ds + mov ds,dx + mov es,ax + + ; Move the data from source to dest. First try to move double + ; words. Then copy the remainder bytes (if any). Putting jumps in + ; this section doesn't result in any savings -- oh well. + mov ax,cx + shr cx,2 + rep movsd + and ax,0011b + mov cx,ax + shr cx,1 + rep movsw + adc cx,cx + rep movsb + + ; Restore CX with the remaining bytes to process. + mov cx,bx + + ; If there are more bytes to process, then loop back. + or cx,cx + jne short ??loop + +??fini: + ; Swap ES:DI and DS:SI back to original orientation. + mov ax,ds + mov bx,es + mov es,ax + mov ds,bx + xchg di,si + + ; Restore DX and return. + pop dx + ret + + ELSE + +??loop_top: + lodsb + or al,al + jz short ??skip + + mov [es:di],al ; store the pixel to the screen +??skip: + inc di + loop ??loop_top + ret + + ENDIF + + ENDP + + +;*************************************************************************** +;* PRIORITY_DRAW -- Function that writes a pixels if they are in front of * +;* the given plate. * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;* 12/01/1992 MBL : Updated to work with latest mask data encoding. * +;* 17/01/1993 MCC : Updated for 386, and optimized * +;*=========================================================================* + + PROC NOLANGUAGE Priority_Draw NEAR + + mov fs,[background] ; get the SEG of the background page + mov gs,[maskpage] ; get the SEG of the mask info + mov ah,[priority] ; keep a copy of priority varible for faster cmp + + +??loop_top: + lodsb ; get the pixel to draw on the screen + + ; get the mask byte for our pixel + mov bl,[ds:di] + ; get rid of non-walkable bit and + ; get rid of scaling id bits + and bl,CLEAR_NON_WALK_BIT_AND_SCALE_BITS + + cmp ah,bl ; are we more toward the front? + jge short ??out_pixel ; if so then write the pixel + + mov al,[fs:di] ; get the pixel to write +??out_pixel: + stosb ; write the pixel and inc the DI + loop ??loop_top + ret + + ENDP + + +;*************************************************************************** +;* PRIORITY_TRANSPARENT_DRAW -- Function that writes a pixels if they are * +;* in front of the given plate. It also deals with * +;* transparent pixels. * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;* 12/01/1992 MBL : Updated to work with latest mask data encoding. * +;* 17/01/1993 MCC : Updated for 386, and optimized * +;*=========================================================================* + + PROC NOLANGUAGE Priority_Transparent_Draw NEAR + + mov fs,[background] ; get the SEG of the background page + mov gs,[maskpage] ; get the SEG of the mask info + mov ah,[priority] ; keep a copy of priority varible for faster cmp + +??loop_top: + lodsb ; get the pixel on the screen + or al,al ; check to see if al is transparent + je short ??write_back ; if it is go write background + + mov bl,[gs:di] ; get the mask byte for our pixel + + ; get rid of non-walkable bit and + ; get rid of scaling id bits + and bl,CLEAR_NON_WALK_BIT_AND_SCALE_BITS + + cmp ah,bl ; are we more toward the front? + jge short ??out_pixel ; if so then write the pixel + +??write_back: + mov al,[fs:di] ; get the pixel to write +??out_pixel: + stosb ; write the pixel + loop ??loop_top + ret + + ENDP + + +;*************************************************************************** +;* GHOST_NORMAL_DRAW -- Function that writes a normal pixel line * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 05/27/1993 MCC : Created. * +;*=========================================================================* + + PROC NOLANGUAGE Ghost_Normal_Draw NEAR + +??loop_top: + lodsb + +;--- + ; Ok, find out if the colour is a Translucent colour + push ax + push ds + + lds bx,[IsTranslucent] + mov ah,al ; preserve real pixel + xlat ; get new al (transluecent pixel + xchg ah,al ; get real pixel back into AL just in case + cmp ah,255 + je short ??normal_pixel ; is it a translucent ? + ; if we get passed here value in + ; AH should be 0-15 + + ; yes, it is a translucent colour so goto our translucent translation + ; table and set up a ptr to the correct table + + mov al,[es:di] + ; mov pixel at destination to al and we have + ; the index to the translation table + ; ((trans_colour * 256) + dest colour) + lds bx,[Translucent] ; get the ptr to it! + add bh,ah ; Add the (trans_color * 256) of the translation equ. + ; XLAT only uses AL so no need to clear AH + xlat ; get new pixel in AL + +??normal_pixel: + pop ds + pop bx + mov ah,bh +;--- + + mov [es:di],al ; store the pixel to the screen + +??skip: + inc di + loop ??loop_top + + ret + + ENDP + + +;*************************************************************************** +;* GHOST_TRANSPARENT_DRAW -- Function that writes a transparent pixel line * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 05/27/1993 MCC : Created. * +;*=========================================================================* + PROC NOLANGUAGE Ghost_Transparent_Draw NEAR + +??loop_top: + lodsb + or al,al + jz short ??skip + +;--- + ; Ok, find out if the colour is a Translucent colour + push ax + push ds + + lds bx,[IsTranslucent] + mov ah,al ; preserve real pixel + xlat ; get new al (transluecent pixel + xchg ah,al ; get real pixel back into AL just in case + cmp ah,255 + je short ??normal_pixel ; is it a translucent ? + ; if we get passed here value in + ; AH should be 0-15 + + ; yes, it is a translucent colour so goto our translucent translation + ; table and set up a ptr to the correct table + + mov al,[es:di] + ; mov pixel at destination to al and we have + ; the index to the translation table + ; ((trans_colour * 256) + dest colour) + lds bx,[Translucent] ; get the ptr to it! + add bh,ah ; Add the (trans_color * 256) of the translation equ. + ; XLAT only uses AL so no need to clear AH + xlat ; get new pixel in AL + +??normal_pixel: + pop ds + pop bx + mov ah,bh +;--- + + mov [es:di],al ; store the pixel to the screen + +??skip: + inc di + loop ??loop_top + ret + + ENDP + + +;*************************************************************************** +;* GHOST_PRIORITY_DRAW -- Function that writes a pixels if they are in fron* +;* the given plate. * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;* 12/01/1992 MBL : Updated to work with latest mask data encoding. * +;* 05/27/1993 MCC : Updated to use the new Ghosting fx * +;* 17/01/1993 MCC : Updated for 386, and optimized * +;*=========================================================================* + PROC NOLANGUAGE Ghost_Priority_Draw NEAR + + mov fs,[background] ; get the SEG of the background page + mov gs,[maskpage] ; get the SEG of the mask info + mov ah,[priority] ; keep a copy of priority varible for faster cmp + + +??loop_top: + lodsb ; get the pixel to draw on the screen + ; get the mask byte for our pixel + mov bl,[ds:di] + ; get rid of non-walkable bit and + ; get rid of scaling id bits + and bl,CLEAR_NON_WALK_BIT_AND_SCALE_BITS + cmp ah,bl ; are we more toward the front? + jge short ??out_pixel ; if so then write the pixel + + mov al,[fs:di] ; get the pixel to write +??out_pixel: + stosb ; write the pixel and inc the DI + loop ??loop_top + + ret + + ENDP + + +;*************************************************************************** +;* GHOST_PRIORITY_TRANSPARENT_DRAW -- Function that writes a pixels if they* +;* in front of the given plate. It also deals with * +;* transparent pixels. * +;* * +;* INPUT: cx - number of pixels to write * +;* ds:si - buffer which holds the pixels to write * +;* es:di - place to put the pixels we are writing * +;* * +;* OUTPUT: ds:si - points to next pixel past last pixel read * +;* es:di - points to next pixel past last pixel written * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 07/17/1992 PWG : Created. * +;* 12/01/1992 MBL : Updated to work with latest mask data encoding. * +;* 05/27/1993 MCC : Updated to use the new Ghosting fx * +;* 17/01/1993 MCC : Updated for 386, and optimized * +;*=========================================================================* + PROC NOLANGUAGE Ghost_Priority_Transparent_Draw NEAR + + mov fs,[background] ; get the SEG of the background page + mov gs,[maskpage] ; get the SEG of the mask info + mov ah,[priority] ; keep a copy of priority varible for faster cmp + +??loop_top: + lodsb ; get the pixel on the screen + or al,al ; check to see if al is transparent + je short ??write_back ; if it is go write background + mov bl,[gs:di] ; get the mask byte for our pixel + ; get rid of non-walkable bit and + ; get rid of scaling id bits + and bl,CLEAR_NON_WALK_BIT_AND_SCALE_BITS + cmp ah,bl ; are we more toward the front? + jge short ??out_pixel ; if so then write the pixel +??write_back: + mov al,[fs:di] ; get the pixel to write +??out_pixel: + stosb ; write the pixel + loop ??loop_top + + ret + + ENDP + + END diff --git a/KEYFBUFF.INC b/KEYFBUFF.INC new file mode 100644 index 0000000..c503ff3 --- /dev/null +++ b/KEYFBUFF.INC @@ -0,0 +1,40 @@ +; +; Command & Conquer(tm) +; Copyright 2025 Electronic Arts Inc. +; +; 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 3 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 General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . +; + +;************************************************************** +; +; Macro to fetch the header of the next line and jump to the appropriate routine +; +next_line macro + + add edi,[dest_adjust_width] ;add in dest modulo + dec edx ;line counter + jz real_out ;return + mov ecx,[save_ecx] ;ecx is pixel count + mov eax,[header_pointer] ;ptr to next header byte + mov al,[eax] + inc [header_pointer] + and eax,BLIT_ALL ;Make sure we dont jump to some spurious address + ; if the header is wrong then its better to draw with the wrong + ; shape routine than to die + shl eax,2 + add eax,[ShapeJumpTableAddress] ;get the address to jump to + jmp [dword eax] ;do the jump + + endm + diff --git a/KEYFRAME.CPP b/KEYFRAME.CPP new file mode 100644 index 0000000..9c6f266 --- /dev/null +++ b/KEYFRAME.CPP @@ -0,0 +1,593 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\keyframe.cpv 2.14 16 Oct 1995 16:48:54 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : KEYFRAME.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/25/95 * + * * + * Last Update : June 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Get_Build_Frame_Count -- Fetches the number of frames in data block. * + * Get_Build_Frame_Width -- Fetches the width of the shape image. * + * Get_Build_Frame_Height -- Fetches the height of the shape image. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + +#define SUBFRAMEOFFS 7 // 3 1/2 frame offsets loaded (2 offsets/frame) + + +#define Apply_Delta(buffer, delta) Apply_XOR_Delta((char*)(buffer), (char*)(delta)) + +typedef struct { + unsigned short frames; + unsigned short x; + unsigned short y; + unsigned short width; + unsigned short height; + unsigned short largest_frame_size; + short flags; +} KeyFrameHeaderType; + +#define INITIAL_BIG_SHAPE_BUFFER_SIZE 12000*1024 +#define THEATER_BIG_SHAPE_BUFFER_SIZE 1000*1024 +#define UNCOMPRESS_MAGIC_NUMBER 56789 + +unsigned BigShapeBufferLength = INITIAL_BIG_SHAPE_BUFFER_SIZE; +unsigned TheaterShapeBufferLength = THEATER_BIG_SHAPE_BUFFER_SIZE; +extern "C"{ + char *BigShapeBufferStart = NULL; + char *TheaterShapeBufferStart = NULL; + BOOL UseBigShapeBuffer = FALSE; + bool IsTheaterShape = false; +} +char *BigShapeBufferPtr = NULL; +int TotalBigShapes=0; +BOOL ReallocShapeBufferFlag = FALSE; +bool OriginalUseBigShapeBuffer = false; + +char *TheaterShapeBufferPtr = NULL; +int TotalTheaterShapes = 0; + + + +#define MAX_SLOTS 1500 +#define THEATER_SLOT_START 1000 + +char **KeyFrameSlots [MAX_SLOTS]; +int TotalSlotsUsed=0; +int TheaterSlotsUsed = THEATER_SLOT_START; + + +typedef struct tShapeHeaderType{ + unsigned draw_flags; + char *shape_data; + int shape_buffer; //1 if shape is in theater buffer +} ShapeHeaderType; + +static int Length; + +void *Get_Shape_Header_Data(void *ptr) +{ + if (UseBigShapeBuffer){ + + ShapeHeaderType *header = (ShapeHeaderType*) ptr; + return ((void*) (header->shape_data + (long)(header->shape_buffer ? TheaterShapeBufferStart : BigShapeBufferStart) ) ); + + }else{ + return (ptr); + } +} + +int Get_Last_Frame_Length(void) +{ + return(Length); +} + + + +void Reset_Theater_Shapes (void) +{ + /* + ** Delete any previously allocated slots + */ + for (int i=THEATER_SLOT_START ; i 16*1024*1024) ? TRUE : FALSE; + OriginalUseBigShapeBuffer = UseBigShapeBuffer; + + // UseBigShapeBuffer = false; +} + + + + +/*********************************************************************************************** + * Disable_Uncompressed_Shapes -- Temporarily turns off shape decompression * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/19/96 2:37PM ST : Created * + *=============================================================================================*/ +void Disable_Uncompressed_Shapes (void) +{ + UseBigShapeBuffer = false; +} + + + +/*********************************************************************************************** + * Enable_Uncompressed_Shapes -- Restores state of shape decompression before it was disabled * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/19/96 2:37PM ST : Created * + *=============================================================================================*/ +void Enable_Uncompressed_Shapes (void) +{ + UseBigShapeBuffer = OriginalUseBigShapeBuffer; +} + + + + +unsigned long Build_Frame(void const *dataptr, unsigned short framenumber, void *buffptr) +{ + char *ptr, *lockptr;//, *uncomp_ptr; + unsigned long offset[SUBFRAMEOFFS]; + unsigned long offcurr, off16, offdiff; + KeyFrameHeaderType *keyfr; + unsigned short buffsize, currframe, subframe; + unsigned long length = 0; + char frameflags; + unsigned long return_value; + char *temp_shape_ptr; + + // + // valid pointer?? + // + Length = 0; + if ( !dataptr || !buffptr ) { + return(0); + } + + // + // look at header then check that frame to build is not greater + // than total frames + // + keyfr = (KeyFrameHeaderType *) dataptr; + + if ( framenumber >= keyfr->frames ) { + return(0); + } + + + if (UseBigShapeBuffer){ + /* + ** If we havnt yet allocated memory for uncompressed shapes then do so now. + ** + */ + if (!BigShapeBufferStart){ + BigShapeBufferStart = (char*)Alloc(BigShapeBufferLength, MEM_NORMAL); + BigShapeBufferPtr = BigShapeBufferStart; + /* + ** Allocate memory for theater specific uncompressed shapes + */ + TheaterShapeBufferStart = (char*) Alloc (TheaterShapeBufferLength, MEM_NORMAL); + TheaterShapeBufferPtr = TheaterShapeBufferStart; + } + + + /* + ** Track memory usage in uncompressed shape buffers. + */ + static bool show_info = true; + + if ((Frame & 0xff) == 0){ + + if (show_info){ + + char crap [128]; + sprintf (crap, "C&C95 - Big shape buffer is now %d Kb.\n", BigShapeBufferLength / 1024); + CCDebugString (crap); + + sprintf (crap, "C&C95 - %d Kb Used in big shape buffer.\n", (unsigned)((unsigned)BigShapeBufferPtr - (unsigned)BigShapeBufferStart)/1024); + CCDebugString (crap); + + sprintf (crap, "C&C95 - %d Kb Used in theater shape buffer.\n", (unsigned)((unsigned)TheaterShapeBufferPtr - (unsigned)TheaterShapeBufferStart)/1024); + CCDebugString (crap); + show_info = false; + } + + }else{ + show_info = true; + + } + + + /* + ** If we are running out of memory (<128k left) for uncompressed shapes + ** then allocate some more. + */ + if (( (unsigned)BigShapeBufferStart + BigShapeBufferLength) - (unsigned)BigShapeBufferPtr < 128*1024){ + ReallocShapeBufferFlag = TRUE; + } + + /* + ** If this animation was not previously uncompressed then + ** allocate memory to keep the pointers to the uncompressed data + ** for these animation frames + */ + if (keyfr->x != UNCOMPRESS_MAGIC_NUMBER){ + keyfr->x = UNCOMPRESS_MAGIC_NUMBER; + if (IsTheaterShape){ + keyfr->y = TheaterSlotsUsed; + TheaterSlotsUsed++; + }else{ + keyfr->y = TotalSlotsUsed; + TotalSlotsUsed++; + } + /* + ** Allocate and clear the memory for the shape info + */ + KeyFrameSlots[keyfr->y]= new char *[keyfr->frames]; + memset (KeyFrameSlots[keyfr->y] , 0 , keyfr->frames*4); + } + + /* + ** If this frame was previously uncompressed then just return + ** a pointer to the raw data + */ + if (*(KeyFrameSlots[keyfr->y]+framenumber)){ + if (IsTheaterShape){ + return ((unsigned long)TheaterShapeBufferStart + (unsigned long)*(KeyFrameSlots[keyfr->y]+framenumber)); + }else{ + return ((unsigned long)BigShapeBufferStart + (unsigned long)*(KeyFrameSlots[keyfr->y]+framenumber)); + } + } + } + + // calc buff size + buffsize = keyfr->width * keyfr->height; + + // get offset into data + ptr = (char *)Add_Long_To_Pointer( dataptr, (((unsigned long)framenumber << 3) + sizeof(KeyFrameHeaderType)) ); + Mem_Copy( ptr, &offset[0], 12L ); + frameflags = (char)(offset[0] >> 24); + + + if ( (frameflags & KF_KEYFRAME) ) { + + ptr = (char *)Add_Long_To_Pointer( dataptr, (offset[0] & 0x00FFFFFFL) ); + + if (keyfr->flags & 1 ) { + ptr = (char *)Add_Long_To_Pointer( ptr, 768L ); + } + length = LCW_Uncompress( ptr, buffptr, buffsize ); + } else { // key delta or delta + + if ( (frameflags & KF_DELTA) ) { + currframe = (unsigned short)offset[1]; + + ptr = (char *)Add_Long_To_Pointer( dataptr, (((unsigned long)currframe << 3) + sizeof(KeyFrameHeaderType)) ); + Mem_Copy( ptr, &offset[0], (long)(SUBFRAMEOFFS * sizeof(unsigned long)) ); + } + + // key frame + offcurr = offset[1] & 0x00FFFFFFL; + + // key delta + offdiff = (offset[0] & 0x00FFFFFFL) - offcurr; + + ptr = (char *)Add_Long_To_Pointer( dataptr, offcurr ); + + if (keyfr->flags & 1 ) { + ptr = (char *)Add_Long_To_Pointer( ptr, 768L ); + } + + off16 = (unsigned long)lockptr & 0x00003FFFL; + + length = LCW_Uncompress( ptr, buffptr, buffsize ); + + if (length > buffsize) { + return(0); + } + + if ( ((offset[2] & 0x00FFFFFFL) - offcurr) >= (0x00010000L - off16) ) { + + ptr = (char *)Add_Long_To_Pointer( ptr, offdiff ); + off16 = (unsigned long)ptr & 0x00003FFFL; + + offcurr += offdiff; + offdiff = 0; + } + + length = buffsize; + Apply_Delta(buffptr, Add_Long_To_Pointer(ptr, offdiff)); + + if ( (frameflags & KF_DELTA) ) { + // adjust to delta after the keydelta + + currframe++; + subframe = 2; + + while (currframe <= framenumber) { + offdiff = (offset[subframe] & 0x00FFFFFFL) - offcurr; + + if ( ((offset[subframe+2] & 0x00FFFFFFL) - offcurr) >= (0x00010000L - off16) ) { + + ptr = (char *)Add_Long_To_Pointer( ptr, offdiff ); + off16 = (unsigned long)lockptr & 0x00003FFFL; + + offcurr += offdiff; + offdiff = 0; + } + + length = buffsize; + Apply_Delta(buffptr, Add_Long_To_Pointer(ptr, offdiff)); + + currframe++; + subframe += 2; + + if ( subframe >= (SUBFRAMEOFFS - 1) && + currframe <= framenumber ) { + Mem_Copy( Add_Long_To_Pointer( dataptr, + (((unsigned long)currframe << 3) + + sizeof(KeyFrameHeaderType)) ), + &offset[0], (long)(SUBFRAMEOFFS * sizeof(unsigned long)) ); + subframe = 0; + } + } + } + } + + if (UseBigShapeBuffer){ + /* + ** Save the uncompressed shape data so we dont have to uncompress it + ** again next time its drawn. + ** We keep a space free before the raw shape data so we can add line + ** header info before the shape is drawn for the first time + */ + + if (IsTheaterShape){ + /* + ** Shape is a theater specific shape + */ + return_value = (unsigned long) TheaterShapeBufferPtr; + temp_shape_ptr = TheaterShapeBufferPtr + keyfr->height+sizeof(ShapeHeaderType); + /* + ** align the actual shape data + */ + if (3 & (unsigned)temp_shape_ptr){ + temp_shape_ptr = (char *) ((unsigned)(temp_shape_ptr + 3) & 0xfffffffc); + } + + memcpy (temp_shape_ptr , buffptr , length); + ((ShapeHeaderType *)TheaterShapeBufferPtr)->draw_flags = -1; //Flag that headers need to be generated + ((ShapeHeaderType *)TheaterShapeBufferPtr)->shape_data = temp_shape_ptr - (unsigned)TheaterShapeBufferStart; //pointer to old raw shape data + ((ShapeHeaderType *)TheaterShapeBufferPtr)->shape_buffer = 1; //Theater buffer + *(KeyFrameSlots[keyfr->y]+framenumber) = TheaterShapeBufferPtr - (unsigned)TheaterShapeBufferStart; + TheaterShapeBufferPtr = (char*)(length + (unsigned)temp_shape_ptr); + /* + ** Align the next shape + */ + if (3 & (unsigned)TheaterShapeBufferPtr){ + TheaterShapeBufferPtr = (char *)((unsigned)(TheaterShapeBufferPtr + 3) & 0xfffffffc); + } + Length = length; + return (return_value); + + }else{ + + + return_value=(unsigned long)BigShapeBufferPtr; + temp_shape_ptr = BigShapeBufferPtr + keyfr->height+sizeof(ShapeHeaderType); + /* + ** align the actual shape data + */ + if (3 & (unsigned)temp_shape_ptr){ + temp_shape_ptr = (char *) ((unsigned)(temp_shape_ptr + 3) & 0xfffffffc); + } + memcpy (temp_shape_ptr , buffptr , length); + ((ShapeHeaderType *)BigShapeBufferPtr)->draw_flags = -1; //Flag that headers need to be generated + ((ShapeHeaderType *)BigShapeBufferPtr)->shape_data = temp_shape_ptr - (unsigned)BigShapeBufferStart; //pointer to old raw shape data + ((ShapeHeaderType *)BigShapeBufferPtr)->shape_buffer = 0; //Normal Big Shape Buffer + *(KeyFrameSlots[keyfr->y]+framenumber) = BigShapeBufferPtr - (unsigned)BigShapeBufferStart; + BigShapeBufferPtr = (char*)(length + (unsigned)temp_shape_ptr); + // Align the next shape + if (3 & (unsigned)BigShapeBufferPtr){ + BigShapeBufferPtr = (char *)((unsigned)(BigShapeBufferPtr + 3) & 0xfffffffc); + } + Length = length; + return (return_value); + } + + }else{ + return ((unsigned long)buffptr); + } +} + + +/*********************************************************************************************** + * Get_Build_Frame_Count -- Fetches the number of frames in data block. * + * * + * Use this routine to determine the number of shapes within the data block. * + * * + * INPUT: dataptr -- Pointer to the keyframe shape data block. * + * * + * OUTPUT: Returns with the number of shapes in the data block. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Commented. * + *=============================================================================================*/ +unsigned short Get_Build_Frame_Count(void const *dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->frames); + } + return(0); +} + + +unsigned short Get_Build_Frame_X(void const *dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->x); + } + return(0); +} + + +unsigned short Get_Build_Frame_Y(void const *dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->y); + } + return(0); +} + + +/*********************************************************************************************** + * Get_Build_Frame_Width -- Fetches the width of the shape image. * + * * + * Use this routine to fetch the width of the shapes within the keyframe shape data block. * + * All shapes within the block have the same width. * + * * + * INPUT: dataptr -- Pointer to the keyframe shape data block. * + * * + * OUTPUT: Returns with the width of the shapes in the block -- expressed in pixels. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Commented * + *=============================================================================================*/ +unsigned short Get_Build_Frame_Width(void const *dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->width); + } + return(0); +} + + +/*********************************************************************************************** + * Get_Build_Frame_Height -- Fetches the height of the shape image. * + * * + * Use this routine to fetch the height of the shapes within the keyframe shape data block. * + * All shapes within the block have the same height. * + * * + * INPUT: dataptr -- Pointer to the keyframe shape data block. * + * * + * OUTPUT: Returns with the height of the shapes in the block -- expressed in pixels. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Commented * + *=============================================================================================*/ +unsigned short Get_Build_Frame_Height(void const *dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->height); + } + return(0); +} + + +bool Get_Build_Frame_Palette(void const * dataptr, void * palette) +{ + if (dataptr && (((KeyFrameHeaderType const *)dataptr)->flags & 1)) { + char const * ptr = (char const *)Add_Long_To_Pointer( dataptr, + ( (( (long)sizeof(unsigned long) << 1) * + ((KeyFrameHeaderType *) dataptr)->frames ) + + 16 + sizeof(KeyFrameHeaderType) ) ); + + memcpy(palette, ptr, 768L); + return(true); + } + return(false); +} diff --git a/LAYER.CPP b/LAYER.CPP new file mode 100644 index 0000000..ec707f2 --- /dev/null +++ b/LAYER.CPP @@ -0,0 +1,163 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\layer.cpv 2.18 16 Oct 1995 16:50:14 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LAYER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 31, 1994 * + * * + * Last Update : March 10, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LayerClass::Submit -- Adds an object to a layer list. * + * LayerClass::Sort -- Perform an incremental sort pass on the layer's objects. * + * LayerClass::Sorted_Add -- Adds object in sorted order to layer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "layer.h" + + +/*********************************************************************************************** + * LayerClass::Submit -- Adds an object to a layer list. * + * * + * This routine is used to add an object to the layer list. If the list is full, then the * + * object is not added. * + * * + * INPUT: object -- Pointer to the object to add. * + * * + * OUTPUT: bool; Was the object added successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 05/31/1994 JLB : Allows sorted insert. * + * 01/02/1995 JLB : Fixed to work with EMSListOf template. * + *=============================================================================================*/ +bool LayerClass::Submit(ObjectClass const * object, bool sort) +{ + /* + ** Add the object to the layer. Either at the end (if "sort" is false) or at the + ** appropriately sorted position. + */ + if (sort) { + return(Sorted_Add(object)); + } + return(Add((ObjectClass *)object)); +} + + +/*********************************************************************************************** + * LayerClass::Sort -- Handles sorting the objects in the layer. * + * * + * This routine is used if the layer objects must be sorted and sorting is to occur now. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Don't call this routine too often since it does take a bit of time to * + * execute. It is a single pass binary sort and thus isn't horribly slow, * + * but it does take some time. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 03/10/1995 JLB : Uses comparison operator. * + *=============================================================================================*/ +void LayerClass::Sort(void) +{ + for (int index = 0; index < Count()-1; index++) { + if (*(*this)[index+1] < *(*this)[index]) { + ObjectClass * temp; + + temp = (*this)[index+1]; + (*this)[index+1] = (*this)[index]; + (*this)[index] = temp; + } + } +} + + +/*********************************************************************************************** + * DynamicVectorClass::Sorted_Add -- Adds object in sorted order to vector. * + * * + * Use this routine to add an object to the vector but it will be inserted in sorted * + * order. This depends on the ">" operator being defined for the vector object. * + * * + * INPUT: object -- Reference to the object that will be added to the vector. * + * * + * OUTPUT: bool; Was the object added to the vector successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +int LayerClass::Sorted_Add(ObjectClass const * const object) +{ + if (ActiveCount >= Length()) { + if ((IsAllocated || !VectorMax) && GrowthStep > 0) { + if (!Resize(Length() + GrowthStep)) { + + /* + ** Failure to increase the size of the vector is an error condition. + ** Return with the error flag. + */ + return(false); + } + } else { + + /* + ** Increasing the size of this vector is not allowed! Bail this + ** routine with the error code. + */ + return(false); + } + } + + /* + ** There is room for the new object now. Add it to the right sorted position. + */ + for (int index = 0; index < ActiveCount; index++) { + if ((*(*this)[index]) > (*object)) { + break; + } + } + + /* + ** Make room if the insertion spot is not at the end of the vector. + */ + for (int i = ActiveCount-1; i >= index; i--) { + (*this)[i+1] = (*this)[i]; + } + (*this)[index] = (ObjectClass *)object; + ActiveCount++; + return(true); +} + + diff --git a/LAYER.H b/LAYER.H new file mode 100644 index 0000000..36094f7 --- /dev/null +++ b/LAYER.H @@ -0,0 +1,67 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\layer.h_v 2.19 16 Oct 1995 16:46:22 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LAYER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 31, 1994 * + * * + * Last Update : May 31, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef LAYER_H +#define LAYER_H + +#include "vector.h" + +class ObjectClass; + +class LayerClass : public DynamicVectorClass +{ + public: + + //----------------------------------------------------------------- + void Sort(void); + bool Submit(ObjectClass const * object, bool sort=false); + int Sorted_Add(ObjectClass const * const object); + + + virtual void Init(void) {Clear();}; + virtual void One_Time(void) {}; + + /* + ** File I/O. + */ + bool Load(FileClass & file); + bool Save(FileClass & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); +}; + +#endif diff --git a/LED.H b/LED.H new file mode 100644 index 0000000..a81abf7 --- /dev/null +++ b/LED.H @@ -0,0 +1,46 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\led.h_v 2.17 16 Oct 1995 16:45:50 JOE_BOSTIC $ */ + +#ifndef LED_H +#define LED_H + +class LEDClass +{ + public: + typedef enum ControlType { + LED_NOCHANGE, // Do nothing (just query). + LED_OFF, // Turn LED off. + LED_ON, // Turn LED on. + LED_TOGGLE // Toggle LED state. + } ControlType; + + protected: + static int Shift_Control(ControlType control, char bit); + + public: + static int Scroll_Lock(ControlType control=LED_TOGGLE) {return Shift_Control(control, 0x01);}; + static int Caps_Lock(ControlType control=LED_TOGGLE) {return Shift_Control(control, 0x02);}; + static int Num_Lock(ControlType control=LED_TOGGLE) {return Shift_Control(control, 0x04);}; + + private: + static void Send_To_Keyboard(unsigned char val); +}; + +#endif diff --git a/LINK.CPP b/LINK.CPP new file mode 100644 index 0000000..db57361 --- /dev/null +++ b/LINK.CPP @@ -0,0 +1,416 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\link.cpv 2.18 16 Oct 1995 16:52:18 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LINK.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 19, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LinkClass::LinkClass -- Default constructor for linked list object. * + * LinkClass::LinkClass -- Copy constructor for linked list object. * + * LinkClass::~LinkClass -- Default destructor for linked list object. * + * LinkClass::Zap -- Forces the link pointers to NULL. * + * LinkClass::operator= -- Assignment operator for linked list class object. * + * LinkClass::Get_Next -- Fetches the next object in list. * + * LinkClass::Get_Prev -- Fetches previous object in linked list. * + * LinkClass::Head_Of_List -- Finds the head of the list. * + * LinkClass::Tail_Of_List -- Scans for the object at the end of the list. * + * LinkClass::Add -- This object adds itself to the given list * + * LinkClass::Add_Head -- This gadget makes itself the head of the given list. * + * LinkClass::Add_Tail -- Add myself to the end of the given list. * + * LinkClass::Remove -- Removes the specified object from the list. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "link.h" + + +/*********************************************************************************************** + * LinkClass::LinkClass -- Default constructor for linked list object. * + * * + * This is the default constructor for a linked list object. It merely initializes the * + * next and previous pointers to NULL. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass::LinkClass(void) +{ + LinkClass::Zap(); +} + + +/*********************************************************************************************** + * LinkClass::LinkClass -- Copy constructor for linked list object. * + * * + * This copy constructor, unlike the assigment operator, doesn't have to deal with an * + * already initialized and legal link object to the left of the "=". It merely puts the * + * destination object into the same list as the source object. * + * * + * INPUT: link -- Reference to the object on the right of the "=". * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass::LinkClass(LinkClass & link) +{ + LinkClass::Zap(); + Add(link); +} + + +/*********************************************************************************************** + * LinkClass::~LinkClass -- Default destructor for linked list object. * + * * + * This default destructor will remove the object from any linked list it may be part of. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass::~LinkClass(void) +{ + Remove(); +} + + +/*********************************************************************************************** + * LinkClass::Zap -- Forces the link pointers to NULL. * + * * + * This routine will "zap" out the link pointers. This is usually necessary when the link * + * pointers start in an undefined state, but we KNOW that they aren't pointing to anything * + * valid. In such a case it becomes necessary to zap them so that when the object is added * + * to a list, it will be added corrrectly. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void LinkClass::Zap(void) +{ + Next = 0; + Prev = 0; +} + + +/*********************************************************************************************** + * LinkClass::operator= -- Assignment operator for linked list class object. * + * * + * The assignment operator makes sure that the previous and next pointers remain valid. * + * Because this class only consists of pointers, the assignment operator doesn't actually * + * transfer any data from the source object. It merely makes the destination object part * + * of the same list as the source object. In essence, this is transferring information * + * but not the actual values. * + * * + * If the destination object is already part of another list, it is removed from that list * + * before being added to the source object's list. This ensures that either list remains * + * in a valid condition. * + * * + * INPUT: link -- The object to the right of the "=" operator. * + * * + * OUTPUT: Returns a reference to the rightmost object -- per standard assigment rules. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::operator=(LinkClass & link) +{ + Remove(); + Add(link); + return(link); +} + + +/*********************************************************************************************** + * LinkClass::Get_Next -- Fetches the next object in list. * + * * + * This routine will return with a pointer to the next object in the list. If there are * + * no more objects, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with pointer to next object in list or NULL if at end of list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass * LinkClass::Get_Next(void) const +{ + return(Next); +} + + +/*********************************************************************************************** + * LinkClass::Get_Prev -- Fetches previous object in linked list. * + * * + * Use this routine to get a pointer to the previous object in the linked list. If there * + * are no previous objects (such as at the head of the list), then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the previous object in the list or NULL if none. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass * LinkClass::Get_Prev(void) const +{ + return(Prev); +} + + +/*********************************************************************************************** + * LinkClass::Head_Of_List -- Finds the head of the list. * + * * + * Use this routine to scan for and return a reference to the object at the head of the * + * list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference to the object at the head of the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass const & LinkClass::Head_Of_List(void) const +{ + LinkClass const * link = this; + while (link->Prev) { + link = link->Prev; + if (link == this) break; // Safety check + } + return(*link); +} + + +/*********************************************************************************************** + * LinkClass::Tail_Of_List -- Scans for the object at the end of the list. * + * * + * Use this routine to scan for and return a reference to the object at the end of the * + * list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference to the object at the end of the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass const & LinkClass::Tail_Of_List(void) const +{ + LinkClass const * link = this; + while (link->Next) { + link = link->Next; + if (link == this) break; // Safety check + } + return(*link); +} + + +/*********************************************************************************************** + * LinkClass::Add -- This object adds itself to the given list * + * * + * Use this routine to add a link object to the list, but to be added right after the * + * given link. This allows inserting a link in the middle of the chain. A quite necessary * + * ability if the chain is order dependant (e.g., the gadget system). * + * * + * INPUT: list -- gadget object to add this one to * + * * + * OUTPUT: Returns with a pointer to the head of the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::Add(LinkClass & list) +{ + LinkClass *ptr; + + /* + ** Save ptr to next gadget. + */ + ptr = list.Next; + + /* + ** Link myself in after 'list'. + */ + list.Next = this; + Prev = &list; + + /* + ** Link myself to next gadget, if there is one. + */ + Next = ptr; + if (ptr) { + ptr->Prev = this; + } + + return(Head_Of_List()); +} + + +/*********************************************************************************************** + * LinkClass::Add_Head -- This gadget makes itself the head of the given list. * + * * + * INPUT: list -- the list to make myself the head of * + * * + * OUTPUT: Returns with a reference to the object at the head of the list. This should be * + * the same object that is passed in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::Add_Head(LinkClass & list) +{ + LinkClass *ptr; + + /* + ** Get head of given list. + */ + ptr = &list.Head_Of_List(); + + /* + ** Link myself in front of it. + */ + ptr->Prev = this; + Next = ptr; + Prev = NULL; + + return(*this); +} + + +/*********************************************************************************************** + * LinkClass::Add_Tail -- Add myself to the end of the given list. * + * * + * INPUT: list -- list to add myself to * + * * + * OUTPUT: the head of the list * + * * + * WARNINGS: The previous and next pointers for the added object MUST have been properly * + * initialized for this routine to work correctly. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::Add_Tail(LinkClass & list) +{ + LinkClass *ptr; + + /* + ** Get head of given list. + */ + ptr = &list.Tail_Of_List(); + + /* + ** Link myself in front of it. + */ + ptr->Next = this; + Prev = ptr; + Next = NULL; + + return(Head_Of_List()); +} + + +/*********************************************************************************************** + * LinkClass::Remove -- Removes the specified object from the list. * + * * + * This routine will remove the specified object from the list of objects. Because of the * + * previous and next pointers, it is possible to remove an object from the list without * + * knowing the head of the list. To do this, just call Remove() with the parameter of * + * "this". * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the new head of list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass * LinkClass::Remove(void) +{ + LinkClass * head = &Head_Of_List(); + LinkClass * tail = &Tail_Of_List(); + + if (Prev) { + Prev->Next = Next; + } + if (Next) { + Next->Prev = Prev; + } + Prev = 0; + Next = 0; + + if (head==this) { + if (tail==this) { + return(0); + } + return(&tail->Head_Of_List()); + } + return(head); +} + + diff --git a/LINK.H b/LINK.H new file mode 100644 index 0000000..8a975f9 --- /dev/null +++ b/LINK.H @@ -0,0 +1,81 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\link.h_v 2.17 16 Oct 1995 16:45:52 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LINK.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef LINK_H +#define LINK_H + +/* +** This implements a simple linked list. It is possible to add, remove, and traverse the +** list. Since this is a doubly linked list, it is possible to remove an entry from the +** middle of an existing list. +*/ +class LinkClass +{ + public: + LinkClass(void); + virtual ~LinkClass(void); + + virtual LinkClass * Get_Next(void) const; + virtual LinkClass * Get_Prev(void) const; + virtual LinkClass & Add(LinkClass & object); + virtual LinkClass & Add_Tail(LinkClass & object); + virtual LinkClass & Add_Head(LinkClass & object); + virtual LinkClass const & Head_Of_List(void) const; + virtual LinkClass & Head_Of_List(void) + { + return (LinkClass &)(((LinkClass const *)this)->Head_Of_List()); + }; + virtual LinkClass const & Tail_Of_List(void) const; + virtual LinkClass & Tail_Of_List(void) + { + return (LinkClass &)(((LinkClass const *)this)->Tail_Of_List()); + }; + virtual void Zap(void); + virtual LinkClass * Remove(void); + + LinkClass & operator=(LinkClass & link); // Assignment operator. + LinkClass(LinkClass & link); // Copy constructor. + + private: + /* + ** Pointers to previous and next link objects in chain. + */ + LinkClass * Next; + LinkClass * Prev; +}; + +#endif diff --git a/LIST.CPP b/LIST.CPP new file mode 100644 index 0000000..47bf758 --- /dev/null +++ b/LIST.CPP @@ -0,0 +1,897 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\list.cpv 2.17 16 Oct 1995 16:51:36 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LIST.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : June 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ListClass::Add -- This object adds itself to the given list * + * ListClass::Add_Head -- This gadget makes itself the head of the given list. * + * ListClass::Add_Item -- Adds a text item (as number) to the list box. * + * ListClass::Add_Item -- Adds an item to the list box. * + * ListClass::Add_Scroll_Bar -- Adds a scroll bar to the list box. * + * ListClass::Add_Tail -- Add myself to the end of the given list. * + * ListClass::Bump -- Bumps the list box up/down one "page". * + * ListClass::Current_Index -- Fetches the current selected index. * + * ListClass::Current_Item -- Fetches pointer to current item string. * + * ListClass::Draw_Entry -- Draws a list box text line as indicated. * + * ListClass::Draw_Me -- Draws the listbox. * + * ListClass::Get_Item -- Fetches an arbitrary item string. * + * ListClass::Peer_To_Peer -- A peer gadget was touched -- make adjustments. * + * ListClass::Remove -- Removes the specified object from the list. * + * ListClass::Remove_Item -- Remove specified text from list box. * + * ListClass::Remove_Scroll_Bar -- Removes the scroll bar if present * + * ListClass::Set_Selected_Index -- Set the top of the listbox to index specified. * + * ListClass::Set_Tabs -- Sets the tab stop list to be used for text printing. * + * ListClass::Set_View_Index -- Sets the top line for the current list view. * + * ListClass::Step -- Moves the list view one line in direction specified. * + * ListClass::Step_Selected_Index -- Change the listbox top line in direction specified. * + * ListClass::~ListClass -- Destructor for list class objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * ListClass::ListClass -- class constructor * + * * + * INPUT: id button ID * + * * + * x,y upper-left corner, in pixels * + * * + * w,h width, height, in pixels * + * * + * list ptr to array of char strings to list* + * * + * flags, style flags for mouse, style of listbox * + * * + * OUTPUT: none. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/05/1995 MML : Created. * + *=========================================================================*/ +ListClass::ListClass (int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down) : + ControlClass(id, x, y, w, h, LEFTPRESS | LEFTRELEASE | KEYBOARD, false), + UpGadget(0, up, x+w, y), + DownGadget(0, down, x+w, y+h), + ScrollGadget(0, x+w, y, 0, h, true) +{ + /* + ** Set preliminary values for the slider related gadgets. They don't automatically + ** appear at this time, but there are some values that can be pre-filled in. + */ + UpGadget.X -= UpGadget.Width; + DownGadget.X -= DownGadget.Width; + DownGadget.Y -= DownGadget.Height; + ScrollGadget.X -= MAX(UpGadget.Width, DownGadget.Width); + ScrollGadget.Y = Y+UpGadget.Height; + ScrollGadget.Height -= UpGadget.Height + DownGadget.Height; + ScrollGadget.Width = MAX(UpGadget.Width, DownGadget.Width); + + /* + ** Set the list box to a default state. + */ + TextFlags = flags; + IsScrollActive = false; + Tabs = 0; + SelectedIndex = 0; + CurrentTopIndex = 0; + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TextFlags); + LineHeight = FontHeight+FontYSpacing-1; + LineCount = (h-1) / LineHeight; +} + + +/*********************************************************************************************** + * ListClass::~ListClass -- Destructor for list class objects. * + * * + * This is the destructor for list objects. It handles removing anything it might have * + * allocated. This is typically the scroll bar. * + * * + * INPUT: none * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/16/1995 JLB : Created. * + *=============================================================================================*/ +ListClass::~ListClass(void) +{ + Remove_Scroll_Bar(); +} + + +/*********************************************************************************************** + * ListClass::Add_Item -- Adds an item to the list box. * + * * + * This will add the specified string to the list box. The string is added to the end * + * of the list. * + * * + * INPUT: text -- Pointer to the string to add to the list box. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Add_Item(char const * text) +{ + if (text) { + List.Add(text); + Flag_To_Redraw(); + + /* + ** Add scroll gadget if the list gets too large to display all of the items + ** at the same time. + */ + if (List.Count() > LineCount) { + Add_Scroll_Bar(); + } + + /* + ** Tell the slider that there is one more entry in the list. + */ + if (IsScrollActive) { + ScrollGadget.Set_Maximum(List.Count()); + } + } + return(List.Count() - 1); +} + + +/*********************************************************************************************** + * ListClass::Add_Item -- Adds a text item (as number) to the list box. * + * * + * This will add the text as specified by the text number provided, to the list box. * + * The string is added to the end of the list. * + * * + * INPUT: text -- The text number for the string to add to the list box. * + * OUTPUT: none * + * WARNINGS: Once a string is added to the list box in this fashion, there is no method of * + * retrieving the text number as it relates to any particular index in the list. * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Add_Item(int text) +{ + if (text != TXT_NONE) { + Add_Item(Text_String(text)); + } + return(List.Count() - 1); +} + + +/*********************************************************************************************** + * ListClass::Remove_Item -- Remove specified text from list box. * + * * + * This routine will remove the specified text string from the list box. * + * * + * INPUT: text -- Pointer to the string to remove. * + * OUTPUT: none * + * WARNINGS: The text pointer passed into this routine MUST be the same text pointer that * + * was used to add the string to the list. * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Remove_Item(char const * text) +{ + if (text) { + List.Delete(text); + + /* + ** If the list is now small enough to display completely within the list box region, + ** then delete the slider gadget (if they are present). + */ + if (List.Count() <= LineCount) { + Remove_Scroll_Bar(); + } + + /* + ** Tell the slider that there is one less entry in the list. + */ + if (IsScrollActive) { + ScrollGadget.Set_Maximum(List.Count()); + } + + /* + ** If we just removed the selected entry, select the previous one + */ + if (SelectedIndex >= List.Count()) { + SelectedIndex--; + if (SelectedIndex < 0) + SelectedIndex = 0; + } + + /* + ** If we just removed the top-displayed entry, step up one item + */ + if (CurrentTopIndex >= List.Count()) { + CurrentTopIndex--; + if (CurrentTopIndex < 0) + CurrentTopIndex = 0; + if (IsScrollActive) + ScrollGadget.Step(1); + } + } +} + + +/*************************************************************************** + * ListClass::Action -- If clicked on, do this! * + * * + * INPUT: int flags -- combination of mouse flags indicating * + * what action to take. * + * * + * OUTPUT: bool result. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/05/1995 MML : Created. * + *=========================================================================*/ +int ListClass::Action(unsigned flags, KeyNumType & key) +{ + if (flags & LEFTRELEASE) { + key = KN_NONE; + flags &= (~LEFTRELEASE); + ControlClass::Action(flags, key); + return(true); + } else { + + /* -------------------------------------------------- + ** Handle keyboard events here. + */ + if (flags & KEYBOARD) { + + /* + ** Process the keyboard character. If indicated, consume this keyboard event + ** so that the edit gadget ID number is not returned. + */ + if (key == KN_UP) { + Step_Selected_Index(-1); + key = KN_NONE; + } else if (key == KN_DOWN) { + Step_Selected_Index(1); + key = KN_NONE; + } else { + flags &= ~KEYBOARD; + } + + } else { + + int index = Get_Mouse_Y() - (Y+1); + index = index / LineHeight; + SelectedIndex = CurrentTopIndex + index; + SelectedIndex = MIN(SelectedIndex, List.Count()-1); + } + } + return(ControlClass::Action(flags, key)); +} + + +/*********************************************************************************************** + * ListClass::Draw_Me -- Draws the listbox. * + * * + * This routine will render the listbox. * + * * + * INPUT: forced -- Should the listbox be redrawn even if it already thinks it doesn't * + * need to be? This is true when something outside of the gadget system * + * has trashed the screen. * + * * + * OUTPUT: Was the listbox redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Draw_Me(int forced) +{ + if (GadgetClass::Draw_Me(forced)) { + + /* + ** Turn off the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + Draw_Box (X, Y, Width, Height, BOXSTYLE_GREEN_BOX, true); + + /* + ** Draw List. + */ + if (List.Count()) { + for (int index = 0; index < LineCount; index++) { + int line = CurrentTopIndex + index; + + if (List.Count() > line) { + + /* + ** Prints the text and handles right edge clipping and tabs. + */ + Draw_Entry(line, X+1, Y+(LineHeight*index)+1, Width-2, (line == SelectedIndex)); + } + } + } + + /* + ** Turn on the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ListClass::Bump -- Bumps the list box up/down one "page". * + * * + * Use this routine to adjust the "page" that is being viewed in the list box. The view * + * will move up or down (as specified) one page (screen full) of text strings. * + * * + * INPUT: up -- Should the adjustment be up? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Bump(int up) +{ + if (IsScrollActive) { + if (ScrollGadget.Step(up)) { + CurrentTopIndex = ScrollGadget.Get_Value(); + Flag_To_Redraw(); + } + } +} + + +/*********************************************************************************************** + * ListClass::Step -- Moves the list view one line in direction specified. * + * * + * This routine will move the current view "page" one line in the direction specified. * + * * + * INPUT: up -- Should the view be moved upward? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Step(int up) +{ + if (IsScrollActive) { + if (ScrollGadget.Step(up)) { + CurrentTopIndex = ScrollGadget.Get_Value(); + Flag_To_Redraw(); + } + } +} + + +/*********************************************************************************************** + * ListClass::Get_Item -- Fetches an arbitrary item string. * + * * + * This routine will fetch an item string from the list box. The item fetched can be any * + * one of the ones in the list. * + * * + * INPUT: index -- The index to examine and return the text pointer from. * + * * + * OUTPUT: Returns with the text pointer to the string at the index position specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +char const * ListClass::Get_Item(int index) const +{ + if (List.Count() == 0) return (NULL); + + index = MIN(index, List.Count()-1 ); + return(List[index]); +} + + +/*********************************************************************************************** + * ListClass::Current_Item -- Fetches pointer to current item string. * + * * + * This routine will fetch a pointer to the currently selected item's text. * + * * + * INPUT: none * + * * + * OUTPUT: Return with pointer to currently selected text. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +char const * ListClass::Current_Item(void) +{ + return(List[SelectedIndex]); +} + + +/*********************************************************************************************** + * ListClass::Current_Index -- Fetches the current selected index. * + * * + * This routine will fetch the index number for the currently selected line. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the index of the currently selected line. This ranges from zero to * + * the number of items in the list minus one. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Current_Index(void) +{ + return(SelectedIndex); +} + + +/*********************************************************************************************** + * ListClass::Peer_To_Peer -- A peer gadget was touched -- make adjustments. * + * * + * This routine is called when one of the peer gadgets (the scroll arrows or the slider) * + * was touched in some fashion. This routine will sort out whom and why and then make * + * any necessary adjustments to the list box. * + * * + * INPUT: flags -- The event flags that affected the peer gadget. * + * * + * key -- The key value at the time of the event. * + * * + * whom -- Which gadget is being touched. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Peer_To_Peer(unsigned flags, KeyNumType &, ControlClass & whom) +{ + if (flags & LEFTRELEASE) { + if (&whom == &UpGadget) { + Step(true); + } + if (&whom == &DownGadget) { + Step(false); + } + } + + /* + ** The slider has changed, so reflect the current list position + ** according to the slider setting. + */ + if (&whom == &ScrollGadget) { + Set_View_Index(ScrollGadget.Get_Value()); + } +} + + +/*********************************************************************************************** + * ListClass::Set_View_Index -- Sets the top line for the current list view. * + * * + * This routine is used to set the line that will be at the top of the list view. This is * + * how the view can be scrolled up and down. This does not affect the currently selected * + * item. * + * * + * INPUT: index -- The line (index) to move to the top of the list view. * + * * + * OUTPUT: bool; Was the view actually changed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Set_View_Index(int index) +{ + index = Bound(index, 0, List.Count() - LineCount); + if (index != CurrentTopIndex) { + CurrentTopIndex = index; + Flag_To_Redraw(); + if (IsScrollActive) { + ScrollGadget.Set_Value(CurrentTopIndex); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ListClass::Add_Scroll_Bar -- Adds a scroll bar to the list box. * + * * + * This routine will add a scroll bar (with matching arrows) to the list box. They are * + * added to the right edge and cause the interior of the list box to become narrower. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the scroll bar added? * + * * + * WARNINGS: The list box becomes narrower when the scroll bar is added. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Add_Scroll_Bar(void) +{ + if (!IsScrollActive) { + IsScrollActive = true; + + /* + ** Everything has been created successfully. Flag the list box to be + ** redrawn because it now must be made narrower to accomodate the new + ** slider gadgets. + */ + Flag_To_Redraw(); + Width -= ScrollGadget.Width; + + /* + ** Tell the newly created gadgets that they should inform this list box + ** whenever they get touched. In this way, the list box will automatically + ** be updated under control of the slider buttons. + */ + UpGadget.Make_Peer(*this); + DownGadget.Make_Peer(*this); + ScrollGadget.Make_Peer(*this); + + /* + ** Add these newly created gadgets to the same gadget list that the + ** list box is part of. + */ + UpGadget.Add(*this); + DownGadget.Add(*this); + ScrollGadget.Add(*this); + + /* + ** Make sure these added gadgets get redrawn at the next opportunity. + */ + UpGadget.Flag_To_Redraw(); + DownGadget.Flag_To_Redraw(); + ScrollGadget.Flag_To_Redraw(); + + /* + ** Inform the slider of the size of the window and the current view position. + */ + ScrollGadget.Set_Maximum(List.Count()); + ScrollGadget.Set_Thumb_Size(LineCount); + ScrollGadget.Set_Value(CurrentTopIndex); + + /* + ** Return with success flag. + */ + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ListClass::Remove_Scroll_Bar -- Removes the scroll bar if present * + * * + * Use this routine to remove any attached scroll bar to this list box. If the scroll bar * + * is not present, then no action occurs. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the scroll bar removed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Remove_Scroll_Bar(void) +{ + if (IsScrollActive) { + IsScrollActive = false; + Width += ScrollGadget.Width; + ScrollGadget.Remove(); + UpGadget.Remove(); + DownGadget.Remove(); + Flag_To_Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ListClass::Set_Tabs -- Sets the tab stop list to be used for text printing. * + * * + * This sets the tab stop list to be used for text printing. It specifies a series of * + * pixel offsets for each tab stop. The offsets are from the starting pixel position that * + * the text begins at. * + * * + * INPUT: tabs -- Pointer to a list of tab pixel offsets. * + * * + * OUTPUT: none * + * * + * WARNINGS: Only a pointer to the tabs is recorded by the ListClass object. Make sure that * + * the list remains intact for the duration of the existence of this object. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Set_Tabs(int const * tabs) +{ + Tabs = tabs; +} + + +/*********************************************************************************************** + * ListClass::Draw_Entry -- Draws a list box text line as indicated. * + * * + * This routine is called by the Draw_Me function when it desired to redraw a particular * + * text line in the list box. * + * * + * INPUT: index -- The index of the list entry to draw. This index is based on the * + * total list and NOT the current visible view page. * + * * + * x,y -- Pixel coordinates for the upper left corner of the text entry. * + * * + * width -- The maximum width that the text may draw over. It is expected that * + * this drawing routine entirely fills this length. * + * * + * selected -- bool; Is this a selected (highlighted) listbox entry? * + * * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + if (TextFlags & TPF_6PT_GRAD) { + TextPrintType flags = TextFlags; + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, CC_GREEN_SHADOW); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(List[index], x, y, CC_GREEN, TBLACK, flags, width, Tabs); + + } else { + Conquer_Clip_Text_Print(List[index], x, y, (selected ? BLUE : WHITE), TBLACK, TextFlags, width, Tabs); + } +} + + +/*********************************************************************************************** + * ListClass::Add -- Adds myself to list immediately after given object * + * * + * Adds the list box to the chain, immemdiately after the given object. The order will be: * + * - Listbox * + * - Up arrow (if active) * + * - Down arrow (if active) * + * - Scroll gadget (if active) * + * * * + * INPUT: object -- Pointer to the object to be added right after this one. * + * * + * OUTPUT: Returns with a pointer to the head of the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & ListClass::Add(LinkClass & list) +{ + /* + ** Add the scroll bar gadgets if they're active. + */ + if (IsScrollActive) { + ScrollGadget.Add(list); + DownGadget.Add(list); + UpGadget.Add(list); + } + + /* + ** Add myself to the list, then return. + */ + return(ControlClass::Add(list)); +} + + +/*********************************************************************************************** + * ListClass::Add_Head -- Adds myself to head of the given list * + * * + * INPUT: list -- list to add myself to * + * * + * OUTPUT: Returns with a reference to the object at the head of the list. This should be * + * the same object that is passed in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & ListClass::Add_Head(LinkClass & list) +{ + /* + ** Add the scroll bar gadgets if they're active. + */ + if (IsScrollActive) { + ScrollGadget.Add_Head(list); + DownGadget.Add_Head(list); + UpGadget.Add_Head(list); + } + + /* + ** Add myself to the list, then return. + */ + return(ControlClass::Add_Head(list)); +} + + +/*********************************************************************************************** + * ListClass::Add_Tail -- Adds myself to tail of given list * + * * + * Adds the list box to the tail of the give chain. The order will be: * + * - Listbox * + * - Up arrow (if active) * + * - Down arrow (if active) * + * - Scroll gadget (if active) * + * * + * INPUT: list -- list to add myself to * + * * + * OUTPUT: none * + * * + * WARNINGS: The previous and next pointers for the added object MUST have been properly * + * initialized for this routine to work correctly. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & ListClass::Add_Tail(LinkClass & list) +{ + /* + ** Add myself to the list. + */ + ControlClass::Add_Tail(list); + + /* + ** Add the scroll bar gadgets if they're active. + */ + if (IsScrollActive) { + UpGadget.Add_Tail(list); + DownGadget.Add_Tail(list); + ScrollGadget.Add_Tail(list); + } + + return(Head_Of_List()); +} + + +/*********************************************************************************************** + * ListClass::Remove -- Removes the specified object from the list. * + * * + * This routine will remove the specified object from the list of objects. Because of the * + * previous and next pointers, it is possible to remove an object from the list without * + * knowing the head of the list. To do this, just call Remove() with the parameter of * + * "this". * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the new head of list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +GadgetClass * ListClass::Remove(void) +{ + /* + ** Remove the scroll bar if it's active + */ + if (IsScrollActive) { + ScrollGadget.Remove(); + DownGadget.Remove(); + UpGadget.Remove(); + } + + /* + ** Remove myself & return + */ + return(ControlClass::Remove()); +} + + +/*********************************************************************************************** + * ListClass::Set_Selected_Index -- Set the top of the listbox to index specified. * + * * + * This routine will set the top line of the listbox to the index value specified. * + * * + * INPUT: index -- The index to set the top of the listbox to. * + * * + * OUTPUT: none * + * * + * WARNINGS: The requested index may be adjusted to fit within legal parameters. * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Set_Selected_Index(int index) +{ + if ((unsigned)index < List.Count()) { + SelectedIndex = index; + Flag_To_Redraw(); + if (SelectedIndex < CurrentTopIndex) { + Set_View_Index(SelectedIndex); + } + if (SelectedIndex >= CurrentTopIndex+LineCount) { + Set_View_Index(SelectedIndex-(LineCount-1)); + } + } +} + + +/*********************************************************************************************** + * ListClass::Step_Selected_Index -- Change the listbox top line in direction specified. * + * * + * This routine will scroll the top line of the listbox in the direction specified. * + * * + * INPUT: step -- The direction (and amount) to adjust the listbox. If negative value, then * + * the top line is scrolled upward. * + * * + * OUTPUT: Returns with the original top line index number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Step_Selected_Index(int step) +{ + int old = SelectedIndex; + + Set_Selected_Index(old + step); + return(old); +} diff --git a/LIST.H b/LIST.H new file mode 100644 index 0000000..279fa83 --- /dev/null +++ b/LIST.H @@ -0,0 +1,149 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\list.h_v 2.17 16 Oct 1995 16:46:24 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LIST.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef LIST_H +#define LIST_H + +#include "control.h" +#include "shapebtn.h" +#include "slider.h" + + +/*************************************************************************** + * ListClass -- Like a Windows ListBox structure * + * * + * INPUT: int x -- x position of gadget * + * int y -- y position of gadget * + * int w -- width of gadget * + * int h -- height of gadget * + * UWORD flags -- see enumeration choices * + * * + * OUTPUT: none. * + * WARNINGS: * + * HISTORY: 01/03/1995 MML : Created. * + *=========================================================================*/ +class ListClass : public ControlClass +{ + public: + ListClass(int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down); + virtual ~ListClass(void); + +// static ListClass * Create_One_Of(int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down); + virtual int Add_Item(char const * text); + virtual int Add_Item(int text); + virtual int Add_Scroll_Bar(void); + virtual void Bump(int up); + virtual int Count(void) {return List.Count();}; + virtual int Current_Index(void); + virtual char const * Current_Item(void); + virtual int Draw_Me(int forced); + virtual char const * Get_Item(int index) const; + virtual int Step_Selected_Index(int forward); + + virtual void Peer_To_Peer(unsigned flags, KeyNumType & key, ControlClass & whom); + virtual void Remove_Item(char const * text); + virtual int Remove_Scroll_Bar(void); + virtual void Set_Selected_Index(int index); + virtual void Set_Tabs(int const * tabs); + virtual int Set_View_Index(int index); + virtual void Step(int up); + + /* + ** These overloaded list routines handle adding/removing the scroll bar + ** automatically when the list box is added or removed. + */ + virtual LinkClass & Add(LinkClass & object); + virtual LinkClass & Add_Tail(LinkClass & object); + virtual LinkClass & Add_Head(LinkClass & object); + virtual GadgetClass * Remove(void); + + protected: + virtual int Action(unsigned flags, KeyNumType &key); + virtual void Draw_Entry(int index, int x, int y, int width, int selected); + + /* + ** This controls what the text looks like. It uses the basic TPF_ flags that + ** are used to control Fancy_Text_Print(). + */ + TextPrintType TextFlags; + + /* + ** This is a series of tabstop pixel positions to use when processing any + ** characters found in a list box string. The tabs are a series of + ** pixel offsets from the starting pixel position of the text. + */ + int const *Tabs; + + /* + ** The actual list of text pointers is maintained by this list manager. The pointers + ** are stored in EMS. The text that is pointed to may also be in EMS. + */ + DynamicVectorClass List; + //EMSListOf List; + + /* + ** This is the total pixel height of a standar line of text. This is greatly + ** influenced by the TextFlags value. + */ + int LineHeight; + + /* + ** This is the number of text lines that can fit within the list box. + */ + int LineCount; + + /* + ** If the slider bar has been created, these point to the respective gadgets + ** that it is composed of. + */ + unsigned IsScrollActive:1; + ShapeButtonClass UpGadget; + ShapeButtonClass DownGadget; + SliderClass ScrollGadget; + + /* + ** This is the currently selected index. It is highlighted. + */ + int SelectedIndex; + + /* + ** This specifies the line (index) that is at the top of the list box. + */ + int CurrentTopIndex; +}; + +#endif diff --git a/LOADDLG.CPP b/LOADDLG.CPP new file mode 100644 index 0000000..33b3178 --- /dev/null +++ b/LOADDLG.CPP @@ -0,0 +1,734 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\loaddlg.cpv 2.18 16 Oct 1995 16:51:18 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LOADDLG.CPP * + * * + * Programmer : Maria Legg, Joe Bostic, Bill Randolph * + * * + * Start Date : March 19, 1995 * + * * + * Last Update : June 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LoadOptionsClass::Clear_List -- clears the list box & Files arrays * + * LoadOptionsClass::Compare -- for qsort * + * LoadOptionsClass::Fill_List -- fills the list box & GameNum arrays * + * LoadOptionsClass::LoadOptionsClass -- class constructor * + * LoadOptionsClass::Num_From_Ext -- clears the list box & GameNum arrays * + * LoadOptionsClass::Process -- main processing routine * + * LoadOptionsClass::~LoadOptionsClass -- class destructor * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include // for unlink + + +/*********************************************************************************************** + * LoadOptionsClass::LoadOptionsClass -- class constructor * + * * + * INPUT: * + * style style for this load/save dialog (LOAD/SAVE/DELETE) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +LoadOptionsClass::LoadOptionsClass(LoadStyleType style) +{ + Style = style; + Files.Clear(); +} + + +/*********************************************************************************************** + * LoadOptionsClass::~LoadOptionsClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +LoadOptionsClass::~LoadOptionsClass() +{ + for (int i = 0; i < Files.Count(); i++) { + delete Files[i]; + } + Files.Clear(); +} + + +/*********************************************************************************************** + * LoadOptionsClass::Process -- main processing routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * false = User cancelled, true = operation completed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +int LoadOptionsClass::Process(void) +{ + /* + ** Dialog & button dimensions + */ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + int d_dialog_w = 250 *factor; + int d_dialog_h = 156 * factor; + int d_dialog_x = (SeenBuff.Get_Width() - d_dialog_w) >> 1; + int d_dialog_y = (SeenBuff.Get_Height() - d_dialog_h) >> 1; + int d_dialog_cx = d_dialog_x + (d_dialog_w >> 1); + int d_txt8_h = 11 * factor; + int d_margin = 7 * factor; + + int d_list_w = d_dialog_w - (d_margin * 2); + int d_list_h = 104 * factor; + int d_list_x = d_dialog_x + d_margin; + int d_list_y = d_dialog_y + d_margin + d_txt8_h + d_margin; + + int d_edit_w = d_dialog_w - (d_margin * 2); + int d_edit_h = 13 * factor; + int d_edit_x = d_dialog_x + d_margin; + int d_edit_y = d_list_y + d_list_h - (30 * factor) + d_margin + d_txt8_h; + +#ifdef german + int d_button_w = 50 * factor; +#else + int d_button_w = 40 * factor; +#endif + int d_button_h = 13 * factor; + int d_button_x = d_dialog_cx - d_button_w - d_margin; + int d_button_y = d_dialog_y + d_dialog_h - d_button_h - d_margin; + +#ifdef german + int d_cancel_w = 50 * factor; +#else + int d_cancel_w = 40 * factor; +#endif + int d_cancel_h = 13 * factor; + int d_cancel_x = d_dialog_cx + d_margin; + int d_cancel_y = d_dialog_y + d_dialog_h - d_cancel_h - d_margin; +#if(0) + enum { + D_DIALOG_W = 250, // dialog width + D_DIALOG_H = 156, // dialog height + D_DIALOG_X = ((320 - D_DIALOG_W) / 2), // centered x-coord + D_DIALOG_Y = ((200 - D_DIALOG_H) / 2), // centered y-coord + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), // coord of x-center + + D_TXT8_H = 11, // ht of 8-pt text + D_MARGIN = 7, // margin width/height + + D_LIST_W = D_DIALOG_W - (D_MARGIN * 2), + D_LIST_H = 104, + D_LIST_X = D_DIALOG_X + D_MARGIN, + D_LIST_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H + D_MARGIN, + + D_EDIT_W = D_DIALOG_W - (D_MARGIN * 2), + D_EDIT_H = 13, + D_EDIT_X = D_DIALOG_X + D_MARGIN, + D_EDIT_Y = D_LIST_Y + D_LIST_H - 30 + D_MARGIN + D_TXT8_H, + +#if (GERMAN | FRENCH) + D_BUTTON_W = 50, +#else + D_BUTTON_W = 40, +#endif + D_BUTTON_H = 13, + D_BUTTON_X = D_DIALOG_CX - D_BUTTON_W - D_MARGIN, + D_BUTTON_Y = D_DIALOG_Y + D_DIALOG_H - D_BUTTON_H - D_MARGIN, + +#if (GERMAN | FRENCH) + D_CANCEL_W = 50,//BG:40 +#else + D_CANCEL_W = 40, +#endif + D_CANCEL_H = 13, + D_CANCEL_X = D_DIALOG_CX + D_MARGIN, + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_CANCEL_H - D_MARGIN, + }; +#endif + /* + ** Button enumerations + */ + enum { + BUTTON_LOAD = 100, + BUTTON_SAVE, + BUTTON_DELETE, + BUTTON_CANCEL, + BUTTON_LIST, + BUTTON_EDIT, + }; + + /* + ** Redraw values: in order from "top" to "bottom" layer of the dialog + */ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /* + ** Dialog variables + */ + bool cancel = false; // true = user cancels + int list_ht = d_list_h; // adjusted list box height + + /* + ** Other Variables + */ + int btn_txt; // text on the 'OK' button + int btn_id; // ID of 'OK' button + int caption; // dialog caption + int game_idx = 0; // index of game to save/load/etc + int game_num = 0; // file number of game to load/save/etc + char game_descr[40] = {0}; // save-game description + char fname[13]; // for generating filename to delete + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + /* + ** Buttons + */ + ControlClass *commands = NULL; // the button list + + if (Style == LOAD) { + btn_txt = TXT_LOAD_BUTTON; + btn_id = BUTTON_LOAD; + caption = TXT_LOAD_MISSION; + } else { + if (Style == SAVE) { + btn_txt = TXT_SAVE_BUTTON; + btn_id = BUTTON_SAVE; + caption = TXT_SAVE_MISSION; + list_ht -= 30; + } else { + btn_txt = TXT_DELETE_BUTTON; + btn_id = BUTTON_DELETE; + caption = TXT_DELETE_MISSION; + } + } + + TextButtonClass button (btn_id, btn_txt, TPF_6PT_GRAD|TPF_CENTER|TPF_NOSHADOW, + d_button_x, d_button_y, d_button_w); + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, TPF_6PT_GRAD|TPF_CENTER|TPF_NOSHADOW, + d_cancel_x, d_cancel_y, d_cancel_w); + + ListClass listbtn (BUTTON_LIST, d_list_x, d_list_y, d_list_w, list_ht, + TPF_6PT_GRAD | TPF_NOSHADOW, + up_button, + down_button); + + EditClass editbtn (BUTTON_EDIT, game_descr, 40, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW, + d_edit_x, d_edit_y, d_edit_w, -1, EditClass::ALPHANUMERIC); + + /* + ** Initialize. + */ + Set_Logic_Page(SeenBuff); + + Fill_List(&listbtn); + + /* + ** Do nothing if list is empty. + */ + if ((Style == LOAD || Style == WWDELETE) && listbtn.Count()==0) { + Clear_List(&listbtn); + CCMessageBox().Process(TXT_NO_SAVES); + return(false); + } + + /* + ** Create the button list. + */ + commands = &button; + cancelbtn.Add_Tail(*commands); + listbtn.Add_Tail(*commands); + if (Style == SAVE) { + editbtn.Add_Tail(*commands); + editbtn.Set_Focus(); + } + + /* + ** Main Processing Loop. + */ + bool firsttime = true; + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback. + */ + if (GameToPlay == GAME_NORMAL) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + cancel = true; + } + } + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=TRUE; + } + + /* + ** Refresh display if needed. + */ + if (display) { + + Hide_Mouse(); + /* + ** Redraw the map. + */ + if (InMainLoop){ + HiddenPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + }else{ + HiddenPage.Clear(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + } + + + /* + ** Display the dialog box. + */ + if (display) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption(caption, d_dialog_x, d_dialog_y, d_dialog_w); + + if (Style == SAVE) { + Fancy_Text_Print(TXT_MISSION_DESCRIPTION, d_dialog_cx, + d_edit_y - d_txt8_h, CC_GREEN, TBLACK, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_CENTER | TPF_NOSHADOW); + } + } + + /* + ** Redraw the buttons. + */ + if (display) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = false; + } + + /* + ** Get user input. + */ + KeyNumType input = commands->Input(); + + /* + ** The first time through the processing loop, set the edit + ** gadget to have the focus if this is the save dialog. The + ** focus must be set here since the gadget list has changed + ** and this change will cause any previous focus setting to be + ** cleared by the input processing routine. + */ + if (firsttime && Style == SAVE) { + firsttime = false; + editbtn.Set_Focus(); + editbtn.Flag_To_Redraw(); + } + + /* + ** If the key was pressed, then default to the appropriate + ** action button according to the style of this dialog box. + */ + if (input == KN_RETURN) { + switch (Style) { + case SAVE: + input = (KeyNumType)(BUTTON_SAVE|KN_BUTTON); + break; + + case LOAD: + input = (KeyNumType)(BUTTON_LOAD|KN_BUTTON); + break; + + case WWDELETE: + input = (KeyNumType)(BUTTON_DELETE|KN_BUTTON); + break; + } + } + + /* + ** Process input. + */ + switch (input) { + /* + ** Load: if load fails, present a message, and stay in the dialog + ** to allow the user to try another game + */ + case (BUTTON_LOAD | KN_BUTTON): + game_idx = listbtn.Current_Index(); + game_num = Files[game_idx]->Num; + if (Files[game_idx]->Valid) { + CCMessageBox().Process(TXT_LOADING, TXT_NONE); + if (!Load_Game(game_num)) { + CCMessageBox().Process(TXT_ERROR_LOADING_GAME); + } else { + Hide_Mouse(); + VisiblePage.Clear(); + Set_Palette(GamePalette); + Show_Mouse(); + process = false; + } + } else { + CCMessageBox().Process(TXT_OBSOLETE_SAVEGAME); + } + break; + + /* + ** Save: Save the game & exit the dialog + */ + case (BUTTON_SAVE | KN_BUTTON): + if (!strlen(game_descr)) { + CCMessageBox().Process(TXT_MUSTENTER_DESCRIPTION); + firsttime = true; + display = true; + break; + } + game_idx = listbtn.Current_Index(); + if (Disk_Space_Available() < SAVE_GAME_DISK_SPACE && game_idx == 0) { +// CCMessageBox().Process("Insuficent disk space to save a game. Please delete a previous save to free up some disk space and try again."); + CCMessageBox().Process(TXT_SPACE_CANT_SAVE); + firsttime = true; + display = true; + break; + } + + game_num = Files[game_idx]->Num; + if (!Save_Game(game_num,game_descr)) { + CCMessageBox().Process(TXT_ERROR_SAVING_GAME); + } else { + CCMessageBox().Process(TXT_GAME_WAS_SAVED, TXT_NONE, TXT_NONE); + } + process = false; + break; + + /* + ** Delete: delete the file & stay in the dialog, to allow the user + ** to delete multiple files. + */ + case (BUTTON_DELETE | KN_BUTTON): + game_idx = listbtn.Current_Index(); + game_num = Files[game_idx]->Num; + if (CCMessageBox().Process(TXT_DELETE_FILE_QUERY,TXT_YES,TXT_NO)==0) { + sprintf(fname,"SAVEGAME.%03d",game_num); + unlink(fname); + Clear_List(&listbtn); + Fill_List(&listbtn); + if (listbtn.Count() == 0) { + process = false; + } + } + display = true; + break; + + /* + ** If the user clicks on the list, see if the there is a new current + ** item; if so, and if we're in SAVE mode, copy the list item into + ** the save-game description field. + */ + case (BUTTON_LIST | KN_BUTTON): + if (Style != SAVE) { + break; + } + + if (listbtn.Count() && listbtn.Current_Index() != game_idx) { + game_idx = listbtn.Current_Index(); + /* + ** Copy the game's description, UNLESS it's the empty slot; if + ** it is, set the edit buffer to empty. + */ + if (game_idx != 0) { + strcpy(game_descr,listbtn.Get_Item(game_idx)); + } else { + game_descr[0] = 0; + } + editbtn.Set_Text(game_descr,40); + } + break; + + /* + ** ESC/Cancel: break + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + default: + break; + } + } + + Clear_List(&listbtn); + + if (cancel) return(false); + + return(true); +} + + +/*********************************************************************************************** + * LoadOptionsClass::Clear_List -- clears the list box & Files arrays * + * * + * This step is essential, because it frees all the strings allocated for list items. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +void LoadOptionsClass::Clear_List(ListClass *list) +{ + /* + ** For every item in the list, free its buffer & remove it from the list. + */ + int j = list->Count(); + for (int i = 0; i < j; i++) { + list->Remove_Item(list->Get_Item(0)); + } + + /* + ** Clear the array of game numbers + */ + for (i = 0; i < Files.Count(); i++) { + delete Files[i]; + } + Files.Clear(); +} + + +/*********************************************************************************************** + * LoadOptionsClass::Fill_List -- fills the list box & GameNum arrays * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 06/25/1995 JLB : Shows which saved games are "(old)". * + *=============================================================================================*/ +void LoadOptionsClass::Fill_List(ListClass *list) +{ + FileEntryClass *fdata; // for adding entries to 'Files' + char descr[DESCRIP_MAX]; + unsigned scenario; // scenario # + HousesType house; // house + struct find_t ff; // for _dos_findfirst + int id; + + /* + ** Make sure the list is empty + */ + Clear_List(list); + + /* + ** Add the Empty Slot entry + */ + if (Style == SAVE) { + fdata = new FileEntryClass; + strcpy(fdata->Descr,Text_String(TXT_EMPTY_SLOT)); + fdata->DateTime = 0xffffffff; // will always be first + Files.Add(fdata); + } + + /* + ** Find all savegame files + */ + int rc = _dos_findfirst("SAVEGAME.*", _A_NORMAL, &ff); + + while (!rc) { + /* + ** Extract the game ID from the filename + */ + id = Num_From_Ext(ff.name); + + /* + ** get the game's info; if success, add it to the list + */ + bool ok = Get_Savefile_Info(id, descr, &scenario, &house); + + fdata = new FileEntryClass; + + fdata->Descr[0] = '\0'; + if (!ok) strcpy(fdata->Descr, Text_String(TXT_OLD_GAME)); + strncat(fdata->Descr, descr, (sizeof(fdata->Descr)-strlen(fdata->Descr))-1); + fdata->Valid = ok; + fdata->Scenario = scenario; + fdata->House = house; + fdata->Num = id; + fdata->DateTime = (((unsigned long)ff.wr_date) << 16) | (unsigned long)ff.wr_time; + Files.Add(fdata); + + /* + ** Find the next file + */ + rc = _dos_findnext(&ff); + } + + /* + ** If saving a game, determine a unique file ID for the empty slot + */ + if (Style == SAVE) { + /* + ** Find an un-used number to associate with the Empty Slot by looking in + ** GameNum for each number from 0 to 'N', where 'N' is the # of entries + ** in the list; if any number isn't found, use that number; otherwise, + ** use 'N + 1'. + */ + for (int i = 0; i < Files.Count(); i++) { // i = the # we're searching for + id = -1; // mark as 'not found' + for (int j = 0; j < Files.Count(); j++) { // loop through all game ID's + if (Files[j]->Num==i) { // if found, mark as found + id = j; + break; + } + } + if (id == -1) break; // if ID not found, use this one + } + + Files[0]->Num = i; // set the empty slot's ID + } + + /* + ** Now sort the list in order of Date/Time (newest first, oldest last) + */ + qsort((void *)(&Files[0]), Files.Count(), sizeof(class FileEntryClass *), LoadOptionsClass::Compare); + + /* + ** Now add every file's name to the list box + */ + for (int i = 0; i < Files.Count(); i++) { + list->Add_Item(Files[i]->Descr); + } +} + + +/*********************************************************************************************** + * LoadOptionsClass::Num_From_Ext -- clears the list box & GameNum arrays * + * * + * INPUT: * + * fname filename to parse * + * * + * OUTPUT: * + * File number for this name. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +int LoadOptionsClass::Num_From_Ext(char *fname) +{ + char ext[_MAX_EXT]; + + _splitpath(fname, NULL, NULL, NULL, ext); + int num = atoi(ext + 1); // skip the '.' + return(num); +} + + +/*********************************************************************************************** + * LoadOptionsClass::Compare -- for qsort * + * * + * INPUT: * + * p1,p2 ptrs to elements to compare * + * * + * OUTPUT: * + * 0 = same, -1 = (*p1) goes BEFORE (*p2), 1 = (*p1) goes AFTER (*p2) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +int LoadOptionsClass::Compare(const void *p1, const void *p2) +{ + class FileEntryClass *fe1,*fe2; + + fe1 = *((class FileEntryClass **)p1); + fe2 = *((class FileEntryClass **)p2); + + if (fe1->DateTime > fe2->DateTime) return(-1); + if (fe1->DateTime < fe2->DateTime) return(1); + return(0); +} diff --git a/LOADDLG.H b/LOADDLG.H new file mode 100644 index 0000000..4e7caa2 --- /dev/null +++ b/LOADDLG.H @@ -0,0 +1,92 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\loaddlg.h_v 2.17 16 Oct 1995 16:48:02 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LOADDLG.H * + * * + * Programmer : Maria Legg, Joe Bostic, Bill Randolph * + * * + * Start Date : March 19, 1995 * + * * + * Last Update : March 19, 1995 * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef LOADDLG_H +#define LOADDLG_H + +class FileEntryClass { + public: + char Descr[40]; // save-game description + unsigned Scenario; // scenario # + HousesType House; // house + int Num; // save file number (from the extension) + unsigned long DateTime; // date/time stamp of file + bool Valid; // Is the scenario valid? +}; + +class LoadOptionsClass +{ + public: + /* + ** This defines the style of the dialog + */ + typedef enum OperationModeEnum { + NONE = 0, + LOAD, + SAVE, + WWDELETE, + } LoadStyleType; + + LoadOptionsClass (LoadStyleType style = LoadOptionsClass::NONE); + ~LoadOptionsClass (); + int Process (void); + + + protected: + /* + ** Internal routines + */ + void Clear_List (ListClass *list); // clears the list & game # array + void Fill_List (ListClass *list); // fills the list & game # array + int Num_From_Ext (char *fname); // translates filename to file # + static int Compare(const void *p1, const void *p2); // for qsort() + + /* + ** This is the requested style of the dialog + */ + LoadStyleType Style; + + /* + ** This is an array of pointers to FileEntryClass objects. These objects + ** are allocated on the fly as files are found, and pointers to them are + ** added to the vector list. Thus, all the objects must be free'd before + ** the vector list is cleared. This list is used for sorting the files + ** by date/time. + */ + DynamicVectorClass Files; +}; + + +#endif diff --git a/LOGIC.CPP b/LOGIC.CPP new file mode 100644 index 0000000..b846751 --- /dev/null +++ b/LOGIC.CPP @@ -0,0 +1,275 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\logic.cpv 2.17 16 Oct 1995 16:50:52 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LOGIC.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 27, 1993 * + * * + * Last Update : December 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LogicClass::AI -- Handles AI logic processing for game objects. * + * LogicClass::Debug_Dump -- Displays logic class status to the mono screen. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "logic.h" + +static unsigned FramesPerSecond=0; + +#ifdef CHEAT_KEYS + +static unsigned TotalFrames; +static unsigned FPSDivider = 1; +static unsigned AverageFramesPerSecond; + +/*********************************************************************************************** + * LogicClass::Debug_Dump -- Displays logic class status to the mono screen. * + * * + * This is a debugging support routine. It displays the current state of the logic class * + * to the monochrome monitor. It assumes that it is being called once per second. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine only once per second. * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void LogicClass::Debug_Dump(MonoClass *mono) const +{ + #define RECORDCOUNT 40 + #define RECORDHEIGHT 21 + static struct { + int Graphic; + } _record[RECORDCOUNT]; + static int _framecounter = 0; + + TotalFrames+= FramesPerSecond; + AverageFramesPerSecond = TotalFrames/FPSDivider++; + + mono->Set_Cursor(21, 9); + mono->Print( + "ÚÄÄÄÄÄÄÄÄÄÄÂÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\r" + "³Units.....³ ³Frame Rate: Avg: Frame: ³\r" + "³Infantry..³ ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\r" + "³Aircraft..³ ³ ³\r" + "³Buildings.³ ³ ³\r" + "³Terrain...³ Ã ´\r" + "³Bullets...³ ³ ³\r" + "³Anims.....³ ³ ³\r" + "³Teams.....³ Ã Ä´\r" + "³Triggers..³ ³ ³\r" + "³Factories.³ ³ ³\r" + "³ ³ Ã ´\r" + "³ ³ ³ ³\r" + "ÀÄÄÄÄÄÄÄÄÄÄÁÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄ´Spare CPU TimeÃÄÄÄÄÄÄÄÄÄÄÄÄÙ\r"); + + _framecounter++; + mono->Set_Cursor(70, 10);mono->Printf("%ld", Frame); + if (ScenarioInit) { + mono->Set_Cursor(21, 9);mono->Printf("%d", ScenarioInit); + } + + mono->Set_Cursor(33, 10);mono->Printf("%3d", Units.Count()); + mono->Set_Cursor(33, 11);mono->Printf("%3d", Infantry.Count()); + mono->Set_Cursor(33, 12);mono->Printf("%3d", Aircraft.Count()); + mono->Set_Cursor(33, 13);mono->Printf("%3d", Buildings.Count()); + mono->Set_Cursor(33, 14);mono->Printf("%3d", Terrains.Count()); + mono->Set_Cursor(33, 15);mono->Printf("%3d", Bullets.Count()); + mono->Set_Cursor(33, 16);mono->Printf("%3d", Anims.Count()); + mono->Set_Cursor(33, 17);mono->Printf("%3d", Teams.Count()); + mono->Set_Cursor(33, 18);mono->Printf("%3d", Triggers.Count()); + mono->Set_Cursor(33, 19);mono->Printf("%3d", Factories.Count()); + + mono->Set_Cursor(48, 10);mono->Printf("%d", FramesPerSecond); + mono->Set_Cursor(58, 10);mono->Printf("%d", AverageFramesPerSecond); + + /* + ** Advance to the next recorded performance record. If the record buffer + ** is full then throw out the oldest record. + */ + memcpy(&_record[0], &_record[1], sizeof(_record[0])*(RECORDCOUNT-1)); + + /* + ** Fill in the data for the current frame's performance record. + */ + SpareTicks = MIN((long)SpareTicks, (long)TIMER_SECOND); + _record[RECORDCOUNT-1].Graphic = Fixed_To_Cardinal(RECORDHEIGHT, Cardinal_To_Fixed(TIMER_SECOND, SpareTicks)); + + /* + ** Draw the bars across the performance record screen. + */ + for (int column = 0; column < RECORDCOUNT; column++) { + for (int row = 1; row < RECORDHEIGHT; row += 2) { + static char _barchar[4] = {' ', 220, 0, 219}; + char str[2]; + int index = 0; + + index |= (_record[column].Graphic >= row) ? 0x01 : 0x00; + index |= (_record[column].Graphic >= row+1) ? 0x02: 0x00; + + str[1] = '\0'; + str[0] = _barchar[index]; + mono->Text_Print(str, 37+column, 21-(row/2)); + } + } + + SpareTicks = 0; + FramesPerSecond = 0; +} +#endif + + +/*********************************************************************************************** + * LogicClass::AI -- Handles AI logic processing for game objects. * + * * + * This routine is used to perform the AI processing for all game objects. This includes * + * all houses, factories, objects, and teams. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/29/1994 JLB : Created. * + * 12/17/1994 JLB : Must perform one complete pass rather than bailing early. * + * 12/23/1994 JLB : Esures that no object gets skipped if it was deleted. * + *=============================================================================================*/ +void LogicClass::AI(void) +{ + int index; + + FramesPerSecond++; + + /* + ** Crate regeneration is handled here. + */ + if (GameToPlay != GAME_NORMAL && CrateMaker && CrateTimer.Expired()) { + Map.Place_Random_Crate(); + CrateTimer = TICKS_PER_MINUTE * Random_Pick(7, 15); + } + + /* + ** Team AI is processed. + */ + for (index = 0; index < Teams.Count(); index++) { + Teams.Ptr(index)->AI(); + } + +// Heap_Dump_Check( "After Team AI" ); + + /* + ** AI for all sentient objects is processed. + */ + for (index = 0; index < Count(); index++) { + ObjectClass * obj = (*this)[index]; + + obj->AI(); + + /* + ** If the object was destroyed in the process of performing its AI, then + ** adjust the index so that no object gets skipped. + */ + if (obj != (*this)[index]) { +// if (!obj->IsActive) { + index--; + } + } + +// Heap_Dump_Check( "After Object AI" ); + + /* + ** A second pass through the sentient objects is required so that the appropriate scan + ** bits will be set for the owner house. + */ + for (index = 0; index < Units.Count(); index++) { + UnitClass const * unit = Units.Ptr(index); + if (unit->IsLocked && (GameToPlay != GAME_NORMAL || !unit->House->IsHuman || unit->IsDiscoveredByPlayer)) { + unit->House->NewUScan |= (1L << unit->Class->Type); + if (!unit->IsInLimbo) unit->House->NewActiveUScan |= (1L << unit->Class->Type); + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass const * infantry = Infantry.Ptr(index); + if (infantry->IsLocked && (GameToPlay != GAME_NORMAL || !infantry->House->IsHuman || infantry->IsDiscoveredByPlayer)) { + infantry->House->NewIScan |= (1L << infantry->Class->Type); + if (!infantry->IsInLimbo) infantry->House->NewActiveIScan |= (1L << infantry->Class->Type); + } + } + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass const * aircraft = Aircraft.Ptr(index); + if (aircraft->IsLocked && (GameToPlay != GAME_NORMAL || !aircraft->House->IsHuman || aircraft->IsDiscoveredByPlayer)) { + aircraft->House->NewAScan |= (1L << aircraft->Class->Type); + if (!aircraft->IsInLimbo) aircraft->House->NewActiveAScan |= (1L << aircraft->Class->Type); + } + } + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass const * building = Buildings.Ptr(index); + if (building->IsLocked && (GameToPlay != GAME_NORMAL || !building->House->IsHuman || building->IsDiscoveredByPlayer)) { + building->House->NewBScan |= (1L << building->Class->Type); + if (!building->IsInLimbo) building->House->NewActiveBScan |= (1L << building->Class->Type); + } + } + +// Heap_Dump_Check( "After Object AI 2" ); + + /* + ** Map related logic is performed. + */ + Map.Logic(); + +// Heap_Dump_Check( "After Map.Logic" ); + + /* + ** Factory processing is performed. + */ + for (index = 0; index < Factories.Count(); index++) { + Factories.Ptr(index)->AI(); + } + +// Heap_Dump_Check( "After Factory AI" ); + + /* + ** House processing is performed. + */ + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr && hptr->IsActive) { + hptr->AI(); + } + } + +// Heap_Dump_Check( "After House AI" ); +} + + diff --git a/LOGIC.H b/LOGIC.H new file mode 100644 index 0000000..e79dbca --- /dev/null +++ b/LOGIC.H @@ -0,0 +1,55 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\logic.h_v 2.17 16 Oct 1995 16:45:52 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LOGIC.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 29, 1994 * + * * + * Last Update : May 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef LOGIC_H +#define LOGIC_H + +#include "layer.h" + +/*********************************************************************************************** +** Game logic processing is controlled by this class. The graphic and AI logic is handled +** separately so that on slower machines, the graphic display is least affected. +*/ +class LogicClass : public LayerClass +{ + public: + void AI(void); + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const; + #endif +}; +#endif diff --git a/MAKEFILE b/MAKEFILE new file mode 100644 index 0000000..bef05c5 --- /dev/null +++ b/MAKEFILE @@ -0,0 +1,950 @@ +# +# Command & Conquer(tm) +# Copyright 2025 Electronic Arts Inc. +# +# 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 3 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +#* $Header$ +#*********************************************************************************************** +#*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** +#*********************************************************************************************** +#* * +#* Project Name : Command & Conquer * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Joe L. Bostic * +#* * +#* Start Date : 03/02/95 * +#* * +#* Last Update : March 2, 1995 [JLB] * +#* * +#*---------------------------------------------------------------------------------------------* +#* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +.SILENT +.OPTIMIZE +.ERASE + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- + +!ifndef %WWFLAT +!error WWFLAT must be set to the root of the library directory. +!else +WWFLAT=$(%WWFLAT) +!endif + +!ifndef %WATCOM +#WATCOM=c:\projects\c&c\code\watcom +!error WATCOM must be set to the Watcom root directory. +!else +WATCOM=$(%WATCOM) +!endif + +!ifndef %CODEDIR +#CODEDIR=c:\projects\code +!error CODEDIR must be set to the root code directory. +!else +CODEDIR=$(%CODEDIR) +!endif + +!ifndef %CDDIR +!error CODEDIR must be set. +#CDDIR=..\cd +!else +CDDIR=$(%CDDIR) +!endif + +!ifndef %VQDIR +#VQDIR=c:\VQA +!error VQDIR must be set to the root VQ directory. +!else +VQDIR=$(%VQDIR) +!endif + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: . +.c: . +.cpp: . +.h: . +.obj: $(%WWOBJ)obj +.lib: $(WWFLAT)\lib +.exe: ..\run + + +#=========================================================================== +# Compiler and assembler flags. +#=========================================================================== +CC_CFG = /i=$(VQDIR)\include # Includes player (VQ) directory. +CC_CFG += /i=$(WWFLAT)\INCLUDE # Normal library include directory. +CC_CFG += /i=$(WATCOM)\H\nt # Normal Watcom include directory. +CC_CFG += /i=$(WATCOM)\H # Normal Watcom include directory. +CC_CFG += /i=..\gcl510\H # Includes Greenleaf headers. + +VCT_CFG = /i=$(VQDIR)\include # Includes player (VQ) directory. +VCT_CFG += /i=$(WWFLAT)\INCLUDE # Normal library include directory. +VCT_CFG += /i=$(WATCOM)\H\nt # Normal Watcom include directory. +VCT_CFG += /i=$(WATCOM)\H # Normal Watcom include directory. +VCT_CFG += /i=..\gcl510\H # Includes Greenleaf headers. + +#CC_CFG += /DDOS4G # Must be defined for Greenleaf +#CC_CFG += /DGF_WATCOM_S # Must be defined for Greenleaf with /3s +#CC_CFG += /d3 # Debugging information. +#CC_CFG += /d1 # Debugging information. +#CC_CFG += /of+ # Generate traceable stack frames. +#CC_CFG += /DOPTION=$(%OPTION) # Optional option define. +#CC_CFG += /zp1 # Pack structures on byte boundary. +#CC_CFG += /5s # Pentium optimized stack calling conventions. +#CC_CFG += /xs # Exception handling enabled. +#CC_CFG += /s # Remove stack check calls. +#CC_CFG += /j # char is now signed. +#CC_CFG += /fh=$(%WWOBJ)conquer.pch # Use precompiled headers. +#CC_CFG += /we # Treat all warnings as errors. +#CC_CFG += /w8 # Most warnings enabled. +#CC_CFG += /ri # char and shorts are returned as int. +CC_CFG += /zq # Operate quietly. +#CC_CFG += /zm # Each routine to be in its own segment. +#CC_CFG += /zld # Disable autodependency information in object file. + +#CC_CFG += /bm # build target is a multi-thread environment +#CC_CFG += /mf # flat model +#CC_CFG += /ze # enable language extensions +#CC_CFG += /zw # create windows code + +#CC_CFG += /od # *** Disable all optimizations *** +#CC_CFG += /ol # Loop optimizations enabled. +#CC_CFG += /or # Reorder instructions for best pipeline usage. +#CC_CFG += /oe # Inline is enabled. +#CC_CFG += /oi # Expand intrisic functions inline. +#CC_CFG += /on # Allow numerically unstable operations. +#CC_CFG += /oo # Compile even if low on memory (i.e. less than 64meg). +#CC_CFG += /oa # Relax aliasing constraints. + +#CC_CFG += -bt=NT /i=q:\include -j -os -zz -W3 -d1 /5 -s -fh=c:\projects\code\conquer.pch +#CC_CFG += -bt=NT /i=q:\include -j -otexan -ol+ -zz -W3 -d1 /5 -s -fh=c:\projects\code\conquer.pch +#CC_CFG += -bt=NT /i=q:\include -zq -j -zz -W3 -d1 /5 -s -fh=c:\projects\code\conquer.pch +CC_CFG += -bt=NT /i=q:\include -j -W3 -zz -d1 -otxan -ol+ /5 -s -fh=d:\projects\ccgold\code\conquer.pch +#CC_CFG += -bt=NT /i=q:\include -j -W3 -zz -d2 -od /5 -s -fh=c:\projects\code\conquer.pch +#CC_CFG += -bt=95 /i=q:\include -j -W3 -hc -od -d3 /4 -s +#CC_CFG += -bt=NT /i=q:\include -j -W3 -d1 -orilt /4 -s -fh=c:\projects\code\conquer.pch -fhq +#CC_CFG += -bt=NT /i=q:\include -j -W3 -d2 -orilt /4 -s -ep -ee -fh=c:\projects\code\conquer.pch -fhq + +ASM_CFG = /i$(WWFLAT)\INCLUDE # Include directory. +ASM_CFG += /zd # Debugging information line numbers. +ASM_CFG += /t # Quiet operation. +ASM_CFG += /m # Allow multiple passes. +ASM_CFG += /w+ # Enable maximum warnings. +ASM_CFG += /jJUMPS # Enable jump optimizations. +ASM_CFG += /ml # Case sensitivity on code. +#ASM_CFG += /zi # Full debugging information. + + +VCT_CFG += -bt=NT /i=q:\include -j -W3 -zz -d2 -od /5 -s -fh=d:\projects\ccgold\code\conquer.pch + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj:#.AUTODEPEND + echo Compiling $< + *watcom\wcc $(C_CFG) -fo$(%WWOBJ)obj\$^. $< + +.cpp.obj: #.AUTODEPEND + echo Compiling $< +!ifdef %NETWORK + f:\projects\c&c95\slaves\NETEXEC /v f:\projects\c&c95\slaves\$(%NETWHO)c $^& + if exist $(%CCNETDIR)\code\netmake.err %abort +!else + *$(WATCOM)\binnt\wpp386 $(CC_CFG) -fo$(%WWOBJ)obj\$^. $(CODEDIR)\$< +!endif + +.asm.obj: + echo Assembling $< +!ifdef %NETWORK + f:\projects\c&c95\slaves\NETEXEC /v f:\projects\c&c95\slaves\$(%NETWHO)a $^& +!else + tasm $(ASM_CFG) $<, $(%WWOBJ)obj\$^. +!endif + + + + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + SUPER.OBJ & + AADATA.OBJ & + WINSTUB.OBJ & + WINASM.OBJ & + ABSTRACT.OBJ & + ADATA.OBJ & + AIRCRAFT.OBJ & + ANIM.OBJ & + AUDIO.OBJ & + BASE.OBJ & + BBDATA.OBJ & + BDATA.OBJ & + BUILDING.OBJ & + BULLET.OBJ & + CARGO.OBJ & + CCFILE.OBJ & + CDATA.OBJ & + CDFILE.OBJ & + CELL.OBJ & + CHECKBOX.OBJ & + CHEKLIST.OBJ & + COLRLIST.OBJ & + COMBAT.OBJ & + COMBUF.OBJ & + CONFDLG.OBJ & + CONNECT.OBJ & + CONQUER.OBJ & + CONST.OBJ & + CONTROL.OBJ & + COORD.OBJ & + CREDITS.OBJ & + CREW.OBJ & + DEBUG.OBJ & + DIAL8.OBJ & + DIALOG.OBJ & + DISPLAY.OBJ & + DOOR.OBJ & + DRIVE.OBJ & + EDIT.OBJ & + EVENT.OBJ & + ENDING.OBJ & + EXPAND.OBJ & + FACING.OBJ & + FACTORY.OBJ & + FINDPATH.OBJ & + FLASHER.OBJ & + FLY.OBJ & + FOOT.OBJ & + FUSE.OBJ & + GADGET.OBJ & + GAMEDLG.OBJ & + GAUGE.OBJ & + GLOBALS.OBJ & + GOPTIONS.OBJ & + GSCREEN.OBJ & + HDATA.OBJ & + HEAP.OBJ & + HELP.OBJ & + HOUSE.OBJ & + IDATA.OBJ & + INFANTRY.OBJ & + INI.OBJ & + INIT.OBJ & + INTERNET.OBJ & + INTERPAL.OBJ & + INTRO.OBJ & + IOMAP.OBJ & + IOOBJ.OBJ & + IPX.OBJ & + IPXADDR.OBJ & + IPXCONN.OBJ & + IPXGCONN.OBJ & + IPXMGR.OBJ & + IPX95.OBJ & + JSHELL.OBJ & + KEYFBUFF.OBJ & + KEYFRAME.OBJ & + LAYER.OBJ & + LINK.OBJ & + LIST.OBJ & + LOADDLG.OBJ & + LOGIC.OBJ & + MAP.OBJ & + MAPEDDLG.OBJ & + MAPEDIT.OBJ & + MAPEDPLC.OBJ & + MAPEDTM.OBJ & + MAPSEL.OBJ & + MENUS.OBJ & + MISSION.OBJ & + MIXFILE.OBJ & + MOUSE.OBJ & + MPLAYER.OBJ & + MSGBOX.OBJ & + MSGLIST.OBJ & + NETDLG.OBJ & + NOSEQCON.OBJ & + NULLCONN.OBJ & + NULLDLG.OBJ & + NULLMGR.OBJ & + OBJECT.OBJ & + ODATA.OBJ & + OPTIONS.OBJ & + OVERLAY.OBJ & + POWER.OBJ & + PROFILE.OBJ & + QUEUE.OBJ & + RADAR.OBJ & + RADIO.OBJ & + RAND.OBJ & + REINF.OBJ & + SAVELOAD.OBJ & + SCENARIO.OBJ & + SCORE.OBJ & + SCROLL.OBJ & + SDATA.OBJ & + SHAPEBTN.OBJ & + SIDEBAR.OBJ & + SLIDER.OBJ & + SMUDGE.OBJ & + SOUNDDLG.OBJ & + SPECIAL.OBJ & + STARTUP.OBJ & + SUPPORT.OBJ & + TAB.OBJ & + TARCOM.OBJ & + TARGET.OBJ & + TCPIP.OBJ & + TDATA.OBJ & + TEAM.OBJ & + TEAMTYPE.OBJ & + TECHNO.OBJ & + TEMPLATE.OBJ & + TERRAIN.OBJ & + TEXTBTN.OBJ & + THEME.OBJ & + TOGGLE.OBJ & + TRIGGER.OBJ & + TURRET.OBJ & + TXTLABEL.OBJ & + TXTPRNT.OBJ & + UDATA.OBJ & + UNIT.OBJ & + VECTOR.OBJ & + VISUDLG.OBJ & + UTRACKER.OBJ & + PACKET.OBJ & + FIELD.OBJ & + STATS.OBJ & + CCDDE.OBJ & + DDE.OBJ & +# ALLOC.OBJ +# DESCDLG.OBJ & +# COORDA.OBJ & + +PROJ_LIBS =# & + #wwflat32.lib + +VQ_LIBS = & + vqa32wp.lib & + vqm32wp.lib + +GCL_LIBS = & + gclfr3s.lib + + +############################################################################ +# Pre-compilation process. Move old files to backup directory and switch +# to monochrome screen. +.BEFORE +!ifndef %SLAVE +# mode mono +!endif + -if exist $(%WWOBJ)*.pch del $(%WWOBJ)*.pch + -if exist $(%CCNETDIR)\code\netmake.err del $(%CCNETDIR)\code\netmake.err + -if exist *.bak move *.bak bak + +# Switch back to normal screen at compilation end. +.AFTER +!ifndef %SLAVE +# mode co80 +# ncc /50 +!endif + +# Switch back to normal screen if there was an error. +.ERROR +!ifndef %SLAVE +# mode co80 +# ncc /50 +!endif + + +############################################################################# +# Default target +all: conquer.exe + + +############################################################################# +# Builds the stub replacement program. +CWSTUB.OBJ: CWSTUB.C + *watcom\wcc /i=watcom\h /dQUIET /dVMM /ms /zQ -fo$(%WWOBJ)obj\$^. $< + +CWSTUB.EXE: CWSTUB.OBJ + *watcom\wlink system dos file $(%WWOBJ)obj\cwstub.obj name cwstub.exe option quiet library watcom\lib286\dos\clibs.lib, watcom\lib286\math87s.lib, watcom\lib286\dos\emu87.lib + + +############################################################################# +# Build the EXE +conquer.exe: $(OBJECTS) obj\mmx.obj conquer.lnk $(WWFLAT)\lib\win32lib.lib $(VQDIR)\lib\vqa32wp.lib $(VQDIR)\lib\vqm32wp.lib + + Echo "conquer.exe" linking phase. +!ifdef %NETWORK + echo Waiting for objects... + for %index in ($(OBJECTS)) do f:\projects\c&c95\slaves\WAITFILE $(%CCNETDIR)\code\obj\%index + ndos.com /c copy $(%CCNETDIR)\code\obj\*.obj $(%CCLOCALDIR)\code\obj /U +!endif + echo Linking the executable. + $(WATCOM)\binnt\wlink name ..\run\$@ @conquer.lnk + $(WATCOM)\binnt\WRC cc_icon ..\run\conquer.exe + Echo "conquer.exe" executable completed. +!ifdef %WWOBJ + xcopy /M e:\obj\*.* c:\projects\c&c\code\obj +!endif +# watcom\wlink $(LINK_CFG) name ..\run\$@ @conquer.lnk + +############################################################################# +# This creates the linker command file. +conquer.lnk : makefile + %create $^@ + %append $^@ system win95 + %append $^@ option stack=128k + %append $^@ option redefsok + %append $^@ option quiet + %append $^@ option map +# %append $^@ option cache + %append $^@ option eliminate + %append $^@ option caseexact +# %append $^@ option stub=cwstub.exe +# %append $^@ debug all + %append $^@ debug all + for %index in ($(OBJECTS)) do %append $^@ file $(%WWOBJ)obj\%index + %append $^@ file $(%WWOBJ)obj\mmx.obj +# %append $^@ file $(%WWOBJ)obj\vector.obj +# for %index in ($(PROJ_LIBS)) do %append $^@ library $(WWFLAT)\lib\%index +# for %index in ($(VQ_LIBS)) do %append $^@ library ..\vq\lib\%index +# for %index in ($(GCL_LIBS)) do %append $^@ library ..\gcl510\w10\%index + %append $^@ library $(WWFLAT)\lib\ddraw.lib + %append $^@ library $(WWFLAT)\lib\dsound.lib + %append $^@ library $(WWFLAT)\lib\keyboard.lib + %append $^@ library $(WWFLAT)\lib\win32lib.lib + %append $^@ library $(VQDIR)\lib\vqa32wp.lib + %append $^@ library $(VQDIR)\lib\vqm32wp.lib + %append $^@ library ipx\wwipx32.lib + + + +############################################################################## +# Creates a symbol-less executable and copies it to the net cd directories +cd: .SYMBOLIC + -wstrip ..\run\conquer.exe ..\run\c&c95.exe + -copy ..\run\c&c95.exe f:\projects\c&c95\setup\data + + +################################################################## +# +# The MMX stuff requires MASM 6.11d so it needs its own rule +# +obj\mmx.obj: mmx.asm mmx.inc + d:\masm611\bin\ml /I. /c /Cx /Zd /Cp /Flmmx.txt /Sc /Foobj\mmx.obj mmx.asm + + + + +############################################################# +# Creates a bound executable in the install directory. +bind: .SYMBOLIC + -copy ..\run\conquer.exe ..\run\temp.exe + -wstrip ..\run\temp.exe + -watcom\4gwbind watcom\4gwpro.exe ..\run\temp.exe ..\cd1\install\c&c.exe -f + -copy ..\cd1\install\c&c.exe ..\cd2\install /u /v + -del ..\run\temp.exe + + +############################################################# +# Update source and art to network. +update: bind .SYMBOLIC +!ifdef %CDDIR + -copy ..\cd1\*.* $(CDDIR)cd1 /v /u /s + -copy ..\cd2\*.* $(CDDIR)cd2 /v /u /s + -copy $(CDDIR)cd1\*.* f:\projects\c&c\cd\cd1 /v /u /s + -copy $(CDDIR)cd2\*.* f:\projects\c&c\cd\cd2 /v /u /s +!else + -copy ..\cd1\*.* f:\projects\c&c\cd\cd1 /v /u /s + -copy ..\cd2\*.* f:\projects\c&c\cd\cd2 /v /u /s +!endif + -copy watcom\dos4gw.exe f:\projects\c&c\playtest + -copy ..\run\conquer.exe f:\projects\c&c\playtest /u /v + -copy conquer.map f:\projects\c&c\playtest /u /v + -mkdir f:\projects\c&c\playtest\%_DATE + -copy ..\run\conquer.exe f:\projects\c&c\playtest\%_DATE /u /v + -copy conquer.map f:\projects\c&c\playtest\%_DATE /u /v + -copy ..\art\ingame\*.* f:\projects\c&c\art\ingame /u /v /s + -copy *.* f:\projects\c&c\code /v /s /u + +############################################################################# +# Explicit rules to build the master zip files (used by Codewrite merge). +BILL_R.ZIP: pkzip.dat .SYMBOLIC + -del f:\projects\c&c\bill_r.zip + -pkzip -p -u -x@pkzip.dat f:\projects\c&c\bill_r.zip *.* eng\*.* + +MARIA_L.ZIP: pkzip.dat .SYMBOLIC + -del f:\projects\c&c\maria_l.zip + -pkzip -p -u -x@pkzip.dat f:\projects\c&c\maria_l.zip *.* eng\*.* + +BARRY_G.ZIP: pkzip.dat .SYMBOLIC + -del f:\projects\c&c\barry_g.zip + -pkzip -p -u -x@pkzip.dat f:\projects\c&c\barry_g.zip *.* eng\*.* + +PHIL_G.ZIP: pkzip.dat .SYMBOLIC + -del f:\projects\c&c95\phil_g.zip + -pkzip -p -u -x@pkzip.dat f:\projects\c&c95\phil_g.zip *.* + +win32lib.ZIP: pkzip.dat .SYMBOLIC + -del f:\projects\c&c95\win32lib.zip + -pkzip -p -r f:\projects\c&c95\win32lib.zip d:\win32lib\*.* + +DAVID_D.ZIP: pkzip.dat .SYMBOLIC + -del f:\projects\c&c\david_d.zip + -pkzip -p -u -x@pkzip.dat f:\projects\c&c\david_d.zip *.* eng\*.* + +BILL_P.ZIP: pkzip.dat .SYMBOLIC + -del f:\projects\c&c\bill_p.zip + -pkzip -p -u -x@pkzip.dat f:\projects\c&c\bill_p.zip *.* eng\*.* + +# Special "mega-zip" process. +JOE_B.ZIP: pkzip.dat .SYMBOLIC + -pkzip -rp -u -xcps\*.* -x@pkzip.dat f:\projects\c&c\joe_b.zip + + +############################################################################# +# Rebuilds the master zip control file. This is used by the zip process. +PKZIP.DAT: makefile .SYMBOLIC + %create $^@ + %append $^@ *.000 + %append $^@ *.@@@ + %append $^@ *.bak + %append $^@ *.bat + %append $^@ *.cfg + %append $^@ *.dat + %append $^@ *.def + %append $^@ *.doc + %append $^@ *.dsw + %append $^@ *.err + %append $^@ *.ewp + %append $^@ *.ext + %append $^@ *.i + %append $^@ *.ide + %append $^@ *.lnk + %append $^@ *.log + %append $^@ *.lst + %append $^@ *.mac + %append $^@ *.map + %append $^@ *.mk + %append $^@ *.mk1 + %append $^@ *.obj + %append $^@ *.out + %append $^@ *.pch + %append $^@ *.pfs + %append $^@ *.pif + %append $^@ *.pjt + %append $^@ *.prf + %append $^@ *.pro + %append $^@ *.ptg + %append $^@ *.rc + %append $^@ *.rep + %append $^@ *.rpt + %append $^@ *.rst + %append $^@ *.sym + %append $^@ *.tag + %append $^@ *.td + %append $^@ *.td + %append $^@ *.tgt + %append $^@ *.tmp + %append $^@ *.tr + %append $^@ *.tr + %append $^@ *.vec + %append $^@ *.wpj + %append $^@ *.zip + %append $^@ state.rst + + +#--------------------------------------------------------------------------- +# Dependency macros (makes defining dependencies easier) +#--------------------------------------------------------------------------- +GENERAL_H = defines.h function.h externs.h conquer.h vector.h heap.h & + debug.h jshell.h compat.h + +TECHNO_H = facing.h techno.h mission.h stage.h cargo.h object.h abstract.h + +UNIT_H = unit.h tarcom.h turret.h drive.h foot.h radio.h $(TECHNO_H) + +INFANTRY_H = infantry.h foot.h radio.h $(TECHNO_H) + +AIRCRAFT_H = aircraft.h fly.h radio.h $(TECHNO_H) + +BUILDING_H = building.h radio.h $(TECHNO_H) + +BULLET_H = bullet.h fly.h fuse.h object.h abstract.h + +OBJ_H = $(UNIT_H) $(INFANTRY_H) $(AIRCRAFT_H) $(BUILDING_H) $(BULLET_H) + +MAP_H = base.h mapedit.h mouse.h scroll.h help.h tab.h power.h sidebar.h & + radar.h display.h map.h gscreen.h cell.h + +GADGET_H = textbtn.h shapebtn.h slider.h gauge.h dial8.h edit.h & + toggle.h list.h cheklist.h control.h gadget.h link.h + +FILE_H = ccfile.h cdfile.h mixfile.h rawfile.h wwfile.h link.h + +TEAM_H = team.h teamtype.h trigger.h + +IPX_H = ipx.h ipxaddr.h + +NET_H = combuf.h connect.h connmgr.h ipx.h ipxaddr.h ipxconn.h ipxgconn.h & + ipxmgr.h noseqcon.h nullconn.h nullmgr.h + +MISC_H = ftimer.h logic.h score.h theme.h event.h queue.h special.h + +#--------------------------------------------------------------------------- +# Dependencies (This is not totally accurate; if you're not sure, rebuild +# everything!) +#--------------------------------------------------------------------------- +aadata.obj: aadata.cpp $(GENERAL_H) type.h + +winstub.obj: winstub.cpp tcpip.h $(GENERAL_H) + +winasm.obj: winasm.asm + +abstract.obj: abstract.cpp $(GENERAL_H) abstract.h + +adata.obj: adata.cpp $(GENERAL_H) type.h + +aircraft.obj: aircraft.cpp $(GENERAL_H) $(AIRCRAFT_H) + +anim.obj: anim.cpp $(GENERAL_H) anim.h stage.h object.h + +audio.obj: audio.cpp $(GENERAL_H) audio.h + +base.obj: base.cpp $(GENERAL_H) type.h + +bbdata.obj: bbdata.cpp $(GENERAL_H) type.h + +bdata.obj: bdata.cpp $(GENERAL_H) type.h + +building.obj: building.cpp $(GENERAL_H) $(BUILDING_H) + +bullet.obj: bullet.cpp $(GENERAL_H) $(BULLET_H) + +cargo.obj: cargo.cpp $(GENERAL_H) $(TECHNO_H) cargo.h + +ccfile.obj: ccfile.cpp $(GENERAL_H) $(FILE_H) + +cdata.obj: cdata.cpp $(GENERAL_H) type.h + +cdfile.obj: cdfile.cpp $(GENERAL_H) $(FILE_H) + +cell.obj: cell.cpp $(GENERAL_H) $(MAP_H) + +checkbox.obj: checkbox.cpp $(GENERAL_H) $(GADGET_H) + +cheklist.obj: cheklist.cpp $(GENERAL_H) $(GADGET_H) + +colrlist.obj: colrlist.cpp $(GENERAL_H) $(GADGET_H) + +combat.obj: combat.cpp $(GENERAL_H) + +combuf.obj: combuf.cpp $(GENERAL_H) combuf.h + +confdlg.obj: confdlg.cpp $(GENERAL_H) $(GADGET_H) + +connect.obj: connect.cpp $(GENERAL_H) connect.h combuf.h + +conquer.obj: conquer.cpp $(GENERAL_H) $(MISC_H) $(OBJ_H) tcpip.h ccdde.h + +const.obj: const.cpp $(GENERAL_H) + +control.obj: control.cpp $(GENERAL_H) $(GADGET_H) + +coord.obj: coord.cpp $(GENERAL_H) + +coorda.obj: coorda.asm + +credits.obj: credits.cpp $(GENERAL_H) credits.h + +crew.obj: crew.cpp $(GENERAL_H) crew.h + +debug.obj: debug.cpp $(GENERAL_H) debug.h + +deldlg.obj: deldlg.cpp $(GENERAL_H) $(GADGET_H) + +#descdlg.obj: descdlg.cpp $(GENERAL_H) $(GADGET_H) + +dial8.obj: dial8.cpp $(GENERAL_H) $(GADGET_H) + +dialog.obj: dialog.cpp $(GENERAL_H) + +display.obj: display.cpp $(GENERAL_H) $(MAP_H) + +door.obj: door.cpp $(GENERAL_H) + +drive.obj: drive.cpp $(GENERAL_H) $(TECHNO_H) + +edit.obj: edit.cpp $(GENERAL_H) $(GADGET_H) + +event.obj: event.cpp $(GENERAL_H) $(MISC_H) ccdde.h + +ending.obj: ending.cpp $(GENERAL_H) $(MISC_H) + +expand.obj: expand.cpp $(GENERAL) + +facing.obj: facing.cpp $(GENERAL_H) facing.h + +factory.obj: factory.cpp $(GENERAL_H) factory.h + +findpath.obj: findpath.cpp $(GENERAL_H) + +flasher.obj: flasher.cpp $(GENERAL_H) flasher.h + +fly.obj: fly.cpp $(GENERAL_H) fly.h + +foot.obj: foot.cpp $(GENERAL_H) $(INFANTRY_H) + +fuse.obj: fuse.cpp $(GENERAL_H) fuse.h + +gadget.obj: gadget.cpp $(GENERAL_H) $(GADGET_H) + +gamedlg.obj: gamedlg.cpp $(GENERAL_H) $(GADGET_H) + +gauge.obj: gauge.cpp $(GENERAL_H) $(GADGET_H) + +globals.obj: globals.cpp $(GENERAL_H) + +goptions.obj: goptions.cpp $(GENERAL_H) $(GADGET_H) + +gscreen.obj: gscreen.cpp $(GENERAL_H) $(MAP_H) + +hdata.obj: hdata.cpp $(GENERAL_H) type.h + +heap.obj: heap.cpp $(GENERAL_H) $(MISC_H) + +help.obj: help.cpp $(GENERAL_H) $(MAP_H) + +house.obj: house.cpp $(GENERAL_H) house.h + +idata.obj: idata.cpp $(GENERAL_H) type.h + +infantry.obj: infantry.cpp $(GENERAL_H) $(INFANTRY_H) + +ini.obj: ini.cpp $(GENERAL_H) $(MISC_H) + +init.obj: init.cpp $(GENERAL_H) $(MISC_H) $(OBJ_H) tcpip.h ccdde.h + +internet.obj: internet.cpp $(GENERAL_H) $(MISC_H) tcpip.h ccdde.h + +interpal.obj: interpal.cpp $(GENERAL_H) $(MISC_H) + +intro.obj: intro.cpp $(GENERAL_H) $(MISC_H) + +iomap.obj: iomap.cpp $(GENERAL_H) $(MAP_H) $(FILE_H) + +ioobj.obj: ioobj.cpp $(GENERAL_H) $(FILE_H) $(OBJ_H) + +ipx.obj: ipx.cpp $(GENERAL_H) $(IPX_H) + +ipxaddr.obj: ipxaddr.cpp $(GENERAL_H) $(IPX_H) + +ipxconn.obj: ipxconn.cpp $(GENERAL_H) $(NET_H) + +ipxgconn.obj: ipxgconn.cpp $(GENERAL_H) $(NET_H) + +ipxmgr.obj: ipxmgr.cpp $(GENERAL_H) $(NET_H) + +ipx95.obj: ipx95.cpp $(GENERAL_H) $(NET_H) + +jshell.obj: jshell.cpp $(GENERAL_H) $(MISC_H) + +keyfbuff.obj: keyfbuff.asm + +keyframe.obj: keyframe.cpp $(GENERAL_H) + +layer.obj: layer.cpp $(GENERAL_H) $(MISC_H) + +link.obj: link.cpp $(GENERAL_H) link.h + +list.obj: list.cpp $(GENERAL_H) $(GADGET_H) + +loaddlg.obj: loaddlg.cpp $(GENERAL_H) $(GADGET_H) + +logic.obj: logic.cpp $(GENERAL_H) $(MISC_H) + +map.obj: map.cpp $(GENERAL_H) $(MAP_H) + +mapsel.obj: mapsel.cpp $(GENERAL_H) + +mapeddlg.obj: mapeddlg.cpp $(GENERAL_H) $(MAP_H) $(OBJ_H) + +mapedit.obj: mapedit.cpp mapedsel.cpp $(GENERAL_H) $(MAP_H) $(OBJ_H) + +mapedplc.obj: mapedplc.cpp $(GENERAL_H) $(MAP_H) $(OBJ_H) + +mapedtm.obj: mapedtm.cpp $(GENERAL_H) $(MAP_H) $(OBJ_H) + +menus.obj: menus.cpp $(GENERAL_H) ccdde.h + +mission.obj: mission.cpp $(GENERAL_H) mission.h stage.h cargo.h object.h abstract.h + +mixfile.obj: mixfile.cpp $(GENERAL_H) $(FILE_H) + +monoc.obj: monoc.cpp $(GENERAL_H) + +mouse.obj: mouse.cpp $(GENERAL_H) $(MAP_H) + +mplayer.obj: mplayer.cpp tcpip.h $(GENERAL_H) + +msgbox.obj: msgbox.cpp $(GENERAL_H) $(GADGET_H) + +msglist.obj: msglist.cpp $(GENERAL_H) $(GADGET_H) + +netdlg.obj: netdlg.cpp $(GENERAL_H) $(GADGET_H) $(NET_H) + +noseqcon.obj: noseqcon.cpp $(GENERAL_H) noseqcon.h connect.h combuf.h + +nullconn.obj: nullconn.cpp $(GENERAL_H) nullconn.h noseqcon.h connect.h combuf.h tcpip.h + +nulldlg.obj: nulldlg.cpp $(GENERAL_H) nullmgr.h nullconn.h connmgr.h noseqcon.h connect.h combuf.h tcpip.h + +nullmgr.obj: nullmgr.cpp $(GENERAL_H) nullmgr.h nullconn.h connmgr.h noseqcon.h connect.h combuf.h tcpip.h + +object.obj: object.cpp $(GENERAL_H) object.h abstract.h + +odata.obj: odata.cpp $(GENERAL_H) type.h + +options.obj: options.cpp $(GENERAL_H) $(GADGET_H) + +overlay.obj: overlay.cpp $(GENERAL_H) overlay.h object.h + +power.obj: power.cpp $(GENERAL_H) $(MAP_H) + +profile.obj: profile.cpp $(GENERAL_H) + +queue.obj: queue.cpp $(GENERAL_H) $(MISC_H) tcpip.h + +rand.obj: rand.cpp $(GENERAL_H) + +radar.obj: radar.cpp $(GENERAL_H) $(MAP_H) + +radio.obj: radio.cpp $(GENERAL_H) $(TECHNO_H) + +reinf.obj: reinf.cpp $(GENERAL_H) $(MISC_H) $(TEAM_H) + +savedlg.obj: savedlg.cpp $(GENERAL_H) $(GADGET_H) + +saveload.obj: saveload.cpp $(GENERAL_H) $(MISC_H) + +scenario.obj: scenario.cpp $(GENERAL_H) $(MISC_H) + +score.obj: score.cpp $(GENERAL_H) + +scroll.obj: scroll.cpp $(GENERAL_H) $(MAP_H) + +sdata.obj: sdata.cpp $(GENERAL_H) type.h + +shapebtn.obj: shapebtn.cpp $(GENERAL_H) $(GADGET_H) + +sidebar.obj: sidebar.cpp $(GENERAL_H) $(MAP_H) + +slider.obj: slider.cpp $(GENERAL_H) $(GADGET_H) + +smudge.obj: smudge.cpp $(GENERAL_H) smudge.h object.h + +sounddlg.obj: sounddlg.cpp $(GENERAL_H) $(GADGET_H) sounddlg.h + +special.obj: special.cpp $(GENERAL_H) $(GADGET_H) special.h + +startup.obj: startup.cpp $(GENERAL_H) ccdde.h + +stuff.obj: stuff.cpp $(GENERAL_H) $(MISC_H) + +support.obj: support.asm + +super.obj: super.cpp $(GENERAL_H) $(MISC_H) + +tab.obj: tab.cpp $(GENERAL_H) $(MAP_H) + +tarcom.obj: tarcom.cpp $(GENERAL_H) $(UNIT_H) + +target.obj: target.cpp $(GENERAL_H) target.h + +tcpip.obj: tcpip.cpp $(GENERAL_H) tcpip.h + +tdata.obj: tdata.cpp $(GENERAL_H) type.h + +team.obj: team.cpp $(GENERAL_H) $(TEAM_H) + +teamtype.obj: teamtype.cpp $(GENERAL_H) $(TEAM_H) + +techno.obj: techno.cpp $(GENERAL_H) $(TECHNO_H) + +template.obj: template.cpp $(GENERAL_H) template.h object.h + +terrain.obj: terrain.cpp $(GENERAL_H) terrain.h stage.h object.h + +textbtn.obj: textbtn.cpp $(GENERAL_H) $(GADGET_H) + +theme.obj: theme.cpp $(GENERAL_H) theme.h + +toggle.obj: toggle.cpp $(GENERAL_H) $(GADGET_H) + +trigger.obj: trigger.cpp $(GENERAL_H) $(TEAM_H) + +turret.obj: turret.cpp $(GENERAL_H) $(UNIT_H) + +txtlabel.obj: txtlabel.cpp $(GENERAL_H) $(GADGET_H) + +txtprnt.obj: txtprnt.asm + +udata.obj: udata.cpp $(GENERAL_H) type.h + +unit.obj: unit.cpp $(GENERAL_H) $(UNIT_H) + +visudlg.obj: visudlg.cpp $(GENERAL_H) $(GADGET_H) + +utracker.obj: utracker.cpp utracker.h + +packet.obj: packet.cpp packet.h field.h + +field.obj: field.cpp field.h + +stats.obj: stats.cpp $(GENERAL_H) packet.h field.h ccdde.h + +ccdde.obj: ccdde.cpp ccdde.h dde.h + +dde.obj: dde.cpp dde.h + + + +vector.obj: vector.cpp $(GENERAL_H) $(MISC_H) + *$(WATCOM)\binnt\wpp386 $(VCT_CFG) -foobj\vector.obj vector.cpp + + +#**************************** End of makefile ****************************** diff --git a/MAKEFILE.BAK b/MAKEFILE.BAK new file mode 100644 index 0000000..501575d --- /dev/null +++ b/MAKEFILE.BAK @@ -0,0 +1,861 @@ +# +# Command & Conquer(tm) +# Copyright 2025 Electronic Arts Inc. +# +# 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 3 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +#* $Header$ +#*********************************************************************************************** +#*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** +#*********************************************************************************************** +#* * +#* Project Name : Command & Conquer * +#* * +#* File Name : MAKEFILE * +#* * +#* Programmer : Joe L. Bostic * +#* * +#* Start Date : 03/02/95 * +#* * +#* Last Update : March 2, 1995 [JLB] * +#* * +#*---------------------------------------------------------------------------------------------* +#* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +.SILENT +.OPTIMIZE +.ERASE + +#--------------------------------------------------------------------------- +# Verify user's environment +#--------------------------------------------------------------------------- +!ifndef %WWFLAT +WWFLAT=c:\wwflat32 +#!error WWFLAT must be set to the root of the library directory. +!else +WWFLAT=$(%WWFLAT) +!endif + +!ifndef %WATCOM +WATCOM=c:\projects\c&c\code\watcom +#!error WATCOM must be set to the Watcom root directory. +!else +WATCOM=$(%WATCOM) +!endif + +#--------------------------------------------------------------------------- +# Path macros: one path for each file type. +# These paths are used to tell make where to find/put each file type. +#--------------------------------------------------------------------------- +.asm: . +.c: . +.cpp: . +.h: . +#.obj: obj +.obj: r: +.lib: $(WWFLAT)\lib +.exe: ..\run + + +#=========================================================================== +# Flags for the different project assembly tools. +#=========================================================================== +CC_CFG = /i=..\vq\include # Includes player (VQ) directory. +CC_CFG += /i=$(WWFLAT)\INCLUDE # Normal library include directory. +CC_CFG += /i=watcom\H # Normal Watcom include directory. +CC_CFG += /i=..\gcl510\H # Includes Greenleaf headers. +CC_CFG += /d2 # Debugging information. +CC_CFG += /of+ # Generate tracable stack frames. +CC_CFG += /zp1 # Pack structures on byte boundary. +CC_CFG += /5s # Pentium optimized stack calling conventions. +CC_CFG += /xs # Exception handling enabled. +CC_CFG += /s # Remove stack check calls. +CC_CFG += /j # char is now signed. +CC_CFG += /fh=conquer.pch # Use precompiled headers. +CC_CFG += /we # Treat all warnings as errors. +CC_CFG += /w8 # Most warnings enabled. +CC_CFG += /ri # char and shorts are returned as int. +CC_CFG += /zq # Operate quietly. +CC_CFG += /zm # Each routine to be in its own segment. +CC_CFG += /zld # Disable autodependency information in object file. +CC_CFG += /od # *** Disable all optimizations *** +CC_CFG += /DDOS4G # Must be defined for Greenleaf +CC_CFG += /DGF_WATCOM_S # Must be defined for Greenleaf with /3s +CC_CFG += /ol # Loop optimizations enabled. +CC_CFG += /or # Reorder instructions for best pipeline usage. +CC_CFG += /oe # Inline is enabled. +CC_CFG += /oi # Expand intrisic functions inline. +CC_CFG += /on # Allow numerically unstable operations. +CC_CFG += /oo # Compile even if low on memory (i.e. less than 64meg). +CC_CFG += /oa # Relax aliasing constraints. +#CC_CFG += /ot # Speed is more important than space optimizations. +#CC_CFG += /fi=watcom.h # Special Watcom control file. + +ASM_CFG = /i$(WWFLAT)\INCLUDE # Include directory. +ASM_CFG += /zd # Debugging information line numbers. +ASM_CFG += /t # Quiet operation. +ASM_CFG += /m # Allow multiple passes. +ASM_CFG += /w+ # Enable maximum warnings. +ASM_CFG += /jJUMPS # Enable jump optimizations. +ASM_CFG += /ml # Case sensitivity on code. +#ASM_CFG += /zi # Full debugging information. + + +#--------------------------------------------------------------------------- +# Implicit rules +# Compiler: +# ($< = full dependent with path) +# Assembler: +# output obj's are constructed from .obj: & the $& macro +# ($< = full dependent with path) +# tasm's cfg file is not invoked as a response file. +#--------------------------------------------------------------------------- + +.c.obj:#.AUTODEPEND + echo Compiling $< + *watcom\wcc $(C_CFG) -foobj\$^. $< + +.cpp.obj: #.AUTODEPEND + echo Compiling $< +!ifdef %NETWORK + f:\projects\c&c\slaves\NETEXEC /v $(%NETWHO)c $^& + if exist $(%CCNETDIR)\code\netmake.err %abort +!else + *watcom\wpp386 $(CC_CFG) -foobj\$^. $< +!endif + +.asm.obj: + echo Assembling $< +!ifdef %NETWORK + f:\projects\c&c\slaves\NETEXEC /v $(%NETWHO)a $^& +!else + watcom\tasm32 $(ASM_CFG) $<, obj\$^. +!endif + +#--------------------------------------------------------------------------- +# Project-dependent variables +#--------------------------------------------------------------------------- +OBJECTS = & + AADATA.OBJ & + ABSTRACT.OBJ & + ADATA.OBJ & + AIRCRAFT.OBJ & + ANIM.OBJ & + AUDIO.OBJ & + BASE.OBJ & + BBDATA.OBJ & + BDATA.OBJ & + BUILDING.OBJ & + BULLET.OBJ & + CARGO.OBJ & + CCFILE.OBJ & + CDATA.OBJ & + CDFILE.OBJ & + CELL.OBJ & + CHEKLIST.OBJ & + COLRLIST.OBJ & + COMBAT.OBJ & + COMBUF.OBJ & + CONFDLG.OBJ & + CONNECT.OBJ & + CONQUER.OBJ & + CONST.OBJ & + CONTROL.OBJ & + COORD.OBJ & + COORDA.OBJ & + CREDITS.OBJ & + CREW.OBJ & + DEBUG.OBJ & + DESCDLG.OBJ & + DIAL8.OBJ & + DIALOG.OBJ & + DISPLAY.OBJ & + DPMI.OBJ & + DRIVE.OBJ & + EDIT.OBJ & + EVENT.OBJ & + FACING.OBJ & + FACTORY.OBJ & + FINDPATH.OBJ & + FLASHER.OBJ & + FLY.OBJ & + FOOT.OBJ & + FUSE.OBJ & + GADGET.OBJ & + GAMEDLG.OBJ & + GAUGE.OBJ & + GLOBALS.OBJ & + GOPTIONS.OBJ & + GSCREEN.OBJ & + HDATA.OBJ & + HEAP.OBJ & + HELP.OBJ & + HOUSE.OBJ & + IDATA.OBJ & + INFANTRY.OBJ & + INI.OBJ & + INIT.OBJ & + INTRO.OBJ & + IOMAP.OBJ & + IOOBJ.OBJ & + IPX.OBJ & + IPXADDR.OBJ & + IPXCONN.OBJ & + IPXGCONN.OBJ & + IPXMGR.OBJ & + IPXPROT.OBJ & + JSHELL.OBJ & + KEYFBUFF.OBJ & + KEYFRAME.OBJ & + LAYER.OBJ & + LINK.OBJ & + LIST.OBJ & + LOADDLG.OBJ & + LOGIC.OBJ & + MAP.OBJ & + MAPEDDLG.OBJ & + MAPEDIT.OBJ & + MAPEDPLC.OBJ & + MAPEDSEL.OBJ & + MAPEDTM.OBJ & + MAPSEL.OBJ & + MENUS.OBJ & + MISSION.OBJ & + MIXFILE.OBJ & + MONOC.OBJ & + MOUSE.OBJ & + MPLAYER.OBJ & + MSGBOX.OBJ & + MSGLIST.OBJ & + NETDLG.OBJ & + NOSEQCON.OBJ & + NULLCONN.OBJ & + NULLDLG.OBJ & + NULLMGR.OBJ & + OBJECT.OBJ & + ODATA.OBJ & + OPTIONS.OBJ & + OVERLAY.OBJ & + POWER.OBJ & + PROFILE.OBJ & + QUEUE.OBJ & + RADAR.OBJ & + RADIO.OBJ & + RAND.OBJ & + RAWFILE.OBJ & + REINF.OBJ & + SAVELOAD.OBJ & + SCENARIO.OBJ & + SCORE.OBJ & + SCROLL.OBJ & + SDATA.OBJ & + SHAPEBTN.OBJ & + SIDEBAR.OBJ & + SLIDER.OBJ & + SMUDGE.OBJ & + SOUNDDLG.OBJ & + STARTUP.OBJ & + SUPPORT.OBJ & + TAB.OBJ & + TARCOM.OBJ & + TARGET.OBJ & + TDATA.OBJ & + TEAM.OBJ & + TEAMTYPE.OBJ & + TECHNO.OBJ & + TEMPLATE.OBJ & + TERRAIN.OBJ & + TEXTBTN.OBJ & + THEME.OBJ & + TOGGLE.OBJ & + TRIGGER.OBJ & + TURRET.OBJ & + TXTLABEL.OBJ & + TXTPRNT.OBJ & + UDATA.OBJ & + UNIT.OBJ & + VECTOR.OBJ & + VISUDLG.OBJ + +PROJ_LIBS = & + wwflat32.lib + +VQ_LIBS = & + vqa32wp.lib & + vqm32wp.lib + +GCL_LIBS = & + gclfr3s.lib + + +############################################################################ +# Pre-compilation process. Move old files to backup directory and switch +# to monochrome screen. +.BEFORE + mode mono + -if exist *.pch del *.pch + -if exist $(%CCNETDIR)\code\netmake.err del $(%CCNETDIR)\code\netmake.err + -if exist *.bak move *.bak bak + +# Switch back to normal screen at compilation end. +.AFTER + mode co80 + +# Switch back to normal screen if there was an error. +.ERROR + mode co80 + + +############################################################################# +# Default target +all: conquer.exe + + +############################################################################# +# Builds the stub replacement program. +CWSTUB.OBJ: CWSTUB.C + *watcom\wcc /i=watcom\h /dQUIET /dVMM /ms /zQ -foobj\$^. $< + +CWSTUB.EXE: CWSTUB.OBJ + *watcom\wlink system dos file obj\cwstub.obj name cwstub.exe option quiet library watcom\lib286\dos\clibs.lib, watcom\lib286\math87s.lib, watcom\lib286\dos\emu87.lib + + +############################################################################# +# Build the EXE +conquer.exe: $(OBJECTS) cwstub.exe conquer.lnk $(WWFLAT)\lib\wwflat32.lib + + Echo "conquer.exe" linking phase. +!ifdef %NETWORK + echo Waiting for objects... + for %index in ($(OBJECTS)) do WAITFILE $(%CCNETDIR)\code\obj\%index +# copy $(%CCNETDIR)\code\obj\*.obj $(%CCLOCALDIR)\code\obj /U + copy $(%CCNETDIR)\code\obj\*.obj r: /U +!endif + watcom\wlink name ..\run\$@ @conquer.lnk + Echo "conquer.exe" executable completed. +!ifdef %WWOBJ + xcopy /M $(WWOBJ)obj\*.obj obj +!endif +# watcom\wlink $(LINK_CFG) name ..\run\$@ @conquer.lnk + +############################################################################# +# This creates the linker command file. +conquer.lnk : makefile + %create $^@ + %append $^@ system dos4g + %append $^@ debug all + %append $^@ option stack=8k + %append $^@ option redefsok + %append $^@ option quiet + %append $^@ option map + %append $^@ option cache + %append $^@ option eliminate + %append $^@ option caseexact +# %append $^@ option stub=cwstub.exe +# for %index in ($(OBJECTS)) do %append $^@ file obj\%index + for %index in ($(OBJECTS)) do %append $^@ file r:\%index + for %index in ($(PROJ_LIBS)) do %append $^@ library $(WWFLAT)\lib\%index + for %index in ($(VQ_LIBS)) do %append $^@ library ..\vq\lib\%index + for %index in ($(GCL_LIBS)) do %append $^@ library ..\gcl510\w10\%index + +bind: .SYMBOLIC + -copy ..\run\conquer.exe ..\run\temp.exe + -wstrip ..\run\temp.exe + -watcom\4gwbind watcom\4gwpro.exe ..\run\temp.exe ..\run\c&c.exe -f + -del ..\run\temp.exe +# -wstrip ..\run\install.exe +# -move ..\run\install.exe ..\run\temp.exe +# -watcom\4gwbind watcom\4gwpro.exe ..\run\temp.exe ..\run\install.exe -f +# -del ..\run\temp.exe + +############################################################# +# Update source and art to network. +update: bind .SYMBOLIC + -del *.pch + -del *.tr + -del *.td + -del *.bak + -del *.rst + -del ..\run\*.swp + -attrib -r f:\projects\c&c\cd\*.* /s + -copy ..\cd\*.* f:\projects\c&c\cd /v /u /s + -deltree /Y f:\projects\c&c\cd\install + -md f:\projects\c&c\cd\install + -copy ..\run\*.* f:\projects\c&c\cd\install /v /u + -del f:\projects\c&c\cd\install\*.cfg + -del f:\projects\c&c\cd\install\*.dat + -del f:\projects\c&c\cd\install\*.doc + -del f:\projects\c&c\cd\install\*.ini + -del f:\projects\c&c\cd\install\*.bin + -del f:\projects\c&c\cd\install\*.lbm + -del f:\projects\c&c\cd\install\*.out + -del f:\projects\c&c\cd\install\*.sym + -del f:\projects\c&c\cd\install\*.log + -del f:\projects\c&c\cd\install\*.txt + -del f:\projects\c&c\cd\install\*.bak + -del f:\projects\c&c\cd\install\*.rst + -del f:\projects\c&c\cd\install\savegame.* + -move f:\projects\c&c\cd\install\conquer.exe f:\projects\c&c\playtest + -attrib +r f:\projects\c&c\cd\*.* /s + -copy watcom\dos4gw.exe f:\projects\c&c\playtest + -deltree /Y f:\projects\c&c\art\ingame + -md f:\projects\c&c\art\ingame + -xcopy ..\art\ingame\*.* f:\projects\c&c\art\ingame /e /v /s + -del /Y f:\projects\c&c\code\*.* + -copy *.* f:\projects\c&c\code /v /s /u + + +############################################################################# +# Explicit rules to build the master zip files (used by Codewrite merge). +BILL_R.ZIP: pkzip.dat .SYMBOLIC + -del f:\projects\c&c\bill_r.zip + -pkzip -p -u -x@pkzip.dat f:\projects\c&c\bill_r.zip *.* eng\*.* + +MARIA_L.ZIP: pkzip.dat .SYMBOLIC + -del f:\projects\c&c\maria_l.zip + -pkzip -p -u -x@pkzip.dat f:\projects\c&c\maria_l.zip *.* eng\*.* + +BARRY_G.ZIP: pkzip.dat .SYMBOLIC + -del f:\projects\c&c\barry_g.zip + -pkzip -p -u -x@pkzip.dat f:\projects\c&c\barry_g.zip *.* eng\*.* + +PHIL_G.ZIP: pkzip.dat .SYMBOLIC + -del f:\projects\c&c\phil_g.zip + -pkzip -p -u -x@pkzip.dat f:\projects\c&c\phil_g.zip *.* eng\*.* + +DAVID_D.ZIP: pkzip.dat .SYMBOLIC + -del f:\projects\c&c\david_d.zip + -pkzip -p -u -x@pkzip.dat f:\projects\c&c\david_d.zip *.* eng\*.* + +BILL_P.ZIP: pkzip.dat .SYMBOLIC + -del f:\projects\c&c\bill_p.zip + -pkzip -p -u -x@pkzip.dat f:\projects\c&c\bill_p.zip *.* eng\*.* + +# Special "mega-zip" process. +JOE_B.ZIP: pkzip.dat .SYMBOLIC + -pkzip -rp -u -xcps\*.* -x@pkzip.dat f:\projects\c&c\joe_b.zip + + +############################################################################# +# Rebuilds the master zip control file. This is used by the zip process. +PKZIP.DAT: makefile .SYMBOLIC + %create $^@ + %append $^@ *.000 + %append $^@ *.@@@ + %append $^@ *.bak + %append $^@ *.bat + %append $^@ *.cfg + %append $^@ *.dat + %append $^@ *.def + %append $^@ *.doc + %append $^@ *.dsw + %append $^@ *.err + %append $^@ *.ewp + %append $^@ *.ext + %append $^@ *.i + %append $^@ *.ide + %append $^@ *.lnk + %append $^@ *.log + %append $^@ *.lst + %append $^@ *.mac + %append $^@ *.map + %append $^@ *.mk + %append $^@ *.mk1 + %append $^@ *.obj + %append $^@ *.out + %append $^@ *.pch + %append $^@ *.pfs + %append $^@ *.pif + %append $^@ *.pjt + %append $^@ *.prf + %append $^@ *.pro + %append $^@ *.ptg + %append $^@ *.rc + %append $^@ *.rep + %append $^@ *.rpt + %append $^@ *.rst + %append $^@ *.sym + %append $^@ *.tag + %append $^@ *.td + %append $^@ *.td + %append $^@ *.tgt + %append $^@ *.tmp + %append $^@ *.tr + %append $^@ *.tr + %append $^@ *.vec + %append $^@ *.wpj + %append $^@ *.zip + %append $^@ state.rst + + +#--------------------------------------------------------------------------- +# Dependency macros (makes defining dependencies easier) +#--------------------------------------------------------------------------- +GENERAL_H = defines.h function.h externs.h conquer.h vector.h heap.h & + debug.h jshell.h compat.h + +TECHNO_H = facing.h techno.h mission.h stage.h cargo.h object.h abstract.h + +UNIT_H = unit.h tarcom.h turret.h drive.h foot.h radio.h $(TECHNO_H) + +INFANTRY_H = infantry.h foot.h radio.h $(TECHNO_H) + +AIRCRAFT_H = aircraft.h fly.h radio.h $(TECHNO_H) + +BUILDING_H = building.h radio.h $(TECHNO_H) + +BULLET_H = bullet.h fly.h fuse.h object.h abstract.h + +OBJ_H = $(UNIT_H) $(INFANTRY_H) $(AIRCRAFT_H) $(BUILDING_H) $(BULLET_H) + +MAP_H = base.h mapedit.h mouse.h scroll.h help.h tab.h power.h sidebar.h & + radar.h display.h map.h gscreen.h cell.h + +GADGET_H = textbtn.h shapebtn.h slider.h gauge.h dial8.h edit.h & + toggle.h list.h cheklist.h control.h gadget.h link.h + +FILE_H = ccfile.h cdfile.h mixfile.h rawfile.h wwfile.h link.h + +TEAM_H = team.h teamtype.h trigger.h + +IPX_H = ipx.h ipxaddr.h + +NET_H = combuf.h connect.h connmgr.h ipx.h ipxaddr.h ipxconn.h ipxgconn.h & + ipxmgr.h noseqcon.h nullconn.h nullmgr.h + +MISC_H = ftimer.h logic.h score.h theme.h event.h queue.h special.h + +#--------------------------------------------------------------------------- +# Dependencies (This is not totally accurate; if you're not sure, rebuild +# everything!) +#--------------------------------------------------------------------------- +aadata.obj: aadata.cpp $(GENERAL_H) type.h + +abstract.obj: abstract.cpp $(GENERAL_H) abstract.h + +adata.obj: adata.cpp $(GENERAL_H) type.h + +aircraft.obj: aircraft.cpp $(GENERAL_H) $(AIRCRAFT_H) + +anim.obj: anim.cpp $(GENERAL_H) anim.h stage.h object.h + +audio.obj: audio.cpp $(GENERAL_H) audio.h + +base.obj: base.cpp $(GENERAL_H) type.h + +bbdata.obj: bbdata.cpp $(GENERAL_H) type.h + +bdata.obj: bdata.cpp $(GENERAL_H) type.h + +building.obj: building.cpp $(GENERAL_H) $(BUILDING_H) + +bullet.obj: bullet.cpp $(GENERAL_H) $(BULLET_H) + +cargo.obj: cargo.cpp $(GENERAL_H) $(TECHNO_H) cargo.h + +ccfile.obj: ccfile.cpp $(GENERAL_H) $(FILE_H) + +cdata.obj: cdata.cpp $(GENERAL_H) type.h + +cdfile.obj: cdfile.cpp $(GENERAL_H) $(FILE_H) + +cell.obj: cell.cpp $(GENERAL_H) $(MAP_H) + +cheklist.obj: cheklist.cpp $(GENERAL_H) $(GADGET_H) + +colrlist.obj: colrlist.cpp $(GENERAL_H) $(GADGET_H) + +combat.obj: combat.cpp $(GENERAL_H) + +combuf.obj: combuf.cpp $(GENERAL_H) combuf.h + +confdlg.obj: confdlg.cpp $(GENERAL_H) $(GADGET_H) + +connect.obj: connect.cpp $(GENERAL_H) connect.h combuf.h + +conquer.obj: conquer.cpp $(GENERAL_H) $(MISC_H) $(OBJ_H) + +const.obj: const.cpp $(GENERAL_H) + +control.obj: control.cpp $(GENERAL_H) $(GADGET_H) + +coord.obj: coord.cpp $(GENERAL_H) + +coorda.obj: coorda.asm + +credits.obj: credits.cpp $(GENERAL_H) credits.h + +crew.obj: crew.cpp $(GENERAL_H) crew.h + +debug.obj: debug.cpp $(GENERAL_H) debug.h + +deldlg.obj: deldlg.cpp $(GENERAL_H) $(GADGET_H) + +descdlg.obj: descdlg.cpp $(GENERAL_H) $(GADGET_H) + +dial8.obj: dial8.cpp $(GENERAL_H) $(GADGET_H) + +dialog.obj: dialog.cpp $(GENERAL_H) + +display.obj: display.cpp $(GENERAL_H) $(MAP_H) + +dpmi.obj: dpmi.cpp $(GENERAL_H) + +drive.obj: drive.cpp $(GENERAL_H) $(TECHNO_H) + +edit.obj: edit.cpp $(GENERAL_H) $(GADGET_H) + +event.obj: event.cpp $(GENERAL_H) $(MISC_H) + +facing.obj: facing.cpp $(GENERAL_H) facing.h + +factory.obj: factory.cpp $(GENERAL_H) factory.h + +findpath.obj: findpath.cpp $(GENERAL_H) + +flasher.obj: flasher.cpp $(GENERAL_H) flasher.h + +fly.obj: fly.cpp $(GENERAL_H) fly.h + +foot.obj: foot.cpp $(GENERAL_H) $(INFANTRY_H) + +fuse.obj: fuse.cpp $(GENERAL_H) fuse.h + +gadget.obj: gadget.cpp $(GENERAL_H) $(GADGET_H) + +gamedlg.obj: gamedlg.cpp $(GENERAL_H) $(GADGET_H) + +gauge.obj: gauge.cpp $(GENERAL_H) $(GADGET_H) + +globals.obj: globals.cpp $(GENERAL_H) + +goptions.obj: goptions.cpp $(GENERAL_H) $(GADGET_H) + +gscreen.obj: gscreen.cpp $(GENERAL_H) $(MAP_H) + +hdata.obj: hdata.cpp $(GENERAL_H) type.h + +heap.obj: heap.cpp $(GENERAL_H) $(MISC_H) + +help.obj: help.cpp $(GENERAL_H) $(MAP_H) + +house.obj: house.cpp $(GENERAL_H) house.h + +idata.obj: idata.cpp $(GENERAL_H) type.h + +infantry.obj: infantry.cpp $(GENERAL_H) $(INFANTRY_H) + +ini.obj: ini.cpp $(GENERAL_H) $(MISC_H) + +init.obj: init.cpp $(GENERAL_H) $(MISC_H) $(OBJ_H) + +iomap.obj: iomap.cpp $(GENERAL_H) $(MAP_H) $(FILE_H) + +ioobj.obj: ioobj.cpp $(GENERAL_H) $(FILE_H) $(OBJ_H) + +ipx.obj: ipx.cpp $(GENERAL_H) $(IPX_H) + +ipxaddr.obj: ipxaddr.cpp $(GENERAL_H) $(IPX_H) + +ipxconn.obj: ipxconn.cpp $(GENERAL_H) $(NET_H) + +ipxgconn.obj: ipxgconn.cpp $(GENERAL_H) $(NET_H) + +ipxmgr.obj: ipxmgr.cpp $(GENERAL_H) $(NET_H) + +ipxreal.ibn: + +jshell.obj: jshell.cpp $(GENERAL_H) $(MISC_H) + +keyfbuff.obj: keyfbuff.asm + +keyframe.obj: keyframe.cpp $(GENERAL_H) + +layer.obj: layer.cpp $(GENERAL_H) $(MISC_H) + +link.obj: link.cpp $(GENERAL_H) link.h + +list.obj: list.cpp $(GENERAL_H) $(GADGET_H) + +loaddlg.obj: loaddlg.cpp $(GENERAL_H) $(GADGET_H) + +logic.obj: logic.cpp $(GENERAL_H) $(MISC_H) + +map.obj: map.cpp $(GENERAL_H) $(MAP_H) + +mapsel.obj: mapsel.cpp $(GENERAL_H) + +mapeddlg.obj: mapeddlg.cpp $(GENERAL_H) $(MAP_H) $(OBJ_H) + +mapedit.obj: mapedit.cpp $(GENERAL_H) $(MAP_H) $(OBJ_H) + +mapedplc.obj: mapedplc.cpp $(GENERAL_H) $(MAP_H) $(OBJ_H) + +mapedsel.obj: mapedsel.cpp $(GENERAL_H) $(MAP_H) $(OBJ_H) + +mapedtm.obj: mapedtm.cpp $(GENERAL_H) $(MAP_H) $(OBJ_H) + +menus.obj: menus.cpp $(GENERAL_H) + +mission.obj: mission.cpp $(GENERAL_H) mission.h stage.h cargo.h object.h abstract.h + +mixfile.obj: mixfile.cpp $(GENERAL_H) $(FILE_H) + +monoc.obj: monoc.cpp $(GENERAL_H) + +mouse.obj: mouse.cpp $(GENERAL_H) $(MAP_H) + +mplayer.obj: mplayer.cpp $(GENERAL_H) + +msgbox.obj: msgbox.cpp $(GENERAL_H) $(GADGET_H) + +msglist.obj: msglist.cpp $(GENERAL_H) $(GADGET_H) + +netdlg.obj: netdlg.cpp $(GENERAL_H) $(GADGET_H) $(NET_H) + +noseqcon.obj: noseqcon.cpp $(GENERAL_H) noseqcon.h connect.h combuf.h + +nullconn.obj: nullconn.cpp $(GENERAL_H) nullconn.h noseqcon.h connect.h combuf.h + +nulldlg.obj: nulldlg.cpp $(GENERAL_H) nullmgr.h nullconn.h connmgr.h noseqcon.h connect.h combuf.h + +nullmgr.obj: nullmgr.cpp $(GENERAL_H) nullmgr.h nullconn.h connmgr.h noseqcon.h connect.h combuf.h + +object.obj: object.cpp $(GENERAL_H) object.h abstract.h + +odata.obj: odata.cpp $(GENERAL_H) type.h + +options.obj: options.cpp $(GENERAL_H) $(GADGET_H) + +overlay.obj: overlay.cpp $(GENERAL_H) overlay.h object.h + +power.obj: power.cpp $(GENERAL_H) $(MAP_H) + +profile.obj: profile.cpp $(GENERAL_H) + +queue.obj: queue.cpp $(GENERAL_H) $(MISC_H) + +rand.obj: rand.cpp $(GENERAL_H) + +radar.obj: radar.cpp $(GENERAL_H) $(MAP_H) + +radio.obj: radio.cpp $(GENERAL_H) $(TECHNO_H) + +rawfile.obj: rawfile.cpp $(GENERAL_H) $(FILE_H) + +reinf.obj: reinf.cpp $(GENERAL_H) $(MISC_H) + +savedlg.obj: savedlg.cpp $(GENERAL_H) $(GADGET_H) + +saveload.obj: saveload.cpp $(GENERAL_H) $(MISC_H) + +scenario.obj: scenario.cpp $(GENERAL_H) $(MISC_H) + +score.obj: score.cpp $(GENERAL_H) + +scroll.obj: scroll.cpp $(GENERAL_H) $(MAP_H) + +sdata.obj: sdata.cpp $(GENERAL_H) type.h + +shapebtn.obj: shapebtn.cpp $(GENERAL_H) $(GADGET_H) + +sidebar.obj: sidebar.cpp $(GENERAL_H) $(MAP_H) + +slider.obj: slider.cpp $(GENERAL_H) $(GADGET_H) + +smudge.obj: smudge.cpp $(GENERAL_H) smudge.h object.h + +sounddlg.obj: sounddlg.cpp $(GENERAL_H) $(GADGET_H) sounddlg.h + +startup.obj: startup.cpp $(GENERAL_H) + +stuff.obj: stuff.cpp $(GENERAL_H) $(MISC_H) + +support.obj: support.asm + +tab.obj: tab.cpp $(GENERAL_H) $(MAP_H) + +tarcom.obj: tarcom.cpp $(GENERAL_H) $(UNIT_H) + +target.obj: target.cpp $(GENERAL_H) target.h + +tdata.obj: tdata.cpp $(GENERAL_H) type.h + +team.obj: team.cpp $(GENERAL_H) $(TEAM_H) + +teamtype.obj: teamtype.cpp $(GENERAL_H) $(TEAM_H) + +techno.obj: techno.cpp $(GENERAL_H) $(TECHNO_H) + +template.obj: template.cpp $(GENERAL_H) template.h object.h + +terrain.obj: terrain.cpp $(GENERAL_H) terrain.h stage.h object.h + +textbtn.obj: textbtn.cpp $(GENERAL_H) $(GADGET_H) + +theme.obj: theme.cpp $(GENERAL_H) theme.h + +toggle.obj: toggle.cpp $(GENERAL_H) $(GADGET_H) + +trigger.obj: trigger.cpp $(GENERAL_H) $(TEAM_H) + +turret.obj: turret.cpp $(GENERAL_H) $(UNIT_H) + +txtlabel.obj: txtlabel.cpp $(GENERAL_H) $(GADGET_H) + +txtprnt.obj: txtprnt.asm + +udata.obj: udata.cpp $(GENERAL_H) type.h + +unit.obj: unit.cpp $(GENERAL_H) $(UNIT_H) + +vector.obj: vector.cpp $(GENERAL_H) $(MISC_H) + +visudlg.obj: visudlg.cpp $(GENERAL_H) $(GADGET_H) + +#-------------------------------------------------------------------------- +# The IPX assembly object files are created in a special way: +# IPXREAL is the real-mode code that gets stuffed into memory by protected- +# mode code. It's assembled, then converted into a big header file by +# the 'EBN' utility. +# IPXPROT is the protected-mode code that includes IPXREAL.IBN, and +# provides routines to let C++ read the code's address & size. +#-------------------------------------------------------------------------- +#obj\ipxreal.ibn: obj\ipxreal.obj +# %create $^*.rsp +# %append $^*.rsp obj\$^&.obj +# %append $^*.rsp obj\$^&.exe +# %append $^*.rsp obj\$^&.map +# tlink @$^*.rsp +# tdstrip obj\ipxreal.exe +# utils\ebn obj\ipxreal.exe + +r:\ipxreal.ibn: r:\ipxreal.obj + %create $^*.rsp + %append $^*.rsp r:\$^&.obj + %append $^*.rsp r:\$^&.exe + %append $^*.rsp r:\$^&.map + tlink @$^*.rsp + tdstrip r:\ipxreal.exe + utils\ebn r:\ipxreal.exe + +ipxreal.obj: ipxreal.asm + tasm /zn /la /ml /m2 ipxreal.asm, r:\ipxreal.obj +# tasm /zn /la /ml /m2 ipxreal.asm, obj\ipxreal.obj + +#ipxprot.obj: obj\ipxreal.ibn ipxprot.asm +ipxprot.obj: r:\ipxreal.ibn ipxprot.asm + +#**************************** End of makefile ****************************** diff --git a/MAP.CPP b/MAP.CPP new file mode 100644 index 0000000..8eaf100 --- /dev/null +++ b/MAP.CPP @@ -0,0 +1,1183 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\map.cpv 2.17 16 Oct 1995 16:52:22 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : August 20, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MapClass::Cell_Distance -- Determines the distance between two cells. * + * MapClass::Cell_Region -- Determines the region from a specified cell number. * + * MapClass::Cell_Threat -- Gets a houses threat value for a cell * + * MapClass::Close_Object -- Finds a clickable close object to the specified coordinate. * + * MapClass::In_Radar -- Is specified cell in the radar map? * + * MapClass::Init -- clears all cells * + * MapClass::Logic -- Handles map related logic functions. * + * MapClass::One_Time -- Performs special one time initializations for the map. * + * MapClass::Overlap_Down -- computes & marks object's overlap cells * + * MapClass::Overlap_Up -- Computes & clears object's overlap cells * + * MapClass::Overpass -- Performs any final cleanup to a freshly constructed map. * + * MapClass::Pick_Up -- Removes specified object from the map. * + * MapClass::Place_Down -- Places the specified object onto the map. * + * MapClass::Place_Random_Crate -- Places a crate at random location on map. * + * MapClass::Read_Binary -- reads the map's binary image file * + * MapClass::Set_Map_Dimensions -- Initialize the map. * + * MapClass::Sight_From -- Mark as visible the cells within a specified radius. * + * MapClass::Validate -- validates every cell on the map * + * MapClass::Write_Binary -- writes the map's binary image file * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#define MCW MAP_CELL_W +int const MapClass::RadiusOffset[] = { + /* 0 */ 0, + /* 1 */ (-MCW*1)-1,(-MCW*1)+0,(-MCW*1)+1,-1,1,(MCW*1)-1,(MCW*1)+0,(MCW*1)+1, + /* 2 */ (-MCW*2)-1,(-MCW*2)+0,(-MCW*2)+1,(-MCW*1)-2,(-MCW*1)+2,-2,2,(MCW*1)-2,(MCW*1)+2,(MCW*2)-1,(MCW*2)+0,(MCW*2)+1, + /* 3 */ (-MCW*3)-1,(-MCW*3)+0,(-MCW*3)+1,(-MCW*2)-2,(-MCW*2)+2,(-MCW*1)-3,(-MCW*1)+3,-3,3,(MCW*1)-3,(MCW*1)+3,(MCW*2)-2,(MCW*2)+2,(MCW*3)-1,(MCW*3)+0,(MCW*3)+1, + /* 4 */ (-MCW*4)-1,(-MCW*4)+0,(-MCW*4)+1,(-MCW*3)-3,(-MCW*3)-2,(-MCW*3)+2,(-MCW*3)+3,(-MCW*2)-3,(-MCW*2)+3,(-MCW*1)-4,(-MCW*1)+4,-4,4,(MCW*1)-4,(MCW*1)+4,(MCW*2)-3,(MCW*2)+3,(MCW*3)-3,(MCW*3)-2,(MCW*3)+2,(MCW*3)+3,(MCW*4)-1,(MCW*4)+0,(MCW*4)+1, + /* 5 */ (-MCW*5)-1,(-MCW*5)+0,(-MCW*5)+1,(-MCW*4)-3,(-MCW*4)-2,(-MCW*4)+2,(-MCW*4)+3,(-MCW*3)-4,(-MCW*3)+4,(-MCW*2)-4,(-MCW*2)+4,(-MCW*1)-5,(-MCW*1)+5,-5,5,(MCW*1)-5,(MCW*1)+5,(MCW*2)-4,(MCW*2)+4,(MCW*3)-4,(MCW*3)+4,(MCW*4)-3,(MCW*4)-2,(MCW*4)+2,(MCW*4)+3,(MCW*5)-1,(MCW*5)+0,(MCW*5)+1, + /* 6 */ (-MCW*6)-1,(-MCW*6)+0,(-MCW*6)+1,(-MCW*5)-3,(-MCW*5)-2,(-MCW*5)+2,(-MCW*5)+3,(-MCW*4)-4,(-MCW*4)+4,(-MCW*3)-5,(-MCW*3)+5,(-MCW*2)-5,(-MCW*2)+5,(-MCW*1)-6,(-MCW*1)+6,-6,6,(MCW*1)-6,(MCW*1)+6,(MCW*2)-5,(MCW*2)+5,(MCW*3)-5,(MCW*3)+5,(MCW*4)-4,(MCW*4)+4,(MCW*5)-3,(MCW*5)-2,(MCW*5)+2,(MCW*5)+3,(MCW*6)-1,(MCW*6)+0,(MCW*6)+1, + /* 7 */ (-MCW*7)-1,(-MCW*7)+0,(-MCW*7)+1,(-MCW*6)-3,(-MCW*6)-2,(-MCW*6)+2,(-MCW*6)+3,(-MCW*5)-5,(-MCW*5)-4,(-MCW*5)+4,(-MCW*5)+5,(-MCW*4)-5,(-MCW*4)+5,(-MCW*3)-6,(-MCW*3)+6,(-MCW*2)-6,(-MCW*2)+6,(-MCW*1)-7,(-MCW*1)+7,-7,7,(MCW*1)-7,(MCW*1)+7,(MCW*2)-6,(MCW*2)+6,(MCW*3)-6,(MCW*3)+6,(MCW*4)-5,(MCW*4)+5,(MCW*5)-5,(MCW*5)-4,(MCW*5)+4,(MCW*5)+5,(MCW*6)-3,(MCW*6)-2,(MCW*6)+2,(MCW*6)+3,(MCW*7)-1,(MCW*7)+0,(MCW*7)+1, + /* 8 */ (-MCW*8)-1,(-MCW*8)+0,(-MCW*8)+1,(-MCW*7)-3,(-MCW*7)-2,(-MCW*7)+2,(-MCW*7)+3,(-MCW*6)-5,(-MCW*6)-4,(-MCW*6)+4,(-MCW*6)+5,(-MCW*5)-6,(-MCW*5)+6,(-MCW*4)-6,(-MCW*4)+6,(-MCW*3)-7,(-MCW*3)+7,(-MCW*2)-7,(-MCW*2)+7,(-MCW*1)-8,(-MCW*1)+8,-8,8,(MCW*1)-8,(MCW*1)+8,(MCW*2)-7,(MCW*2)+7,(MCW*3)-7,(MCW*3)+7,(MCW*4)-6,(MCW*4)+6,(MCW*5)-6,(MCW*5)+6,(MCW*6)-5,(MCW*6)-4,(MCW*6)+4,(MCW*6)+5,(MCW*7)-3,(MCW*7)-2,(MCW*7)+2,(MCW*7)+3,(MCW*8)-1,(MCW*8)+0,(MCW*8)+1, + /* 9 */ (-MCW*9)-1,(-MCW*9)+0,(-MCW*9)+1,(-MCW*8)-3,(-MCW*8)-2,(-MCW*8)+2,(-MCW*8)+3,(-MCW*7)-5,(-MCW*7)-4,(-MCW*7)+4,(-MCW*7)+5,(-MCW*6)-6,(-MCW*6)+6,(-MCW*5)-7,(-MCW*5)+7,(-MCW*4)-7,(-MCW*4)+7,(-MCW*3)-8,(-MCW*3)+8,(-MCW*2)-8,(-MCW*2)+8,(-MCW*1)-9,(-MCW*1)+9,-9,9,(MCW*1)-9,(MCW*1)+9,(MCW*2)-8,(MCW*2)+8,(MCW*3)-8,(MCW*3)+8,(MCW*4)-7,(MCW*4)+7,(MCW*5)-7,(MCW*5)+7,(MCW*6)-6,(MCW*6)+6,(MCW*7)-5,(MCW*7)-4,(MCW*7)+4,(MCW*7)+5,(MCW*8)-3,(MCW*8)-2,(MCW*8)+2,(MCW*8)+3,(MCW*9)-1,(MCW*9)+0,(MCW*9)+1, + /* 10 */ (-MCW*10)-1,(-MCW*10)+0,(-MCW*10)+1,(-MCW*9)-3,(-MCW*9)-2,(-MCW*9)+2,(-MCW*9)+3,(-MCW*8)-5,(-MCW*8)-4,(-MCW*8)+4,(-MCW*8)+5,(-MCW*7)-7,(-MCW*7)-6,(-MCW*7)+6,(-MCW*7)+7,(-MCW*6)-7,(-MCW*6)+7,(-MCW*5)-8,(-MCW*5)+8,(-MCW*4)-8,(-MCW*4)+8,(-MCW*3)-9,(-MCW*3)+9,(-MCW*2)-9,(-MCW*2)+9,(-MCW*1)-10,(-MCW*1)+10,-10,10,(MCW*1)-10,(MCW*1)+10,(MCW*2)-9,(MCW*2)+9,(MCW*3)-9,(MCW*3)+9,(MCW*4)-8,(MCW*4)+8,(MCW*5)-8,(MCW*5)+8,(MCW*6)-7,(MCW*6)+7,(MCW*7)-7,(MCW*7)-6,(MCW*7)+6,(MCW*7)+7,(MCW*8)-5,(MCW*8)-4, + (MCW*8)+4,(MCW*8)+5,(MCW*9)-3,(MCW*9)-2,(MCW*9)+2,(MCW*9)+3,(MCW*10)-1,(MCW*10)+0,(MCW*10)+1, +}; + +int const MapClass::RadiusCount[11] = {1,9,21,37,61,89,121,161,205,253,309}; + + +CellClass *BlubCell; + +/*********************************************************************************************** + * MapClass::One_Time -- Performs special one time initializations for the map. * + * * + * This routine is used by the game initialization function in order to perform any one * + * time initializations required for the map. This includes allocation of the map and * + * setting up its default dimensions. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine MUST be called once and only once. * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 12/01/1994 BR : Added CellTriggers initialization * + *=============================================================================================*/ +void MapClass::One_Time(void) +{ + GScreenClass::One_Time(); + + XSize = MAP_CELL_W; + YSize = MAP_CELL_H; + Size = XSize * YSize; + + /* + ** Allocate the cell array. + */ + Alloc_Cells(); + + /* + ** Init the CellTriggers array to the required size. + */ + CellTriggers.Resize(MAP_CELL_TOTAL); +} + + +/*********************************************************************************************** + * MapClass::Init_Clear -- clears the map & buffers to a known state * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Init_Clear(void) +{ + GScreenClass::Init_Clear(); + Init_Cells(); + TiberiumScan = 0; + IsForwardScan = true; + TiberiumGrowthCount = 0; + TiberiumSpreadCount = 0; +} + + +/*********************************************************************************************** + * MapClass::Alloc_Cells -- allocates the cell array * + * * + * This routine should be called at One_Time, and after loading the Map object from a save * + * game, but prior to loading the cell objects. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Alloc_Cells(void) +{ + /* + ** Assume that whatever the contents of the VectorClass are is garbage + ** (it may have been loaded from a save-game file), so zero it out first. + */ + Vector = 0; + VectorMax = 0; + IsAllocated = 0; + Resize(Size); +} + + +/*********************************************************************************************** + * MapClass::Free_Cells -- frees the cell array * + * * + * This routine is used by the Load_Game routine to free the map's cell array before loading * + * the map object from disk; the array is then re-allocated & cleared before the cell objects * + * are loaded. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Free_Cells(void) +{ + Clear(); +} + + +/*********************************************************************************************** + * MapClass::Init_Cells -- Initializes the cell array to a fresh state. * + * * + * This routine is used by Init_Clear to set the cells to a known state; it's also used by * + * the Load_Game routine to init all cells before loading a set of cells from disk, so it * + * needs to be called separately from the other Init_xxx() routines. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Init_Cells(void) +{ + TotalValue = 0; +#ifdef NEVER + Free_Cells(); + Alloc_Cells(); +#else + memset(&Map[0], 0, sizeof(CellClass) * (MAP_CELL_TOTAL/2)); + memset(&Map[MAP_CELL_TOTAL/2], 0, sizeof(CellClass) * (MAP_CELL_TOTAL/2)); + for (int index = 0; index < MAP_CELL_TOTAL; index++) { + Map[index].Overlay = OVERLAY_NONE; + Map[index].Smudge = SMUDGE_NONE; + Map[index].TType = TEMPLATE_NONE; + Map[index].Owner = HOUSE_NONE; + Map[index].InfType = HOUSE_NONE; + } +#endif +} + + +/*********************************************************************************************** + * MapClass::Set_Map_Dimensions -- Set map dimensions. * + * * + * This routine is used to set the legal limits and position of the * + * map as it relates to the overall map array. Typically, this is * + * called by the scenario loading code. * + * * + * INPUT: x,y -- The X and Y coordinate of the "upper left" corner * + * of the map. * + * * + * w,h -- The width and height of the legal map. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + *=============================================================================================*/ +void MapClass::Set_Map_Dimensions(int x, int y, int w, int h) +{ + MapCellX = x; + MapCellY = y; + MapCellWidth = w; + MapCellHeight = h; +} + + +/*********************************************************************************************** + * MapClass::Sight_From -- Mark as visible the cells within a specified radius. * + * * + * This routine is used to reveal the cells around a specific location. * + * Typically, as a unit moves or is deployed, this routine will be * + * called. Since it deals with MANY cells, it needs to be extremely * + * fast. * + * * + * INPUT: cell -- The coordinate that the sighting originates from. * + * * + * sightrange-- The distance in cells that sighting extends. * + * * + * incremental-- Is this an incremental sighting. In other * + * words, has this function been called before where * + * the center coordinate is no more than one cell * + * distant from the last time? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1992 JLB : Created. * + * 03/08/1994 JLB : Updated to use sight table and incremental flag. * + * 05/18/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void MapClass::Sight_From(CELL cell, int sightrange, bool incremental) +{ + int xx; // Center cell X coordinate (bounds checking). + int const *ptr; // Offset pointer. + int count; // Counter for number of offsets to process. + + /* + ** Units that are off-map cannot sight. + */ + if (!In_Radar(cell)) return; + if (!sightrange || sightrange > 10) return; + + /* + ** Determine logical cell coordinate for center scan point. + */ + xx = Cell_X(cell); + + /* + ** Incremental scans only scan the outer rings. Full scans + ** scan all internal cells as well. + */ + count = RadiusCount[sightrange]; + ptr = &RadiusOffset[0]; + if (incremental) { + if (sightrange > 1) { + ptr += RadiusCount[sightrange-2]; + count -= RadiusCount[sightrange-2]; + } + } + + /* + ** Process all offsets required for the desired scan. + */ + while (count--) { + CELL newcell; // New cell with offset. + int xdiff; // New cell's X coordinate distance from center. + + newcell = cell + *ptr++; + + /* + ** Determine if the map edge has been wrapped. If so, + ** then don't process the cell. + */ + if ((unsigned)newcell >= MAP_CELL_TOTAL) continue; + xdiff = Cell_X(newcell) - xx; + xdiff = ABS(xdiff); + if (xdiff > sightrange) continue; + if (Distance(newcell, cell) > sightrange) continue; + + /* + ** Map the cell. For incremental scans, then update + ** adjacent cells as well. For full scans, just update + ** the cell itself. + */ + if (!(*this)[newcell].IsMapped) { + Map.Map_Cell(newcell, PlayerPtr); + } + } +} + + +/*********************************************************************************************** + * MapClass::Cell_Distance -- Determines the distance between two cells. * + * * + * This routine will return with the calculated "straight line" * + * distance between the two cells specified. It uses the dragon strike * + * method of distance calculation. * + * * + * INPUT: cell1 -- First cell. * + * * + * cell2 -- Second cell. * + * * + * OUTPUT: Returns with the "cell" distance between the two cells * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/29/1994 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + *=============================================================================================*/ +int MapClass::Cell_Distance(CELL cell1, CELL cell2) +{ + register int x,y; // Difference on X and Y axis. + + x = Cell_X(cell1) - Cell_X(cell2); + y = Cell_Y(cell1) - Cell_Y(cell2); + + if (x < 0) x = -x; + if (y < 0) y = -y; + + if (x > y) { + return(x + (y>>1)); + } + return(y + (x>>1)); +} + + +/*********************************************************************************************** + * MapClass::In_Radar -- Is specified cell in the radar map? * + * * + * This determines if the specified cell can be within the navigable * + * bounds of the map. Technically, this means, any cell that can be * + * scanned by radar. If a cell returns false from this function, then * + * the player could never move to or pass over this cell. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: bool; Is this cell possible to be displayed on radar? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + * 05/01/1994 JLB : Speeded up. * + *=============================================================================================*/ +bool MapClass::In_Radar(CELL cell) const +{ + if (cell & 0xF000) return(false); + return((unsigned)(Cell_X(cell) - MapCellX) < (unsigned)MapCellWidth && (unsigned)(Cell_Y(cell) - MapCellY) < (unsigned)MapCellHeight); +} + + +/*********************************************************************************************** + * MapClass::Place_Down -- Places the specified object onto the map. * + * * + * This routine is used to place an object onto the map. It updates the "occupier" of the * + * cells that this object covers. The cells are determined from the Occupy_List function * + * provided by the object. Only one cell can have an occupier and this routine is the only * + * place that sets this condition. * + * * + * INPUT: cell -- The cell to base object occupation around. * + * * + * object -- The object to place onto the map. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/31/1994 JLB : Created. * + *=============================================================================================*/ +void MapClass::Place_Down(CELL cell, ObjectClass * object) +{ + if (!object) return; + + short const *list = object->Occupy_List(); + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Occupy_Down(object); + (*this)[newcell].Recalc_Attributes(); + (*this)[newcell].Redraw_Objects(); + } + } + + list = object->Overlap_List(); + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Overlap_Down(object); + (*this)[newcell].Redraw_Objects(); + } + } +} + + +/*********************************************************************************************** + * MapClass::Pick_Up -- Removes specified object from the map. * + * * + * The object specified is removed from the map by this routine. This will remove the * + * occupation flag for all the cells that the object covers. The cells that are covered * + * are determined from the Occupy_List function. * + * * + * INPUT: cell -- The cell that the object is centered about. * + * * + * object -- Pointer to the object that will be removed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/31/1994 JLB : Created. * + *=============================================================================================*/ +void MapClass::Pick_Up(CELL cell, ObjectClass * object) +{ + if (!object) return; + + short const *list = object->Occupy_List(); + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Occupy_Up(object); + (*this)[newcell].Recalc_Attributes(); + (*this)[newcell].Redraw_Objects(); + } + } + + list = object->Overlap_List(); + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Overlap_Up(object); + (*this)[newcell].Redraw_Objects(); + } + } +} + + +/*********************************************************************************************** + * MapClass::Overlap_Down -- computes & marks object's overlap cells * + * * + * This routine is just like Place_Down, but it doesn't mark the cell's Occupier. * + * This routine is used to implement MARK_OVERLAP_DOWN, which is useful for changing * + * an object's render size, but not its logical size (ie when it's selected or an * + * animation is attached to it). * + * * + * INPUT: * + * cell -- The cell to base object overlap around. * + * object -- The object to place onto the map. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/12/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Overlap_Down(CELL cell, ObjectClass * object) +{ + if (!object) return; + + short const *list = object->Overlap_List(); + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Overlap_Down(object); + (*this)[newcell].Redraw_Objects(); + } + } +} + + +/*********************************************************************************************** + * MapClass::Overlap_Up -- Computes & clears object's overlap cells * + * * + * This routine is just like Pick_Up, but it doesn't mark the cell's Occupier. * + * This routine is used to implement MARK_OVERLAP_UP, which is useful for changing * + * an object's render size, but not its logical size (ie when it's selected or an * + * animation is attached to it). * + * * + * INPUT: * + * cell -- The cell to base object overlap around. * + * object -- The object to place onto the map. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/12/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Overlap_Up(CELL cell, ObjectClass * object) +{ + if (!object) return; + + short const *list = object->Overlap_List(); + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Overlap_Up(object); + (*this)[newcell].Redraw_Objects(); + } + } +} + + +/*********************************************************************************************** + * MapClass::Overpass -- Performs any final cleanup to a freshly constructed map. * + * * + * This routine will clean up anything necessary with the presumption that the map has * + * been freshly created. Such things to clean up include various tiberium concentrations. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the total credit value of the tiberium on the map. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + * 02/13/1995 JLB : Returns total tiberium worth. * + * 02/15/1995 JLB : Optimal scan. * + *=============================================================================================*/ +long MapClass::Overpass(void) +{ + long value = 0; + + /* + ** Smooth out Tiberium. Cells that are not surrounded by other tiberium + ** will be reduced in density. + */ + for (int y = 0; y < MapCellHeight-1; y++) { + for (int x = 0; x < MapCellWidth; x++) { + value += (*this)[(MapCellY+y) * MAP_CELL_W + (MapCellX+x)].Tiberium_Adjust(true); + } + } + return(value); +} + + +/*********************************************************************************************** + * MapClass::Read_Binary -- reads the map's binary image file * + * * + * INPUT: * + * root root filename for scenario * + * crc ptr to CRC value to update * + * * + * OUTPUT: * + * 1 = success, 0 = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/14/1994 BR : Created. * + * 01/08/1995 JLB : Fixup any obsolete icons detected. * + *=============================================================================================*/ +#ifdef DEMO +bool MapClass::Read_Binary(char const * root, unsigned long *) +#else +bool MapClass::Read_Binary(char const * root, unsigned long *crc) +#endif +{ + CCFileClass file; + char fname[_MAX_FNAME+_MAX_EXT]; + int i; + char *map; + void *rawmap; + void const * shape; + + /* + ** Filename = INI name with BIN extension. + */ + sprintf(fname,"%s.BIN",root); + + /* + ** Create object & open file. + */ + file.Set_Name(fname); + if (!file.Is_Available()) { + return(false); + } + file.Open(READ); + + /* + ** Loop through all cells. + */ + CellClass * cellptr = &Map[0]; + for (i = 0; i < MAP_CELL_TOTAL; i++) { + struct { + TemplateType TType; // Template type. + unsigned char TIcon; // Template icon number. + } temp; + + if (file.Read(&temp, sizeof(temp)) != sizeof(temp)) break; + if (temp.TType == (TemplateType)255) { + temp.TType = TEMPLATE_NONE; + } + + /* + ** Verify that the template type actually contains the template number specified. If + ** an illegal icon was specified, then replace it with clear terrain. + */ + if (temp.TType != TEMPLATE_CLEAR1 && temp.TType != TEMPLATE_NONE) { + shape = TemplateTypeClass::As_Reference(temp.TType).Get_Image_Data(); + if (shape) { + rawmap = Get_Icon_Set_Map(shape); + if (rawmap) { + map = (char*)rawmap; + if (map[temp.TIcon] == -1) { + temp.TIcon = 0; + temp.TType = TEMPLATE_NONE; + } + } + } + } + + cellptr->TType = temp.TType; + cellptr->TIcon = temp.TIcon; + cellptr->Recalc_Attributes(); + +#ifndef DEMO + Add_CRC(crc, (unsigned long)cellptr->TType); + Add_CRC(crc, (unsigned long)cellptr->TIcon); +#endif + + cellptr++; + } + + /* + ** Close the file. + */ + file.Close(); + + return(i == MAP_CELL_TOTAL); +} + + +/*********************************************************************************************** + * MapClass::Write_Binary -- writes the map's binary image file * + * * + * INPUT: * + * root root filename for scenario * + * * + * OUTPUT: * + * 1 = success, 0 = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/14/1994 BR : Created. * + *=============================================================================================*/ +bool MapClass::Write_Binary(char const * root) +{ + CCFileClass *file; + char fname[_MAX_FNAME+_MAX_EXT]; + int i; + + /* + ** Filename = INI name with BIN extension. + */ + sprintf(fname,"%s.BIN",root); + + /* + ** Create object & open file. + */ + file = new CCFileClass(fname); + file->Open(WRITE); + + /* + ** Loop through all cells. + */ + for (i = 0; i < MAP_CELL_TOTAL; i++) { + /* + ** Save TType. + */ + if (file->Write (&(Map[i].TType), sizeof(TemplateType)) != sizeof(TemplateType)) { + file->Close(); + delete file; + return(false); + } + + /* + ** Save TIcon. + */ + if (file->Write (&(Map[i].TIcon), sizeof(unsigned char)) != sizeof(unsigned char)) { + file->Close(); + delete file; + return(false); + } + } + + /* + ** Close the file. + */ + file->Close(); + delete file; + + return(true); +} + + +/*********************************************************************************************** + * MapClass::Logic -- Handles map related logic functions. * + * * + * Manages tiberium growth and spread. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/11/1995 JLB : Created. * + * 07/09/1995 JLB : Handles two directional scan. * + * 08/01/1995 JLB : Gives stronger weight to blossom trees. * + *=============================================================================================*/ +void MapClass::Logic(void) +{ + /* + ** Bail early if there is no allowed growth or spread of Tiberium. + */ + if (!Special.IsTGrowth && !Special.IsTSpread) return; + + /* + ** Scan another block of the map in order to accumulate the potential + ** Tiberium cells that can grow or spread. + */ + int subcount = 30; + for (int index = TiberiumScan; index < MAP_CELL_TOTAL; index++) { + CELL cell = index; + if (!IsForwardScan) cell = (MAP_CELL_TOTAL-1) - index; + CellClass *ptr = &(*this)[cell]; + + if (Special.IsTGrowth && ptr->Land_Type() == LAND_TIBERIUM && ptr->OverlayData < 11) { + if (TiberiumGrowthCount < sizeof(TiberiumGrowth)/sizeof(TiberiumGrowth[0])) { + TiberiumGrowth[TiberiumGrowthCount++] = cell; + } else { + TiberiumGrowth[Random_Pick(0, TiberiumGrowthCount-1)] = cell; + } + } + + /* + ** Heavy Tiberium growth can spread. + */ + TerrainClass * terrain = ptr->Cell_Terrain(); + if (Special.IsTSpread && + (ptr->Land_Type() == LAND_TIBERIUM && ptr->OverlayData > 6) || + (terrain && terrain->Class->IsTiberiumSpawn)) { + + int tries = 1; + if (terrain) tries = 3; + for (int i = 0; i < tries; i++) { + if (TiberiumSpreadCount < sizeof(TiberiumSpread)/sizeof(TiberiumSpread[0])) { + TiberiumSpread[TiberiumSpreadCount++] = cell; + } else { + TiberiumSpread[Random_Pick(0, TiberiumSpreadCount-1)] = cell; + } + } + } + subcount--; + if (!subcount) break; + } + TiberiumScan = index; + + if (TiberiumScan >= MAP_CELL_TOTAL) { + int tries = 1; + if (Special.IsTFast || GameToPlay != GAME_NORMAL) tries = 2; + TiberiumScan = 0; + IsForwardScan = (IsForwardScan == false); + + /* + ** Growth logic. + */ + if (TiberiumGrowthCount) { + for (int i = 0; i < tries; i++) { + CELL cell = TiberiumGrowth[Random_Pick(0, TiberiumGrowthCount-1)]; + CellClass * newcell = &(*this)[cell]; + if (newcell->Land_Type() == LAND_TIBERIUM && newcell->OverlayData < 12-1) { + newcell->OverlayData++; + } + } + } + TiberiumGrowthCount = 0; + + /* + ** Spread logic. + */ + if (TiberiumSpreadCount) { + for (int i = 0; i < tries; i++) { + CELL cell = TiberiumSpread[Random_Pick(0, TiberiumSpreadCount-1)]; + + /* + ** Find a pseudo-random adjacent cell that doesn't contain any tiberium. + */ + if (Map.In_Radar(cell)) { + FacingType offset = Random_Pick(FACING_N, FACING_NW); + for (FacingType index = FACING_N; index < FACING_COUNT; index++) { + CellClass *newcell = &(*this)[cell].Adjacent_Cell(index+offset); + + if (newcell && newcell->Cell_Object() == NULL && newcell->Land_Type() == LAND_CLEAR && newcell->Overlay == OVERLAY_NONE) { + bool found = false; + + switch (newcell->TType) { + case TEMPLATE_BRIDGE1: + case TEMPLATE_BRIDGE2: + case TEMPLATE_BRIDGE3: + case TEMPLATE_BRIDGE4: + break; + + default: + found = true; + new OverlayClass(Random_Pick(OVERLAY_TIBERIUM1, OVERLAY_TIBERIUM12), newcell->Cell_Number()); + newcell->OverlayData = 1; + break; + + } + if (found) break; + } + } + } + } + } + TiberiumSpreadCount = 0; + } +} + + +/*********************************************************************************************** + * MapClass::Cell_Region -- Determines the region from a specified cell number. * + * * + * Use this routine to determine what region a particular cell lies in. * + * * + * INPUT: cell -- The cell number to examine. * + * * + * OUTPUT: Returns with the region that the specified cell occupies. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 JLB : Created. * + *=============================================================================================*/ +int MapClass::Cell_Region(CELL cell) +{ + return((Cell_X(cell) / REGION_WIDTH) + 1) + (((Cell_Y(cell) / REGION_HEIGHT) + 1) * MAP_REGION_WIDTH); +} + + +/*************************************************************************** + * MapClass::Cell_Threat -- Gets a houses threat value for a cell * + * * + * INPUT: CELL cell - the cell number to check * + * HouseType house - the house to check * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/25/1995 PWG : Created. * + *=========================================================================*/ +int MapClass::Cell_Threat(CELL cell, HousesType house) +{ + int threat = HouseClass::As_Pointer(house)->Regions[Map.Cell_Region(Map[cell].Cell_Number())].Threat_Value(); + if (!threat && Map[cell].IsVisible) { + threat = 1; + } + return(threat); +} + + +/*********************************************************************************************** + * MapClass::Place_Random_Crate -- Places a crate at random location on map. * + * * + * This routine will place a crate at a random location on the map. This routine will only * + * make a limited number of attempts to place and if unsuccessful, it will not place any. * + * * + * INPUT: none * + * * + * OUTPUT: Was a crate successfully placed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +bool MapClass::Place_Random_Crate(void) +{ + int old = ScenarioInit; + ScenarioInit = 0; + for (int index = 0; index < 100; index++) { + int x = Random_Pick(0, MapCellWidth-1); + int y = Random_Pick(0, MapCellHeight-1); + CELL cell = XY_Cell(MapCellX+x, MapCellY+y); + + CellClass * ptr = &(*this)[cell]; + if (ptr->Is_Generally_Clear() && ptr->Overlay == OVERLAY_NONE) { + ptr->Overlay = OVERLAY_WOOD_CRATE; + ptr->OverlayData = 0; + ptr->Redraw_Objects(); + ScenarioInit = old; + return(true); + } + } + ScenarioInit = old; + return(false); +} + + +/*************************************************************************** + * MapClass::Validate -- validates every cell on the map * + * * + * This is a debugging routine, designed to detect memory trashers that * + * alter the map. This routine is slow, but thorough. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = map is OK, false = an error was found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/08/1995 BRR : Created. * + *=========================================================================*/ +int MapClass::Validate(void) +{ + CELL cell; + TemplateType ttype; + unsigned char ticon; + TemplateTypeClass const *tclass; + unsigned char map[13*8]; + OverlayType overlay; + SmudgeType smudge; + ObjectClass *obj; + LandType land; + int i; + +BlubCell = &((*this)[797]); + +if (BlubCell->Overlapper[1]) { + obj = BlubCell->Overlapper[1]; + if (obj) { + if (obj->IsInLimbo) + obj = obj; + } +} + + /*------------------------------------------------------------------------ + Check every cell on the map, even those that aren't displayed, + in the hopes of detecting a memory trasher. + ------------------------------------------------------------------------*/ + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + /*..................................................................... + Validate Template & Icon data + .....................................................................*/ + ttype = (*this)[cell].TType; + ticon = (*this)[cell].TIcon; + if (ttype >= TEMPLATE_COUNT && ttype != TEMPLATE_NONE) + return(false); + + /*..................................................................... + To validate the icon value, we have to get a copy of the template's + "icon map"; this map will have 0xff's in spots where there is no + icon. If the icon value is out of range or points to an invalide spot, + return an error. + .....................................................................*/ + if (ttype != TEMPLATE_NONE) { + tclass = &TemplateTypeClass::As_Reference(ttype); + ticon = (*this)[cell].TIcon; + Mem_Copy(Get_Icon_Set_Map(tclass->Get_Image_Data()), map, + tclass->Width * tclass->Height); + if (ticon < 0 || + ticon >= (tclass->Width * tclass->Height) || + map[ticon]==0xff) + return (false); + } + + /*..................................................................... + Validate Overlay + .....................................................................*/ + overlay = (*this)[cell].Overlay; + if (overlay < OVERLAY_NONE || overlay >= OVERLAY_COUNT) + return(false); + + /*..................................................................... + Validate Smudge + .....................................................................*/ + smudge = (*this)[cell].Smudge; + if (smudge < SMUDGE_NONE || smudge >= SMUDGE_COUNT) + return(false); + + /*..................................................................... + Validate LandType + .....................................................................*/ + land = (*this)[cell].Land_Type(); + if (land < LAND_CLEAR || land >= LAND_COUNT) + return(false); + + /*..................................................................... + Validate Occupier + .....................................................................*/ + obj = (*this)[cell].Cell_Occupier(); + if (obj) { + if ( + ((unsigned int)obj & 0xff000000) || + ((unsigned int)obj->Next & 0xff000000) || + ((unsigned int)obj->Trigger & 0xff000000) || + obj->IsInLimbo || + ((unsigned int)Coord_Cell(obj->Coord) > 4095) + ) + return (false); + } + + /*..................................................................... + Validate Overlappers + .....................................................................*/ + for (i = 0; i < 3; i++) { + obj = (*this)[cell].Overlapper[i]; + if (obj) { + if ( + ((unsigned int)obj & 0xff000000) || + ((unsigned int)obj->Next & 0xff000000) || + ((unsigned int)obj->Trigger & 0xff000000) || + obj->IsInLimbo || + ((unsigned int)Coord_Cell(obj->Coord) > 4095) + ) + return (false); + } + } + } + + return (true); +} + + +/*********************************************************************************************** + * MapClass::Close_Object -- Finds a clickable close object to the specified coordinate. * + * * + * This routine is used by the mouse input processing code to find a clickable object * + * close to coordinate specified. This is for targeting as well as selection determination. * + * * + * INPUT: coord -- The coordinate to scan for close object from. * + * * + * OUTPUT: Returns with a pointer to an object that is nearby the specified coordinate. * + * * + * WARNINGS: There could be a cloaked object at the location, but it won't be considered * + * if it is not owned by the player. * + * * + * HISTORY: * + * 08/20/1995 JLB : Created. * + *=============================================================================================*/ +ObjectClass * MapClass::Close_Object(COORDINATE coord) const +{ + ObjectClass * object = 0; + int distance = 0; + CELL cell = Coord_Cell(coord); + + /* + ** Scan through current and adjacent cells, looking for the + ** closest object (within reason) to the specified coordinate. + */ + static int _offsets[] = {0, -1, 1, -MAP_CELL_W, MAP_CELL_W, MAP_CELL_W-1, MAP_CELL_W+1, -(MAP_CELL_W-1), -(MAP_CELL_W+1)}; + for (int index = 0; index < (sizeof(_offsets) / sizeof(_offsets[0])); index++) { + + /* + ** Examine the cell for close object. Make sure that the cell actually is a + ** legal one. + */ + CELL newcell = cell + _offsets[index]; + if (In_Radar(newcell)) { + + /* + ** Search through all objects that occupy this cell and then + ** find the closest object. Check against any previously found object + ** to ensure that it is actually closer. + */ + ObjectClass * o = (*this)[newcell].Cell_Occupier(); + while (o) { + + /* + ** Special case check to ignore cloaked object if not owned by the player. + */ + if (!o->Is_Techno() || ((TechnoClass *)o)->IsOwnedByPlayer || ((TechnoClass *)o)->Cloak != CLOAKED) { + int d=-1; + if (o->What_Am_I() == RTTI_BUILDING) { + d = Distance(coord, Cell_Coord(newcell)); + if (d > 0x00B5) d = -1; + } else { + d = Distance(coord, o->Center_Coord()); + } + if (d >= 0 && (!object || d < distance)) { + distance = d; + object = o; + } + } + o = o->Next; + } + } + } + + /* + ** Only return the object if it is within 1/4 cell distance from the specified + ** coordinate. + */ + if (object && distance > 0xB5) { + object = 0; + } + return(object); +} + diff --git a/MAP.H b/MAP.H new file mode 100644 index 0000000..031abd9 --- /dev/null +++ b/MAP.H @@ -0,0 +1,154 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\map.h_v 2.19 16 Oct 1995 16:46:12 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAP.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : April 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MAP_H +#define MAP_H + +#include "gscreen.h" + +#define BIGMAP 0 + + +class MapClass: public GScreenClass +{ + public: + + /* + ** Initialization + */ + virtual void One_Time(void); // Theater-specific inits + virtual void Init_Clear(void); // Clears all to known state + virtual void Alloc_Cells(void); // Allocates buffers + virtual void Free_Cells(void); // Frees buffers + virtual void Init_Cells(void); // Frees buffers + + /*-------------------------------------------------------- + ** Main functions that deal with groupings of cells within the map or deals with the cell + ** as it relates to the map - not what the cell contains. + */ + ObjectClass * Close_Object(COORDINATE coord) const; + virtual void Detach(ObjectClass * ) {}; + int Cell_Region(CELL cell); + int Cell_Threat(CELL cell, HousesType house); + int Cell_Distance(CELL cell1, CELL cell2); + bool In_Radar(CELL cell) const; + void Sight_From(CELL cell, int sightrange, bool incremental=false); + void Place_Down(CELL cell, ObjectClass * object); + void Pick_Up(CELL cell, ObjectClass * object); + void Overlap_Down(CELL cell, ObjectClass * object); + void Overlap_Up(CELL cell, ObjectClass * object); + bool Read_Binary(char const *root, unsigned long *crc); + bool Write_Binary(char const *root); + bool Place_Random_Crate(void); + + long Overpass(void); + + virtual void Logic(void); + virtual void Set_Map_Dimensions(int x, int y, int w, int h); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Debug routine + */ + int Validate(void); + + /* + ** This is the dimensions and position of the sub section of the global map. + ** It is this region that appears on the radar map and constrains normal + ** movement. + */ + int MapCellX; + int MapCellY; + int MapCellWidth; + int MapCellHeight; + + /* + ** This is the total value of all harvestable Tiberium on the map. + */ + long TotalValue; + + protected: + + /* + ** These are the size dimensions of the underlying array of cell objects. + ** This is the dimensions of the "map" that the tactical view is + ** restricted to. + */ + int XSize; + int YSize; + int Size; + + static int const RadiusCount[11]; + static int const RadiusOffset[]; + + private: + friend class CellClass; + + /* + ** Tiberium growth potiential cells are recorded here. + */ + CELL TiberiumGrowth[50]; + int TiberiumGrowthCount; + + /* + ** List of cells that are full enough strength that they could spread + ** Tiberium to adjacent cells. + */ + CELL TiberiumSpread[50]; + int TiberiumSpreadCount; + + /* + ** This is the current cell number in the incremental map scan process. + */ + CELL TiberiumScan; + + /* + ** If the Tiberium map scan is processing forward, then this flag + ** will be true. It alternates between forward and backward scanning + ** in order to avoid the "Tiberium Creep". + */ + unsigned IsForwardScan:1; + + enum MapEnum {SCAN_AMOUNT=MAP_CELL_TOTAL}; +}; + +#endif diff --git a/MAPEDDLG.CPP b/MAPEDDLG.CPP new file mode 100644 index 0000000..e2e22f9 --- /dev/null +++ b/MAPEDDLG.CPP @@ -0,0 +1,3850 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\mapeddlg.cpv 2.18 16 Oct 1995 16:49:00 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPEDDLG.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : November 18, 1994 * + * * + * Last Update : December 12, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Map Editor dialogs & main menu options * + *-------------------------------------------------------------------------* + * Functions: * + * MapEditClass::New_Scenario -- creates a new scenario * + * MapEditClass::Load_Scenario -- loads a scenario INI file * + * MapEditClass::Save_Scenario -- saves current scenario to an INI file * + * MapEditClass::Pick_Scenario -- dialog for choosing scenario * + * MapEditClass::Size_Map -- lets user set size & location of map * + * MapEditClass::Scenario_Dialog -- scenario global parameters dialog * + * MapEditClass::Handle_Triggers -- processes the trigger dialogs * + * MapEditClass::Select_Trigger -- lets user select a trigger * + * MapEditClass::Edit_Trigger -- lets user edit a [new] trigger * + * MapEditClass::Import_Triggers -- lets user import triggers * + * MapEditClass::Import_Teams -- lets user import teams * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef SCENARIO_EDITOR + + +/*************************************************************************** + * MapEditClass::New_Scenario -- creates a new scenario * + * * + * - Prompts user for scenario data (house, scenario #); sets globals * + * PlayerPtr (for house) & Scenario (for scenario #) * + * - Prompts user for map size * + * - Initializes the scenario by calling Clear_Scenario(), which calls * + * everybody's Init() routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = new scenario created, -1 = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::New_Scenario(void) +{ + int scen_num = Scenario; + ScenarioPlayerType player = ScenPlayer; + ScenarioDirType dir = ScenDir; + ScenarioVarType var = ScenVar; + int rc; + HousesType house; + + /* + ------------------------ Prompt for scenario info ------------------------ + */ + rc = Pick_Scenario("New Scenario", &scen_num, &player, &dir, &var, 1); + if (rc != 0) { + return(-1); + } + + /* + -------------------------- Blow away everything -------------------------- + */ + Clear_Scenario(); + + /* + ----------------------------- Set parameters ----------------------------- + */ + Scenario = scen_num; + ScenPlayer = player; + ScenDir = dir; + ScenVar = var; + Set_Scenario_Name(ScenarioName,scen_num,player,dir,var); + + /* + ----------------------------- Create houses ------------------------------ + */ + for (house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + new HouseClass(house); + } + + if (ScenPlayer == SCEN_PLAYER_MPLAYER) { + PlayerPtr = HouseClass::As_Pointer(HOUSE_MULTI1); + PlayerPtr->IsHuman = true; + LastHouse = HOUSE_MULTI1; + } else { + if (player == SCEN_PLAYER_GDI) { + PlayerPtr = HouseClass::As_Pointer(HOUSE_GOOD); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_BAD; + } else { + if (player == SCEN_PLAYER_NOD) { + PlayerPtr = HouseClass::As_Pointer(HOUSE_BAD); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_GOOD; + } else { + PlayerPtr = HouseClass::As_Pointer(HOUSE_MULTI4); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_MULTI4; + } + } + LastHouse = HOUSE_GOOD; + } + + /* + -------------------------- Init the entire map --------------------------- + */ + Init_Clear(); + Fill_In_Data(); + + /* + -------------------------- Prompt for map size --------------------------- + */ + Size_Map(-1,-1,20,20); + + /* + ------ Set the Home & Reinforcement Cells to the center of the map ------- + */ + Waypoint[WAYPT_REINF] = XY_Cell(MapCellX + MapCellWidth / 2, MapCellY + MapCellHeight / 2); + Waypoint[WAYPT_HOME] = XY_Cell(MapCellX + MapCellWidth / 2, MapCellY + MapCellHeight / 2); + (*this)[Coord_Cell(TacticalCoord)].IsWaypoint = 1; + Flag_Cell(Coord_Cell(TacticalCoord)); + + ScenarioInit++; + Set_Tactical_Position(Cell_Coord(Waypoint[WAYPT_HOME])); + ScenarioInit--; + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Load_Scenario -- loads a scenario INI file * + * * + * - Prompts user for scenario data (house, scenario #); sets globals * + * PlayerPtr (for house) & Scenario (for scenario #) * + * - Loads the INI file for that scenario * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Load_Scenario(void) +{ + int scen_num = Scenario; + ScenarioPlayerType player = ScenPlayer; + ScenarioDirType dir = ScenDir; + ScenarioVarType var = ScenVar; + int rc; + + /* + ------------------------ Prompt for scenario info ------------------------ + */ + rc = Pick_Scenario("Load Scenario", &scen_num, &player, &dir, &var, 1); + if (rc != 0) { + return(-1); + } + + /* + ----------------------------- Set parameters ----------------------------- + */ + Scenario = scen_num; + ScenPlayer = player; + ScenDir = dir; + ScenVar = var; + Set_Scenario_Name(ScenarioName,scen_num,player,dir,var); + + /*------------------------------------------------------------------------ + Read_Scenario_Ini() must be able to set PlayerPtr to the right house: + - Reading the INI will create the house objects + - PlayerPtr must be set before any Techno objects are created + - For GDI or NOD scenarios, PlayerPtr is set by reading the INI; + but for multiplayer, it's set via the MPlayerLocalID; so, here we have + to set various multiplayer variables to fool the Assign_Houses() routine + into working properly. + ------------------------------------------------------------------------*/ + if (ScenPlayer == SCEN_PLAYER_MPLAYER) { + MPlayerLocalID = Build_MPlayerID(2,HOUSE_GOOD); + MPlayerCount = 1; + LastHouse = HOUSE_MULTI1; + } + else if (ScenPlayer==SCEN_PLAYER_JP) { + PlayerPtr = HouseClass::As_Pointer(HOUSE_MULTI4); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_MULTI4; + } + else { + LastHouse = HOUSE_GOOD; + } + + /* + -------------------------- Blow away everything -------------------------- + */ + Clear_Scenario(); + + /* + ------------------------------ Read the INI ------------------------------ + */ + if (Read_Scenario_Ini(ScenarioName) == 0) { + CCMessageBox().Process("Unable to read scenario!"); + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + } else { + Fill_In_Data(); + Set_Palette(GamePalette); + } + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Save_Scenario -- saves current scenario to an INI file * + * * + * - Prompts user for scenario data (house, scenario #); sets globals * + * PlayerPtr (for house) & Scenario (for scenario #) * + * - Saves the INI file for this scenario * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = error/cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Save_Scenario(void) +{ + int scen_num = Scenario; + ScenarioPlayerType player = ScenPlayer; + ScenarioDirType dir = ScenDir; + ScenarioVarType var = ScenVar; + int rc; + FILE *fp; + char fname[13]; + + /* + ------------------------ Prompt for scenario info ------------------------ + */ + rc = Pick_Scenario("Save Scenario", &scen_num, &player, &dir, &var, 0); + if (rc != 0) { + return(-1); + } + + /* + ------------------- Warning if scenario already exists ------------------- + */ + Set_Scenario_Name(fname, scen_num, player, dir, var); + fp = fopen(fname,"rb"); + if (fp) { + fclose(fp); + rc = CCMessageBox().Process("File exists. Replace?", TXT_YES, TXT_NO); + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + if (rc==1) + return(-1); + } + + /* + ----------------------------- Set parameters ----------------------------- + */ + Scenario = scen_num; + ScenPlayer = player; + ScenDir = dir; + ScenVar = var; + Set_Scenario_Name(ScenarioName,scen_num,player,dir,var); + + /*------------------------------------------------------------------------ + Player may have changed from GDI to NOD, so change playerptr accordingly + ------------------------------------------------------------------------*/ + if (ScenPlayer == SCEN_PLAYER_GDI || ScenPlayer==SCEN_PLAYER_NOD) { + if (ScenPlayer==SCEN_PLAYER_GDI) { + PlayerPtr = HouseClass::As_Pointer(HOUSE_GOOD); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_BAD; + } else { + if (ScenPlayer == SCEN_PLAYER_NOD) { + PlayerPtr = HouseClass::As_Pointer(HOUSE_BAD); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_GOOD; + } else { + PlayerPtr = HouseClass::As_Pointer(HOUSE_MULTI4); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_MULTI4; + } + } + LastHouse = HOUSE_GOOD; + } + + /* + ----------------------------- Write the INI ------------------------------ + */ + Write_Scenario_Ini(ScenarioName); + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Pick_Scenario -- dialog for choosing scenario * + * * + * Prompts user for: * + * - House (GDI, NOD) * + * - Scenario # * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Caption ³ * + * ³ ³ * + * ³ Scenario ___ ³ * + * ³ Version ___ ³ * + * ³ ³ * + * ³ [East] [West] ³ * + * ³ ³ * + * ³ [ GDI ] ³ * + * ³ [ NOD ] ³ * + * ³ [Multi-Player] ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * caption string to use as a title * + * scen_nump output: ptr to scenario # * + * playerp output: ptr to player type * + * dirp output: ptr to direction * + * varp output: ptr to variation * + * multi 1 = allow to change single/multiplayer; 0 = not * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Pick_Scenario(char const * caption, int *scen_nump, + ScenarioPlayerType *playerp, ScenarioDirType *dirp, ScenarioVarType *varp, + int multi) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + enum { + D_DIALOG_W = 400, // dialog width + D_DIALOG_H = 328, // dialog height + D_DIALOG_X = ((640 - D_DIALOG_W) / 2), // centered x-coord + D_DIALOG_Y = ((400 - D_DIALOG_H) / 2), // centered y-coord + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), // coord of x-center + + D_TXT8_H = 22, // ht of 8-pt text + D_MARGIN = 14, // margin width/height + + D_SCEN_W = 90, // Scenario # width + D_SCEN_H = 18, // Scenario # height + D_SCEN_X = D_DIALOG_CX + 5, // Scenario # x + D_SCEN_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H + D_MARGIN, // Scenario # y + + D_VARA_W = 26, // Version A width + D_VARA_H = 18, // Version A height + D_VARA_X = D_DIALOG_CX - (D_VARA_W * 5) / 2, // Version A x + D_VARA_Y = D_SCEN_Y + D_SCEN_H + D_MARGIN, // Version A y + + D_VARB_W = 26, // Version B width + D_VARB_H = 18, // Version B height + D_VARB_X = D_VARA_X + D_VARA_W, // Version B x + D_VARB_Y = D_SCEN_Y + D_SCEN_H + D_MARGIN, // Version B y + + D_VARC_W = 26, // Version C width + D_VARC_H = 18, // Version C height + D_VARC_X = D_VARB_X + D_VARB_W, // Version C x + D_VARC_Y = D_SCEN_Y + D_SCEN_H + D_MARGIN, // Version C y + + D_VARD_W = 26, // Version D width + D_VARD_H = 18, // Version D height + D_VARD_X = D_VARC_X + D_VARC_W, // Version D x + D_VARD_Y = D_SCEN_Y + D_SCEN_H + D_MARGIN, // Version D y + + D_VARLOSE_W = 26, // Version Lose width + D_VARLOSE_H = 18, // Version Lose height + D_VARLOSE_X = D_VARD_X + D_VARD_W, // Version Lose x + D_VARLOSE_Y = D_SCEN_Y + D_SCEN_H + D_MARGIN, // Version Lose y + + D_EAST_W = 100, // EAST width + D_EAST_H = 18, // EAST height + D_EAST_X = D_DIALOG_CX - D_EAST_W - 5, // EAST x + D_EAST_Y = D_VARLOSE_Y + D_VARLOSE_H + D_MARGIN,// EAST y + + D_WEST_W = 100, // WEST width + D_WEST_H = 18, // WEST height + D_WEST_X = D_DIALOG_CX + 5, // WEST x + D_WEST_Y = D_VARLOSE_Y + D_VARLOSE_H + D_MARGIN,// EAST y + + D_GDI_W = 140, // GDI width + D_GDI_H = 18, // GDI height + D_GDI_X = D_DIALOG_CX - (D_GDI_W / 2), // GDI x + D_GDI_Y = D_EAST_Y + D_EAST_H + D_MARGIN, // GDI y + + D_NOD_W = 140, // NOD width + D_NOD_H = 18, // NOD height + D_NOD_X = D_DIALOG_CX - (D_NOD_W / 2), // NOD x + D_NOD_Y = D_GDI_Y + D_GDI_H, // NOD y + + D_NEU_W = 140, // Neutral width + D_NEU_H = 18, // Neutral height + D_NEU_X = D_DIALOG_CX - (D_NOD_W / 2), // Neutral x + D_NEU_Y = D_NOD_Y + D_NOD_H, // Neutral y + + D_MPLAYER_W = 140, // Multi-Player width + D_MPLAYER_H = 18, // Multi-Player height + D_MPLAYER_X = D_DIALOG_CX - (D_MPLAYER_W / 2), // Multi-Player x + D_MPLAYER_Y = D_NEU_Y + D_NEU_H, // Multi-Player y + + D_OK_W = 90, // OK width + D_OK_H = 18, // OK height + D_OK_X = D_DIALOG_CX - D_OK_W - 5, // OK x + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - D_MARGIN, // OK y + + D_CANCEL_W = 90, // Cancel width + D_CANCEL_H = 18, // Cancel height + D_CANCEL_X = D_DIALOG_CX + 5, // Cancel x + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_CANCEL_H - D_MARGIN, // Cancel y + + }; + /*........................................................................ + Button enumerations + ........................................................................*/ + enum { + BUTTON_GDI=100, + BUTTON_NOD, + BUTTON_NEUTRAL, + BUTTON_MPLAYER, + BUTTON_EAST, + BUTTON_WEST, + BUTTON_OK, + BUTTON_CANCEL, + BUTTON_SCENARIO, + BUTTON_VAR_A, + BUTTON_VAR_B, + BUTTON_VAR_C, + BUTTON_VAR_D, + BUTTON_VAR_L, + }; + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display; // requested redraw level + bool process; // loop while true + KeyNumType input; + bool cancel = false; // true = user cancels + /*........................................................................ + Other Variables + ........................................................................*/ + char scen_buf[10]={0}; // buffer for editing scenario # + /*........................................................................ + Buttons + ........................................................................*/ + ControlClass *commands = NULL; // the button list + + EditClass editbtn (BUTTON_SCENARIO, + scen_buf, 5, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_SCEN_X, D_SCEN_Y, D_SCEN_W, D_SCEN_H, EditClass::NUMERIC); + + TextButtonClass varabtn (BUTTON_VAR_A, "A", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_VARA_X, D_VARA_Y, D_VARA_W, D_VARA_H); + + TextButtonClass varbbtn (BUTTON_VAR_B, "B", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_VARB_X, D_VARB_Y, D_VARB_W, D_VARB_H); + + TextButtonClass varcbtn (BUTTON_VAR_C, "C", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_VARC_X, D_VARC_Y, D_VARC_W, D_VARC_H); + + TextButtonClass vardbtn (BUTTON_VAR_D, "D", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_VARD_X, D_VARD_Y, D_VARD_W, D_VARD_H); + + TextButtonClass varlbtn (BUTTON_VAR_L, "L", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_VARLOSE_X, D_VARLOSE_Y, D_VARLOSE_W, D_VARLOSE_H); + + TextButtonClass gdibtn (BUTTON_GDI, "GDI", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_GDI_X, D_GDI_Y, D_GDI_W, D_GDI_H); + + TextButtonClass nodbtn (BUTTON_NOD, "NOD", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NOD_X, D_NOD_Y, D_NOD_W, D_NOD_H); + + TextButtonClass playermbtn (BUTTON_MPLAYER, "Multi Player", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MPLAYER_X, D_MPLAYER_Y, D_MPLAYER_W, D_MPLAYER_H); + + TextButtonClass eastbtn (BUTTON_EAST, "East", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_EAST_X, D_EAST_Y, D_EAST_W, D_EAST_H); + + TextButtonClass westbtn (BUTTON_WEST, "West", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_WEST_X, D_WEST_Y, D_WEST_W, D_WEST_H); + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + sprintf(scen_buf,"%d",(*scen_nump)); // init edit buffer + editbtn.Set_Text(scen_buf,5); + + varabtn.Turn_Off(); + varbbtn.Turn_Off(); + varcbtn.Turn_Off(); + vardbtn.Turn_Off(); + varlbtn.Turn_Off(); + switch (*varp) { + case SCEN_VAR_A: + varabtn.Turn_On(); + break; + + case SCEN_VAR_B: + varbbtn.Turn_On(); + break; + + case SCEN_VAR_C: + varcbtn.Turn_On(); + break; + + case SCEN_VAR_D: + vardbtn.Turn_On(); + break; + + case SCEN_VAR_LOSE: + varlbtn.Turn_On(); + break; + } + + /* + ......................... Create the button list ......................... + */ + commands = &editbtn; + varabtn.Add_Tail(*commands); + varbbtn.Add_Tail(*commands); + varcbtn.Add_Tail(*commands); + vardbtn.Add_Tail(*commands); + varlbtn.Add_Tail(*commands); + if (multi) { + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); + playermbtn.Add_Tail(*commands); + } else { + if ((*playerp) == SCEN_PLAYER_MPLAYER) { + playermbtn.Add_Tail(*commands); + } else { + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); + } + } + eastbtn.Add_Tail(*commands); + westbtn.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + /* + ......................... Init the button states ......................... + */ + if ((*playerp) == SCEN_PLAYER_GDI) { + gdibtn.Turn_On(); + nodbtn.Turn_Off(); + playermbtn.Turn_Off(); + } else { + if ((*playerp) == SCEN_PLAYER_NOD) { + gdibtn.Turn_Off(); + nodbtn.Turn_On(); + playermbtn.Turn_Off(); + } else { + gdibtn.Turn_Off(); + nodbtn.Turn_Off(); + playermbtn.Turn_On(); + } + } + + if ((*dirp)==SCEN_DIR_EAST) { + eastbtn.Turn_On(); + westbtn.Turn_Off(); + } else { + eastbtn.Turn_Off(); + westbtn.Turn_On(); + } + + /* + -------------------------- Main Processing Loop -------------------------- + */ + display = REDRAW_ALL; + process = true; + while (process) { + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + + /* + ...................... Display the dialog box ...................... + */ + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + /* + ....................... Draw the captions ....................... + */ + Fancy_Text_Print(caption, D_DIALOG_CX, D_DIALOG_Y + D_MARGIN, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print("Scenario", D_DIALOG_CX - 5, + D_SCEN_Y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + + /* + ........................ Redraw the buttons ........................ + */ + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (BUTTON_VAR_A | KN_BUTTON): + (*varp) = SCEN_VAR_A; + varabtn.Turn_On(); + varbbtn.Turn_Off(); + varcbtn.Turn_Off(); + vardbtn.Turn_Off(); + varlbtn.Turn_Off(); + break; + + case (BUTTON_VAR_B | KN_BUTTON): + (*varp) = SCEN_VAR_B; + varabtn.Turn_Off(); + varbbtn.Turn_On(); + varcbtn.Turn_Off(); + vardbtn.Turn_Off(); + varlbtn.Turn_Off(); + break; + + case (BUTTON_VAR_C | KN_BUTTON): + (*varp) = SCEN_VAR_C; + varabtn.Turn_Off(); + varbbtn.Turn_Off(); + varcbtn.Turn_On(); + vardbtn.Turn_Off(); + varlbtn.Turn_Off(); + break; + + case (BUTTON_VAR_D | KN_BUTTON): + (*varp) = SCEN_VAR_D; + varabtn.Turn_Off(); + varbbtn.Turn_Off(); + varcbtn.Turn_Off(); + vardbtn.Turn_On(); + varlbtn.Turn_Off(); + break; + + case (BUTTON_VAR_L | KN_BUTTON): + (*varp) = SCEN_VAR_LOSE; + varabtn.Turn_Off(); + varbbtn.Turn_Off(); + varcbtn.Turn_Off(); + vardbtn.Turn_Off(); + varlbtn.Turn_On(); + break; + + case (BUTTON_EAST | KN_BUTTON): + (*dirp) = SCEN_DIR_EAST; + eastbtn.Turn_On(); + westbtn.Turn_Off(); + break; + + case (BUTTON_WEST | KN_BUTTON): + (*dirp) = SCEN_DIR_WEST; + eastbtn.Turn_Off(); + westbtn.Turn_On(); + break; + + case (BUTTON_GDI | KN_BUTTON): + (*playerp) = SCEN_PLAYER_GDI; + gdibtn.Turn_On(); + nodbtn.Turn_Off(); + playermbtn.Turn_Off(); + break; + + case (BUTTON_NOD | KN_BUTTON): + (*playerp) = SCEN_PLAYER_NOD; + gdibtn.Turn_Off(); + nodbtn.Turn_On(); + playermbtn.Turn_Off(); + break; + + case (BUTTON_MPLAYER | KN_BUTTON): + (*playerp) = SCEN_PLAYER_MPLAYER; + gdibtn.Turn_Off(); + nodbtn.Turn_Off(); + playermbtn.Turn_On(); + break; + + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + cancel = false; + process = false; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + case (BUTTON_SCENARIO | KN_BUTTON): + break; + + default: + break; + } + } + + /* + --------------------------- Redraw the display --------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ------------------------- If cancel, just return ------------------------- + */ + if (cancel) + return(-1); + + /* + ------------------------ Save selections & return ------------------------ + */ + (*scen_nump) = atoi(scen_buf); + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Size_Map -- lets user set size & location of map * + * * + * Lets the user select a side of the map and expand/shrink it to the * + * desired size, or move the whole map around the available map area. * + * * + * The entire available map area is displayed, but the map is limited such * + * that there's always one blank cell around the map; this lets objects * + * properly exit the screen, since they have a blank undisplayed cell to * + * exit onto. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ Clear Terrain ³ * + * ³ ³ ³ Water ³ * + * ³ ³ ³ Tiberium ³ * + * ³ ³ ³ Rock/Wall/Road ³ * + * ³ ³ (Map Area) ³ GDI Unit ³ * + * ³ ³ ³ NOD Unit ³ * + * ³ ³ ³ Neutral Unit ³ * + * ³ ³ ³ Terrain Object ³ * + * ³ ³ ³ Starting Cell ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ³ * + * ³ X Y Width Height ³ * + * ³ ## ## ## ## ³ * + * ³ ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * x,y,w,h: initial size parameters (-1 = center the thing) * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Size_Map(int x, int y, int w, int h) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + enum { + D_DIALOG_W = 480, // dialog width + D_DIALOG_H = 280, // dialog height + D_DIALOG_X = ((640 - D_DIALOG_W) / 2), // centered x-coord + D_DIALOG_Y = ((400 - D_DIALOG_H) / 2), // centered y-coord + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), // coord of x-center + + D_TXT8_H = 22, // ht of 8-pt text + D_MARGIN = 14, // margin width/height + + D_BORD_X1 = D_DIALOG_X + (D_DIALOG_W / 2 - MAP_CELL_W) / 2, + D_BORD_Y1 = D_DIALOG_Y + 10, + D_BORD_X2 = D_BORD_X1 + MAP_CELL_W + 1, + D_BORD_Y2 = D_BORD_Y1 + MAP_CELL_H + 1, + + D_OK_W = 90, // OK width + D_OK_H = 18, // OK height + D_OK_X = D_DIALOG_CX - D_OK_W - 5, // OK x + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - D_MARGIN, // OK y + + D_CANCEL_W = 90, // Cancel width + D_CANCEL_H = 18, // Cancel height + D_CANCEL_X = D_DIALOG_CX + 5, // Cancel x + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_CANCEL_H - D_MARGIN, // Cancel y + + }; + /*........................................................................ + Button enumerations: + ........................................................................*/ + enum { + BUTTON_OK=100, + BUTTON_CANCEL, + }; + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_MAP, // includes map interior & coord values + REDRAW_BACKGROUND, // includes box, map bord, key, coord labels, btns + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + /*........................................................................ + Dialog variables: + ........................................................................*/ + bool process; // Loop while true + RedrawType display; // requested redraw level + bool cancel = false; // true = user cancels + KeyNumType input; // user input + int grabbed = 0; // 1=TLeft,2=TRight,3=BRight,4=BLeft + int map_x1; // map coords x1, pixel coords + int map_x2; // map coords x2, pixel coords + int map_y1; // map coords y1, pixel coords + int map_y2; // map coords y2, pixel coords + int delta1, delta2; // mouse-click proximity + int mx,my; // last-saved mouse coords + char txt[40]; + int txt_x,txt_y; // for displaying text + unsigned index; // for drawing map symbology + CELL cell; // for drawing map symbology + int color; // for drawing map symbology + ObjectClass * occupier; // cell's occupier + /*........................................................................ + Buttons + ........................................................................*/ + ControlClass *commands = NULL; + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + /*........................................................................ + Set up the actual map area relative to the map's border coords + ........................................................................*/ + if (x==-1) { + map_x1 = D_BORD_X1 + (MAP_CELL_W - w) / 2 + 1; + } else { + map_x1 = D_BORD_X1 + x + 1; + } + + if (y==-1) { + map_y1 = D_BORD_Y1 + (MAP_CELL_H - h) / 2 + 1; + } else { + map_y1 = D_BORD_Y1 + y + 1; + } + + map_x2 = map_x1 + w - 1; + map_y2 = map_y1 + h - 1; + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + + /*------------------------------------------------------------------------ + Main processing loop + ------------------------------------------------------------------------*/ + display = REDRAW_ALL; + process = true; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ------------------------ Invoke game callback ------------------------- + */ + Call_Back(); + + /* + ---------------------- Refresh display if needed ---------------------- + */ + if (display) { + Hide_Mouse(); + /*------------------------------------------------------------------ + Redraw the background, map border, key, and coord labels + ------------------------------------------------------------------*/ + if (display >= REDRAW_BACKGROUND) { + /* + .......................... Background ........................... + */ + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + /* + ..................... Draw the map border ....................... + */ + LogicPage->Lock(); + LogicPage->Draw_Rect(D_BORD_X1, D_BORD_Y1, D_BORD_X2, D_BORD_Y2, CC_GREEN_SHADOW); + for (index = D_BORD_X1; index < D_BORD_X2; + index += (320/ICON_PIXEL_W)) { + LogicPage->Put_Pixel(index, D_BORD_Y1-1, CC_GREEN_SHADOW); + LogicPage->Put_Pixel(index, D_BORD_Y2+1, CC_GREEN_SHADOW); + } + for (index = D_BORD_Y1; index < D_BORD_Y2-8; + index += (200/ICON_PIXEL_H)) { + LogicPage->Put_Pixel(D_BORD_X1-1, index, CC_GREEN_SHADOW); + LogicPage->Put_Pixel(D_BORD_X2+1, index, CC_GREEN_SHADOW); + } + + /*............................................................... + Draw the map "key" + ...............................................................*/ + txt_x = D_DIALOG_CX; + txt_y = D_DIALOG_Y + 8; + Fancy_Text_Print("Clear Terrain", txt_x, txt_y, LTGREY, TBLACK, TPF_DROPSHADOW | TPF_6POINT); + txt_y += 16; + Fancy_Text_Print("Water", txt_x, txt_y, BLUE, TBLACK, TPF_DROPSHADOW | TPF_6POINT); + txt_y += 16; + Fancy_Text_Print("Tiberium", txt_x, txt_y, GREY, TBLACK, TPF_DROPSHADOW | TPF_6POINT); + txt_y += 16; + Fancy_Text_Print("Rock/Wall/Road", txt_x, txt_y, BROWN, TBLACK, TPF_DROPSHADOW | TPF_6POINT); + txt_y += 16; + Fancy_Text_Print("GDI Unit", txt_x, txt_y, YELLOW, TBLACK, TPF_DROPSHADOW | TPF_6POINT); + txt_y += 16; + Fancy_Text_Print("Nod Unit", txt_x, txt_y, RED, TBLACK, TPF_DROPSHADOW | TPF_6POINT); + txt_y += 16; + Fancy_Text_Print("Neutral Unit", txt_x, txt_y, PURPLE, TBLACK, TPF_DROPSHADOW | TPF_6POINT); + txt_y += 16; + Fancy_Text_Print("Terrain Object", txt_x, txt_y, DKGREEN, TBLACK, TPF_DROPSHADOW | TPF_6POINT); + txt_y += 16; + Fancy_Text_Print("Starting Cell", txt_x, txt_y, WHITE, TBLACK, TPF_DROPSHADOW | TPF_6POINT); + /* + .................. Draw the coordinate labels ................... + */ + txt_x = D_DIALOG_X + D_DIALOG_W / 8; + txt_y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - 10 - 33; + Fancy_Text_Print("X", txt_x, txt_y, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + txt_x += (D_DIALOG_W - 20) / 4; + Fancy_Text_Print("Y", txt_x, txt_y, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + txt_x += (D_DIALOG_W - 20) / 4; + Fancy_Text_Print("Width", txt_x, txt_y, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + txt_x += (D_DIALOG_W - 20) / 4; + Fancy_Text_Print("Height", txt_x, txt_y, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + LogicPage->Unlock(); + + /* + ...................... Redraw the buttons ....................... + */ + commands->Flag_List_To_Redraw(); + + } + + /*------------------------------------------------------------------ + Redraw the map symbology & location + ------------------------------------------------------------------*/ + if (display >= REDRAW_MAP) { + + LogicPage->Lock(); + + /* + .................... Erase the map interior ..................... + */ + LogicPage->Fill_Rect(D_BORD_X1 + 1, D_BORD_Y1 + 1, D_BORD_X2 - 1, D_BORD_Y2 - 1, BLACK); + + /*............................................................... + Draw Land map symbols (use color according to Ground[] array). + ...............................................................*/ + for (cell=0; cell < MAP_CELL_TOTAL; cell++) { + occupier = (*this)[cell].Cell_Occupier(); + if (occupier == NULL) { + color = Ground[(*this)[cell].Land_Type()].Color; + LogicPage->Put_Pixel(D_BORD_X1 + Cell_X(cell) + 1, D_BORD_Y1 + Cell_Y(cell) + 1, color); + } + } + + LogicPage->Unlock(); + + /* + ................. Draw the actual map location .................. + */ + LogicPage->Draw_Rect(map_x1, map_y1, map_x2, map_y2, WHITE); + switch (grabbed) { + case 1: + LogicPage->Draw_Line(map_x1, map_y1, map_x1 + 5, map_y1, BLUE); + LogicPage->Draw_Line(map_x1, map_y1, map_x1, map_y1 + 5, BLUE); + break; + + case 2: + LogicPage->Draw_Line(map_x2, map_y1, map_x2 - 5, map_y1, BLUE); + LogicPage->Draw_Line(map_x2, map_y1, map_x2, map_y1 + 5, BLUE); + break; + + case 3: + LogicPage->Draw_Line(map_x2, map_y2, map_x2 - 5, map_y2, BLUE); + LogicPage->Draw_Line(map_x2, map_y2, map_x2, map_y2 - 5, BLUE); + break; + + case 4: + LogicPage->Draw_Line(map_x1, map_y2, map_x1 + 5, map_y2, BLUE); + LogicPage->Draw_Line(map_x1, map_y2, map_x1, map_y2 - 5, BLUE); + break; + + case 5: + LogicPage->Draw_Rect(map_x1, map_y1, map_x2, map_y2, BLUE); + break; + + default: + break; + } + + /*............................................................... + Draw Unit map symbols (Use the radar map color according to + that specified in the house type class object. + DKGREEN = terrain object + ...............................................................*/ + for (cell=0; cell < MAP_CELL_TOTAL; cell++) { + occupier = (*this)[cell].Cell_Occupier(); + if (occupier) { + color = DKGREEN; + if (occupier && occupier->Owner() != HOUSE_NONE) { + color = HouseClass::As_Pointer(occupier->Owner())->Class->Color; + } + LogicPage->Put_Pixel(D_BORD_X1 + Cell_X(cell) + 1, D_BORD_Y1 + Cell_Y(cell) + 1, color); + } + } + + /* + ...................... Draw Home location ....................... + */ + LogicPage->Put_Pixel(D_BORD_X1 + Cell_X(Waypoint[WAYPT_HOME]) + 1, D_BORD_Y1 + Cell_Y(Waypoint[WAYPT_HOME]) + 1, WHITE); + + /* + ..................... Erase old coordinates ..................... + */ + LogicPage->Fill_Rect( D_DIALOG_X + 7, + D_DIALOG_Y + D_DIALOG_H - D_OK_H - 10 - 22, + D_DIALOG_X + D_DIALOG_W - 7, + D_DIALOG_Y + D_DIALOG_H - D_OK_H - 10 - 22 + 10, BLACK); + + /* + ..................... Draw the coordinates ...................... + */ + txt_x = D_DIALOG_X + D_DIALOG_W / 8; + txt_y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - 10 - 22; + sprintf(txt,"%d",map_x1 - D_BORD_X1 - 1); + Fancy_Text_Print(txt, txt_x, txt_y, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + txt_x += (D_DIALOG_W - 20) / 4; + sprintf(txt,"%d",map_y1 - D_BORD_Y1 - 1); + Fancy_Text_Print(txt, txt_x, txt_y, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + txt_x += (D_DIALOG_W - 20) / 4; + sprintf(txt,"%d",map_x2 - map_x1 + 1); + Fancy_Text_Print(txt, txt_x, txt_y, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + txt_x += (D_DIALOG_W - 20) / 4; + sprintf(txt,"%d",map_y2 - map_y1 + 1); + Fancy_Text_Print(txt, txt_x, txt_y, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ------------------------- Process user input -------------------------- + */ + input = commands->Input(); + /*..................................................................... + Normal button processing: This is done when the mouse button is NOT + being held down ('grabbed' is 0). + .....................................................................*/ + if (grabbed == 0) { + switch (input) { + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + cancel = false; + process = false; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + case KN_LMOUSE: + /* + ....................... Grab top left ........................ + */ + delta1 = abs(_Kbd->MouseQX - map_x1); + delta2 = abs(_Kbd->MouseQY - map_y1); + if (delta1 < 3 && delta2 < 3) { + grabbed = 1; + mx = _Kbd->MouseQX; + my = _Kbd->MouseQY; + display = REDRAW_MAP; + break; + } + /* + ...................... Grab top right ........................ + */ + delta1 = abs(_Kbd->MouseQX - map_x2); + delta2 = abs(_Kbd->MouseQY - map_y1); + if (delta1 < 3 && delta2 < 3) { + grabbed = 2; + mx = _Kbd->MouseQX; + my = _Kbd->MouseQY; + display = REDRAW_MAP; + break; + } + /* + ..................... Grab bottom right ...................... + */ + delta1 = abs(_Kbd->MouseQX - map_x2); + delta2 = abs(_Kbd->MouseQY - map_y2); + if (delta1 < 3 && delta2 < 3) { + grabbed = 3; + mx = _Kbd->MouseQX; + my = _Kbd->MouseQY; + display = REDRAW_MAP; + break; + } + /* + ..................... Grab bottom left ....................... + */ + delta1 = abs(_Kbd->MouseQX - map_x1); + delta2 = abs(_Kbd->MouseQY - map_y2); + if (delta1 < 3 && delta2 < 3) { + grabbed = 4; + mx = _Kbd->MouseQX; + my = _Kbd->MouseQY; + display = REDRAW_MAP; + break; + } + /* + ..................... Grab the whole map ..................... + */ + delta1 = abs(_Kbd->MouseQX - ((map_x1 + map_x2) / 2)); + delta2 = abs(_Kbd->MouseQY - ((map_y1 + map_y2) / 2)); + if (delta1 < (map_x2 - map_x1) / 4 && + delta2 < (map_y2 - map_y1) / 4) { + grabbed = 5; + mx = _Kbd->MouseQX; + my = _Kbd->MouseQY; + display = REDRAW_MAP; + } + break; + + default: + break; + } + } else { + /*..................................................................... + Mouse motion processing: This is done while the left mouse button IS + being held down. + - First, check for the button release; if detected, un-grab + - Then, handle mouse motion. WWLIB doesn't pass through a KN_MOUSE_MOVE + value while the button is being held down, so this case must be + trapped as a default. + .....................................................................*/ + switch (input) { + case ((int)KN_LMOUSE | (int)KN_RLSE_BIT): + grabbed = 0; + display = REDRAW_MAP; + break; + + default: + delta1 = Get_Mouse_X() - mx; + delta2 = Get_Mouse_Y() - my; + if (delta1==0 && delta2==0) { + break; + } + + /* + ....................... Move top left ........................ + */ + if (grabbed==1) { + map_x1 += delta1; + if (map_x1 > map_x2 - 2) { + map_x1 = map_x2 - 2; + } else { + if (map_x1 < D_BORD_X1 + 2) { + map_x1 = D_BORD_X1 + 2; + } + } + + map_y1 += delta2; + if (map_y1 > map_y2 - 2) { + map_y1 = map_y2 - 2; + } else { + if (map_y1 < D_BORD_Y1 + 2) { + map_y1 = D_BORD_Y1 + 2; + } + } + display = REDRAW_MAP; + mx = Get_Mouse_X(); + my = Get_Mouse_Y(); + } + + /* + ....................... Move top right ....................... + */ + if (grabbed==2) { + map_x2 += delta1; + if (map_x2 < map_x1 + 2) { + map_x2 = map_x1 + 2; + } else { + if (map_x2 > D_BORD_X2 - 2) { + map_x2 = D_BORD_X2 - 2; + } + } + + map_y1 += delta2; + if (map_y1 > map_y2 - 2) { + map_y1 = map_y2 - 2; + } else { + if (map_y1 < D_BORD_Y1 + 2) { + map_y1 = D_BORD_Y1 + 2; + } + } + display = REDRAW_MAP; + mx = Get_Mouse_X(); + my = Get_Mouse_Y(); + } + + /* + ..................... Move bottom right ...................... + */ + if (grabbed==3) { + map_x2 += delta1; + if (map_x2 < map_x1 + 2) { + map_x2 = map_x1 + 2; + } else { + if (map_x2 > D_BORD_X2 - 2) { + map_x2 = D_BORD_X2 - 2; + } + } + + map_y2 += delta2; + if (map_y2 < map_y1 + 2) { + map_y2 = map_y1 + 2; + } else { + if (map_y2 > D_BORD_Y2 - 2) { + map_y2 = D_BORD_Y2 - 2; + } + } + display = REDRAW_MAP; + mx = Get_Mouse_X(); + my = Get_Mouse_Y(); + } + + /* + ...................... Move bottom left ...................... + */ + if (grabbed==4) { + map_x1 += delta1; + if (map_x1 > map_x2 - 2) { + map_x1 = map_x2 - 2; + } else { + if (map_x1 < D_BORD_X1 + 2) { + map_x1 = D_BORD_X1 + 2; + } + } + + map_y2 += delta2; + if (map_y2 < map_y1 + 2) { + map_y2 = map_y1 + 2; + } else { + if (map_y2 > D_BORD_Y2 - 2) { + map_y2 = D_BORD_Y2 - 2; + } + } + display = REDRAW_MAP; + mx = Get_Mouse_X(); + my = Get_Mouse_Y(); + } + + /* + ....................... Move whole map ....................... + */ + if (grabbed==5) { + if (map_x1 + delta1 > D_BORD_X1 + 1 && map_x2 + delta1 < D_BORD_X2 - 1) { + map_x1 += delta1; + map_x2 += delta1; + } + + if (map_y1 + delta2 > D_BORD_Y1 + 1 && map_y2 + delta2 < D_BORD_Y2 - 1) { + map_y1 += delta2; + map_y2 += delta2; + } + display = REDRAW_MAP; + mx = Get_Mouse_X(); + my = Get_Mouse_Y(); + } + break; + } + } + } + + /* + --------------------------- Redraw the display --------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ------------------------- If cancel, just return ------------------------- + */ + if (cancel) { + return(-1); + } + + /* + ---------------------------- Save selections ----------------------------- + */ + MapCellX = map_x1 - D_BORD_X1 - 1; + MapCellY = map_y1 - D_BORD_Y1 - 1; + MapCellWidth = map_x2 - map_x1 + 1; + MapCellHeight = map_y2 - map_y1 + 1; + + /* + --------------------- Clip Home Cell to new map size --------------------- + */ + if (Cell_X(Waypoint[WAYPT_HOME]) < MapCellX) { + Waypoint[WAYPT_HOME] = XY_Cell(MapCellX, Cell_Y(Waypoint[WAYPT_HOME])); + } + + if (Cell_X(Waypoint[WAYPT_HOME]) > MapCellX + MapCellWidth - 1) { + Waypoint[WAYPT_HOME] = XY_Cell(MapCellX + MapCellWidth - 1, Cell_Y(Waypoint[WAYPT_HOME])); + } + + if (Cell_Y(Waypoint[WAYPT_HOME]) < MapCellY) { + Waypoint[WAYPT_HOME] = XY_Cell(Cell_X(Waypoint[WAYPT_HOME]), MapCellY); + } + + if (Cell_Y(Waypoint[WAYPT_HOME]) > MapCellY + MapCellHeight - 1) { + Waypoint[WAYPT_HOME] = XY_Cell(Cell_X(Waypoint[WAYPT_HOME]), MapCellY + MapCellHeight - 1); + } + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Scenario_Dialog -- scenario global parameters dialog * + * * + * Lets the user edit the Theater, starting credits for houses, and the * + * Edge for HOUSE_GOOD & HOUSE_BAD. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Theater Credits / 1000 ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ Temperate ³ GDI: _____ ³ * + * ³ ³ Desert ³ NOD: _____ ³ * + * ³ ³ Jungle ³ Neutral: _____ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ Build Level:___ ³ * + * ³ ³ * + * ³ Reinforcements ³ * + * ³ ³ * + * ³ GDI NOD ³ * + * ³ ³ * + * ³   ³ * + * ³ <- -> <- -> ³ * + * ³   ³ * + * ³ ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * Uses HIDBUFF. * + * * + * HISTORY: * + * 11/14/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Scenario_Dialog(void) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + enum { + D_DIALOG_W = 544, + D_DIALOG_H = 320, + D_DIALOG_X = ((640 - D_DIALOG_W) / 2), + D_DIALOG_Y = ((400 - D_DIALOG_H) / 2), + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), + + D_TXT8_H = 22, + D_MARGIN = 14, + + D_THEATER_W = 200, + D_THEATER_H = 68, + D_THEATER_X = D_DIALOG_X + D_MARGIN, + D_THEATER_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H, + + D_LEVEL_W = 80, + D_LEVEL_H = 18, + D_LEVEL_X = D_THEATER_X + D_THEATER_W - D_LEVEL_W, + D_LEVEL_Y = D_THEATER_Y + D_THEATER_H + D_MARGIN, + + D_GDICRED_W = 120, + D_GDICRED_H = 18, + D_GDICRED_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_GDICRED_W, + D_GDICRED_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H, + + D_NODCRED_W = 120, + D_NODCRED_H = 18, + D_NODCRED_X = D_GDICRED_X, + D_NODCRED_Y = D_GDICRED_Y + D_GDICRED_H, + + D_NEUTCRED_W = 120, + D_NEUTCRED_H = 18, + D_NEUTCRED_X = D_GDICRED_X, + D_NEUTCRED_Y = D_NODCRED_Y + D_NODCRED_H, + + D_GDIN_W = 26, + D_GDIN_H = 18, + D_GDIN_X = D_DIALOG_CX - 5 - D_GDIN_W * 2, + D_GDIN_Y = D_LEVEL_Y + D_LEVEL_H + D_MARGIN + D_TXT8_H + D_MARGIN + D_TXT8_H, + + D_GDIS_W = 26, + D_GDIS_H = 18, + D_GDIS_X = D_GDIN_X, + D_GDIS_Y = D_GDIN_Y + D_GDIN_H * 2, + + D_GDIW_W = 26, + D_GDIW_H = 18, + D_GDIW_X = D_DIALOG_CX - 5 - D_GDIN_W * 3, + D_GDIW_Y = D_GDIN_Y + D_GDIN_H, + + D_GDIE_W = 26, + D_GDIE_H = 18, + D_GDIE_X = D_DIALOG_CX - 5 - D_GDIN_W, + D_GDIE_Y = D_GDIN_Y + D_GDIN_H, + + D_NODN_W = 26, + D_NODN_H = 18, + D_NODN_X = D_DIALOG_CX + 5 + D_NODN_W, + D_NODN_Y = D_LEVEL_Y + D_LEVEL_H + D_MARGIN + D_TXT8_H + D_MARGIN + D_TXT8_H, + + D_NODS_W = 26, + D_NODS_H = 18, + D_NODS_X = D_NODN_X, + D_NODS_Y = D_NODN_Y + D_NODN_H * 2, + + D_NODW_W = 26, + D_NODW_H = 18, + D_NODW_X = D_DIALOG_CX + 5, + D_NODW_Y = D_NODN_Y + D_NODN_H, + + D_NODE_W = 26, + D_NODE_H = 18, + D_NODE_X = D_DIALOG_CX + 5 + D_NODN_W * 2, + D_NODE_Y = D_NODN_Y + D_NODN_H, + + D_OK_W = 90, + D_OK_H = 18, + D_OK_X = D_DIALOG_CX - D_OK_W - 5, + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - D_MARGIN, + + D_CANCEL_W = 90, + D_CANCEL_H = 18, + D_CANCEL_X = D_DIALOG_CX + 5, + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_CANCEL_H - D_MARGIN, + }; + /*........................................................................ + Button enumerations: + ........................................................................*/ + enum { + LIST_THEATER=100, + TEDIT_LEVEL, + TEDIT_GDICRED, + TEDIT_NODCRED, + TEDIT_NEUTCRED, + BUTTON_GDI_N, + BUTTON_GDI_E, + BUTTON_GDI_S, + BUTTON_GDI_W, + BUTTON_NOD_N, + BUTTON_NOD_E, + BUTTON_NOD_S, + BUTTON_NOD_W, + BUTTON_OK, + BUTTON_CANCEL, + }; + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, // includes map interior & coord values + REDRAW_BACKGROUND, // includes box, map bord, key, coord labels, btns + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + /*........................................................................ + Dialog variables: + ........................................................................*/ + KeyNumType input; // input from user + bool process; // loop while true + RedrawType display; // true = re-draw everything + bool cancel = false; // true = user cancels + /* + .......................... Scenario parameters ........................... + */ + TheaterType theater; // DisplayClass::Theater + TheaterType orig_theater; // original theater + long gdi_credits; // HouseClass::As_Pointer(HouseType)->Credits + long nod_credits; // HouseClass::As_Pointer(HouseType)->Credits + long neut_credits; // HouseClass::As_Pointer(HouseType)->Credits + SourceType gdi_edge; // HouseClass::As_Pointer(HouseType)->Edge + SourceType nod_edge; // HouseClass::As_Pointer(HouseType)->Edge + char level_buf[10] = {0}; + char gdicred_buf[10] = {0}; + char nodcred_buf[10] = {0}; + char neutcred_buf[10] = {0}; + /* + ....................... Theater-changing variables ....................... + */ + unsigned char theater_mask; // template/terrain mask + TerrainClass * terrain; // cell's terrain pointer + CELL i; // loop counter + /*........................................................................ + Buttons + ........................................................................*/ + ControlClass *commands = NULL; // the button list + ListClass theaterbtn (LIST_THEATER, + D_THEATER_X, D_THEATER_Y, D_THEATER_W, D_THEATER_H, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + Hires_Retrieve("BTN-UP.SHP"), + Hires_Retrieve("BTN-DN.SHP")); + + EditClass leveledt (TEDIT_GDICRED, level_buf, 4, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_LEVEL_X, D_LEVEL_Y, D_LEVEL_W, D_LEVEL_H, EditClass::NUMERIC); + + EditClass gdicred (TEDIT_GDICRED, gdicred_buf, 8, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_GDICRED_X, D_GDICRED_Y, D_GDICRED_W, D_GDICRED_H, EditClass::NUMERIC); + + EditClass nodcred (TEDIT_NODCRED, nodcred_buf, 8, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NODCRED_X, D_NODCRED_Y, D_NODCRED_W, D_NODCRED_H, EditClass::NUMERIC); + + EditClass neutcred (TEDIT_NEUTCRED, neutcred_buf, 8, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NEUTCRED_X, D_NEUTCRED_Y, D_NEUTCRED_W, D_NEUTCRED_H, EditClass::NUMERIC); + + TextButtonClass gdinbtn (BUTTON_GDI_N, TXT_UP, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_GDIN_X, D_GDIN_Y, D_GDIN_W, D_GDIN_H); + + TextButtonClass gdiebtn (BUTTON_GDI_E, TXT_RIGHT, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_GDIE_X, D_GDIE_Y, D_GDIE_W, D_GDIE_H); + + TextButtonClass gdisbtn (BUTTON_GDI_S, TXT_DOWN, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_GDIS_X, D_GDIS_Y, D_GDIS_W, D_GDIS_H); + + TextButtonClass gdiwbtn (BUTTON_GDI_W, TXT_LEFT, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_GDIW_X, D_GDIW_Y, D_GDIW_W, D_GDIW_H); + + TextButtonClass nodnbtn (BUTTON_NOD_N, TXT_UP, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NODN_X, D_NODN_Y, D_NODN_W, D_NODN_H); + + TextButtonClass nodebtn (BUTTON_NOD_E, TXT_RIGHT, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NODE_X, D_NODE_Y, D_NODE_W, D_NODE_H); + + TextButtonClass nodsbtn (BUTTON_NOD_S, TXT_DOWN, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NODS_X, D_NODS_Y, D_NODS_W, D_NODS_H); + + TextButtonClass nodwbtn (BUTTON_NOD_W, TXT_LEFT, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NODW_X, D_NODW_Y, D_NODW_W, D_NODW_H); + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + /* + .......................... Fill in theater items ......................... + */ + theaterbtn.Add_Item("Desert"); + theaterbtn.Add_Item("Jungle"); + theaterbtn.Add_Item("Temperate"); + theaterbtn.Add_Item("Winter"); + + /* + ............................ Init parameters ............................. + */ + orig_theater = theater = Theater; + if (ScenPlayer != SCEN_PLAYER_MPLAYER) { + gdi_credits = HouseClass::As_Pointer(HOUSE_GOOD)->Credits / 1000L; + nod_credits = HouseClass::As_Pointer(HOUSE_BAD)->Credits / 1000L; + neut_credits = HouseClass::As_Pointer(HOUSE_NEUTRAL)->Credits / 1000L; + gdi_edge = HouseClass::As_Pointer(HOUSE_GOOD)->Edge; + nod_edge = HouseClass::As_Pointer(HOUSE_BAD)->Edge; + } else { + gdi_credits = 0; + nod_credits = 0; + neut_credits = 0; + gdi_edge = SOURCE_NONE; + nod_edge = SOURCE_NONE; + } + + /* + ............................ Create the list ............................. + */ + commands = &theaterbtn; + leveledt.Add_Tail(*commands); + gdicred.Add_Tail(*commands); + nodcred.Add_Tail(*commands); + neutcred.Add_Tail(*commands); + gdinbtn.Add_Tail(*commands); + gdiebtn.Add_Tail(*commands); + gdisbtn.Add_Tail(*commands); + gdiwbtn.Add_Tail(*commands); + nodnbtn.Add_Tail(*commands); + nodebtn.Add_Tail(*commands); + nodsbtn.Add_Tail(*commands); + nodwbtn.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + + /* + ...................... Init GDI Edge button states ....................... + */ + if (gdi_edge==SOURCE_NORTH) gdinbtn.Turn_On(); + if (gdi_edge==SOURCE_EAST) gdiebtn.Turn_On(); + if (gdi_edge==SOURCE_SOUTH) gdisbtn.Turn_On(); + if (gdi_edge==SOURCE_WEST) gdiwbtn.Turn_On(); + + /* + ...................... Init NOD Edge button states ....................... + */ + if (nod_edge==SOURCE_NORTH) nodnbtn.Turn_On(); + if (nod_edge==SOURCE_EAST) nodebtn.Turn_On(); + if (nod_edge==SOURCE_SOUTH) nodsbtn.Turn_On(); + if (nod_edge==SOURCE_WEST) nodwbtn.Turn_On(); + + /* + .......................... Init credits buffers .......................... + */ + sprintf(level_buf,"%ld",BuildLevel); + leveledt.Set_Text(level_buf,4); + + sprintf(gdicred_buf,"%ld",gdi_credits); + gdicred.Set_Text(gdicred_buf,8); + + sprintf(nodcred_buf,"%ld",nod_credits); + nodcred.Set_Text(nodcred_buf,8); + + sprintf(neutcred_buf,"%ld",neut_credits); + neutcred.Set_Text(neutcred_buf,8); + + theaterbtn.Set_Selected_Index(orig_theater - THEATER_NONE - 1); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + display = REDRAW_ALL; + process = true; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + + /* + ..................... Draw the background ....................... + */ + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + /* + ....................... Draw the labels ......................... + */ + Fancy_Text_Print("Theater", D_THEATER_X + D_THEATER_W / 2, + D_THEATER_Y - D_TXT8_H, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print("Build Level", D_LEVEL_X, D_LEVEL_Y, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print("Credits/1000", D_GDICRED_X + D_GDICRED_W / 2, + D_GDICRED_Y - D_TXT8_H, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print ("GDI", D_GDICRED_X - 5, D_GDICRED_Y, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print ("NOD", D_NODCRED_X - 5, D_NODCRED_Y, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print ("Neutral", D_NEUTCRED_X - 5, D_NEUTCRED_Y, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print ("Reinforcements", D_DIALOG_CX, + D_LEVEL_Y + D_LEVEL_H + D_MARGIN, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print ("GDI", D_GDIN_X + D_GDIN_W / 2, + D_GDIN_Y - D_TXT8_H, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print ("NOD", D_NODN_X + D_NODN_W / 2, + D_NODN_Y - D_TXT8_H, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + + /*.................................................................. + Credit edit boxes: no need for any action + ..................................................................*/ + case (TEDIT_GDICRED | KN_BUTTON): + break; + + case (TEDIT_NODCRED | KN_BUTTON): + break; + + case (TEDIT_NEUTCRED | KN_BUTTON): + break; + + /*.................................................................. + GDI Edge buttons: turn this one on, others off, save the edge value + ..................................................................*/ + case (BUTTON_GDI_N | KN_BUTTON): + gdi_edge = SOURCE_NORTH; + gdinbtn.Turn_On(); + gdiebtn.Turn_Off(); + gdisbtn.Turn_Off(); + gdiwbtn.Turn_Off(); + break; + + case (BUTTON_GDI_E | KN_BUTTON): + gdi_edge = SOURCE_EAST; + gdinbtn.Turn_Off(); + gdiebtn.Turn_On(); + gdisbtn.Turn_Off(); + gdiwbtn.Turn_Off(); + break; + + case (BUTTON_GDI_S | KN_BUTTON): + gdi_edge = SOURCE_SOUTH; + gdinbtn.Turn_Off(); + gdiebtn.Turn_Off(); + gdisbtn.Turn_On(); + gdiwbtn.Turn_Off(); + break; + + case (BUTTON_GDI_W | KN_BUTTON): + gdi_edge = SOURCE_WEST; + gdinbtn.Turn_Off(); + gdiebtn.Turn_Off(); + gdisbtn.Turn_Off(); + gdiwbtn.Turn_On(); + break; + + /*.................................................................. + NOD Edge buttons: turn this one on, others off, save the edge value + ..................................................................*/ + case (BUTTON_NOD_N | KN_BUTTON): + nod_edge = SOURCE_NORTH; + nodnbtn.Turn_On(); + nodebtn.Turn_Off(); + nodsbtn.Turn_Off(); + nodwbtn.Turn_Off(); + break; + + case (BUTTON_NOD_E | KN_BUTTON): + nod_edge = SOURCE_EAST; + nodnbtn.Turn_Off(); + nodebtn.Turn_On(); + nodsbtn.Turn_Off(); + nodwbtn.Turn_Off(); + break; + + case (BUTTON_NOD_S | KN_BUTTON): + nod_edge = SOURCE_SOUTH; + nodnbtn.Turn_Off(); + nodebtn.Turn_Off(); + nodsbtn.Turn_On(); + nodwbtn.Turn_Off(); + break; + + case (BUTTON_NOD_W | KN_BUTTON): + nod_edge = SOURCE_WEST; + nodnbtn.Turn_Off(); + nodebtn.Turn_Off(); + nodsbtn.Turn_Off(); + nodwbtn.Turn_On(); + break; + + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + cancel = false; + process = false; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + default: + break; + } + } + + /* + ----------------------------- Redraw the map ----------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ------------------------- If cancel, just return ------------------------- + */ + if (cancel) { + return(-1); + } + + /* + ------------------------ Save selections & return ------------------------ + */ + if (ScenPlayer != SCEN_PLAYER_MPLAYER) { + /* + .............................. Credits ................................ + */ + gdi_credits = atol(gdicred_buf); + nod_credits = atol(nodcred_buf); + neut_credits = atol(neutcred_buf); + HouseClass::As_Pointer(HOUSE_GOOD)->Credits = gdi_credits * 1000L; + HouseClass::As_Pointer(HOUSE_BAD)->Credits = nod_credits * 1000L; + HouseClass::As_Pointer(HOUSE_NEUTRAL)->Credits = neut_credits * 1000L; + /* + ............................... Edges ................................. + */ + HouseClass::As_Pointer(HOUSE_GOOD)->Edge = gdi_edge; + HouseClass::As_Pointer(HOUSE_BAD)->Edge = nod_edge; + } + + /* + ........................... Sidebar build level .......................... + */ + BuildLevel = atoi(level_buf); + + /*........................................................................ + Change the theater: + - 1st set the Theater global + - scan all cells to check their TType for compatibility with the new + theater; if not compatible, set TType to TEMPLATE_NONE & TIcon to 0 + - Then, re-initialize the TypeClasses for the new Theater + ........................................................................*/ + theater = (TheaterType)(THEATER_NONE + 1 + theaterbtn.Current_Index()); + if (theater != orig_theater) { + /* + ....................... Loop through all cells ........................ + */ + for (i =0;iClass->Theater; + if ( (theater_mask & (1<Remove(); + CurTrigger = NULL; + Changed = 1; + } + } + } + + /*------------------------------------------------------------------------ + Don't allow trigger placement if the trigger is house-specific; such + triggers cannot be "placed". + ------------------------------------------------------------------------*/ + if (CurTrigger) { + if (!TriggerClass::Event_Need_Object(CurTrigger->Event)) { + CurTrigger = NULL; + } + } +} + + +/*************************************************************************** + * MapEditClass::Select_Trigger -- lets user select a trigger * + * * + * CurTrigger can be NULL when this function is called. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Triggers ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³ Name Event Action House Team ³³ ³ * + * ³ ³ Name Event Action House Team ÃÄ´ ³ * + * ³ ³ Name Event Action House Team ³ ³ ³ * + * ³ ³ Name Event Action House Team ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ÃÄ´ ³ * + * ³ ³ ³³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ ³ * + * ³ [Edit] [New] [Delete] [OK] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, 1 = Edit, 2 = New, 3 = Delete * + * * + * WARNINGS: * + * Uses HIDBUFF. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Select_Trigger(void) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + enum { + D_DIALOG_W = 640, + D_DIALOG_H = 290, + D_DIALOG_X = ((640 - D_DIALOG_W) / 2), + D_DIALOG_Y = ((400 - D_DIALOG_H) / 2), + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), + + D_TXT8_H = 22, + D_MARGIN = 14, + + D_LIST_W = 612, + D_LIST_H = 208, + D_LIST_X = D_DIALOG_X + D_MARGIN, + D_LIST_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H, + + D_EDIT_W = 90, + D_EDIT_H = 18, + D_EDIT_X = D_DIALOG_X + (D_DIALOG_W / 8) - (D_EDIT_W / 2), + D_EDIT_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_EDIT_H, + + D_NEW_W = 90, + D_NEW_H = 18, + D_NEW_X = D_DIALOG_X + (D_DIALOG_W / 8) * 3 - (D_NEW_W / 2), + D_NEW_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_NEW_H, + + D_DELETE_W = 90, + D_DELETE_H = 18, + D_DELETE_X = D_DIALOG_X + (D_DIALOG_W / 8) * 5 - (D_DELETE_W / 2), + D_DELETE_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_DELETE_H, + + D_OK_W = 90, + D_OK_H = 18, + D_OK_X = D_DIALOG_X + (D_DIALOG_W / 8) * 7 - (D_OK_W / 2), + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_OK_H, + + }; + + /*........................................................................ + Button enumerations: + ........................................................................*/ + enum { + TRIGGER_LIST=100, + BUTTON_EDIT, + BUTTON_NEW, + BUTTON_DELETE, + BUTTON_OK, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables: + ........................................................................*/ + RedrawType display; // requested redraw level + bool process; // loop while true + char *trigtext[TRIGGER_MAX + 1]; // text for defined triggers + KeyNumType input; // user input + bool edit_trig = false; // true = user wants to edit + bool new_trig = false; // true = user wants to new + bool del_trig = false; // true = user wants to new + int i; // loop counter + int def_idx; // default list index + static int tabs[] = {70, 240, 390, 440}; // list box tab stops + + /*........................................................................ + Buttons + ........................................................................*/ + ControlClass *commands = NULL; // the button list + + ListClass triggerlist (TRIGGER_LIST, D_LIST_X, D_LIST_Y, D_LIST_W, D_LIST_H, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + Hires_Retrieve("BTN-UP.SHP"), + Hires_Retrieve("BTN-DN.SHP")); + + TextButtonClass editbtn (BUTTON_EDIT, "Edit", TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW, + D_EDIT_X, D_EDIT_Y, D_EDIT_W, D_EDIT_H); + + TextButtonClass newbtn (BUTTON_NEW, "New", TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW, + D_NEW_X, D_NEW_Y, D_NEW_W, D_NEW_H); + + TextButtonClass deletebtn (BUTTON_DELETE, "Delete", TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW, + D_DELETE_X, D_DELETE_Y, D_DELETE_W, D_DELETE_H); + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW, + D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + /* + ......................... Fill in trigger names .......................... + */ + def_idx = 0; + for (i = 0; i < Triggers.Count(); i++) { + /*..................................................................... + Generate string for this trigger + - Name can be up to 4 characters + - Event can be up to 15 characters + - Action can be up to 15 characters + - House is 3 characters + - Team name is up to 11 characters + .....................................................................*/ + //trigtext[i] = (char *)HidPage.Get_Graphic_Buffer()->Get_Buffer() + 60 * i; + trigtext[i] = new char[255]; + sprintf(trigtext[i],"%s\t%s\t%s\t", + Triggers.Ptr(i)->Get_Name(), + TriggerClass::Name_From_Event(Triggers.Ptr(i)->Event), + TriggerClass::Name_From_Action(Triggers.Ptr(i)->Action)); + + /* + ......................... Add on the house ID ......................... + */ + if (TriggerClass::Event_Need_House(Triggers.Ptr(i)->Event)) { + if (Triggers.Ptr(i)->House != HOUSE_NONE) { + strcat(trigtext[i], HouseTypeClass::As_Reference(Triggers.Ptr(i)->House).Suffix); + } else { + strcat(trigtext[i], "!!!"); + } + } else { + strcat(trigtext[i]," "); + } + + /* + .......................... Add the team name .......................... + */ + strcat(trigtext[i],"\t"); + if (TriggerClass::Action_Need_Team(Triggers.Ptr(i)->Action)) { + if (Triggers.Ptr(i)->Team) { + strcat(trigtext[i],Triggers.Ptr(i)->Team->IniName); + } else { + strcat(trigtext[i], "!!!"); + } + } + + /* + ................. Set def_idx if this is CurTrigger ................... + */ + if (Triggers.Ptr(i) == CurTrigger) { + def_idx = i; + } + } + + /* + .......................... Fill in the list box .......................... + */ + for (i = 0; i < Triggers.Count(); i++) { + triggerlist.Add_Item(trigtext[i]); + } + triggerlist.Set_Selected_Index(def_idx); + + /* + ....................... Set CurTrigger if it isn't ....................... + */ + if (Triggers.Count()==0) { + CurTrigger = NULL; + } else { + if (!CurTrigger) { + CurTrigger = Triggers.Ptr(def_idx); + } + } + + /* + ............................ Create the list ............................. + */ + commands = &triggerlist; + editbtn.Add_Tail(*commands); + newbtn.Add_Tail(*commands); + deletebtn.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + + /* + ------------------------ Init tab stops for list ------------------------- + */ + triggerlist.Set_Tabs(tabs); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + display = REDRAW_ALL; + process = true; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + + /* + ...................... Display the dialog box ...................... + */ + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + /* + ....................... Draw the captions ....................... + */ + Fancy_Text_Print("Triggers", D_DIALOG_CX, D_DIALOG_Y + D_MARGIN, CC_GREEN, TBLACK, + TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + } + + /* + ........................ Redraw the buttons ........................ + */ + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (TRIGGER_LIST | KN_BUTTON): + def_idx = triggerlist.Current_Index(); + if (def_idx < Triggers.Count()) { + CurTrigger = Triggers.Ptr(def_idx); + } + break; + + case (BUTTON_EDIT | KN_BUTTON): + if (CurTrigger) { // only allow if there's one selected + process = false; + edit_trig = true; + } + break; + + case (BUTTON_NEW | KN_BUTTON): + process = false; + new_trig = true; + break; + + case (BUTTON_DELETE | KN_BUTTON): + process = false; + del_trig = true; + break; + + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + } + } + + /* + --------------------------- Redraw the display --------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + for (i = 0; i < Triggers.Count(); i++) { + delete [] trigtext[i]; + } + + if (edit_trig) return(1); + if (new_trig) return(2); + if (del_trig) return(3); + return(0); +} + + +/*************************************************************************** + * MapEditClass::Edit_Trigger -- lets user edit a [new] trigger * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Trigger Editor ³ * + * ³ ³ * + * ³ Events Actions ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³ ³³ ³ ³³ ³ * + * ³ ³ ÃÄ´ ³ ÃÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ³ ÃÄ´ ³ ÃÄ´ ³ * + * ³ ³ ³³ ³ ³³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ ³ * + * ³ Name: _______ [ Volatile ] ³ * + * ³ [GDI] [ Persistent ] ³ * + * ³ Time / Credits: _______ [NOD] [SemiPersistent] ³ * + * ³ ³ * + * ³ [Team] Team_Name ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * CurTrigger must NOT be NULL when this function is called. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Edit_Trigger(void) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + enum { + D_DIALOG_W = 528, + D_DIALOG_H = 376, + D_DIALOG_X = ((640 - D_DIALOG_W) / 2), + D_DIALOG_Y = ((400 - D_DIALOG_H) / 2), + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), + + D_TXT8_H = 22, + D_MARGIN = 14, + + D_EVENT_W = 240, + D_EVENT_H = 88, + D_EVENT_X = D_DIALOG_X + D_MARGIN, + D_EVENT_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H + D_MARGIN + D_TXT8_H, + + D_ACTION_W = 240, + D_ACTION_H = 88, + D_ACTION_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_ACTION_W, + D_ACTION_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H + D_MARGIN + D_TXT8_H, + + D_NAME_W = 80, + D_NAME_H = 18, + D_NAME_X = D_EVENT_X + (D_EVENT_W / 2) - 10, + D_NAME_Y = D_EVENT_Y + D_EVENT_H + D_MARGIN, + + D_DATA_W = 80, + D_DATA_H = 18, + D_DATA_X = D_NAME_X, + D_DATA_Y = D_NAME_Y + D_NAME_H + D_MARGIN, + + D_TEAM_W = 80, + D_TEAM_H = 18, + D_TEAM_X = D_NAME_X - D_TEAM_W - 5, + D_TEAM_Y = D_DATA_Y + D_DATA_H + D_MARGIN, + + D_GDI_W = 90, + D_GDI_H = 18, + D_GDI_X = D_DIALOG_CX - (D_GDI_W / 2), + D_GDI_Y = D_NAME_Y, + + D_NOD_W = 90, + D_NOD_H = 18, + D_NOD_X = D_GDI_X, + D_NOD_Y = D_GDI_Y + D_GDI_H, + + D_NEU_W = 90, + D_NEU_H = 18, + D_NEU_X = D_NOD_X, + D_NEU_Y = D_NOD_Y + D_NOD_H, + + D_MULTI1_W = 44, + D_MULTI1_H = 18, + D_MULTI1_X = D_GDI_X, + D_MULTI1_Y = D_GDI_Y, + + D_MULTI2_W = 44, + D_MULTI2_H = 18, + D_MULTI2_X = D_GDI_X + D_MULTI1_W, + D_MULTI2_Y = D_GDI_Y, + + D_MULTI3_W = 44, + D_MULTI3_H = 18, + D_MULTI3_X = D_NOD_X, + D_MULTI3_Y = D_NOD_Y, + + D_MULTI4_W = 44, + D_MULTI4_H = 18, + D_MULTI4_X = D_NOD_X + D_MULTI1_W, + D_MULTI4_Y = D_NOD_Y, + + D_VOLATILE_W = 100, + D_VOLATILE_H = 18, + D_VOLATILE_X = D_ACTION_X + (D_ACTION_W / 2) - (D_VOLATILE_W / 2) + 10, + D_VOLATILE_Y = D_NAME_Y, + + D_PERSIST_W = 100, + D_PERSIST_H = 18, + D_PERSIST_X = D_ACTION_X + (D_ACTION_W / 2) - (D_PERSIST_W / 2) + 10, + D_PERSIST_Y = D_VOLATILE_Y + D_VOLATILE_H, + + D_SEMIPERSIST_W = 100, + D_SEMIPERSIST_H = 18, + D_SEMIPERSIST_X = D_ACTION_X + (D_ACTION_W / 2) - (D_SEMIPERSIST_W / 2) + 10, + D_SEMIPERSIST_Y = D_PERSIST_Y + D_PERSIST_H, + + D_OK_W = 90, + D_OK_H = 18, + D_OK_X = D_DIALOG_CX - 5 - D_OK_W, + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_OK_H, + + D_CANCEL_W = 90, + D_CANCEL_H = 18, + D_CANCEL_X = D_DIALOG_CX + 5, + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_CANCEL_H, + + }; + + /*........................................................................ + Button enumerations: + ........................................................................*/ + enum { + EVENT_LIST=100, + ACTION_LIST, + NAME_EDIT, + DATA_EDIT, + BUTTON_TEAM, + BUTTON_GDI, + BUTTON_NOD, + BUTTON_NEUTRAL, + BUTTON_JP, // placeholder + BUTTON_MULTI1, + BUTTON_MULTI2, + BUTTON_MULTI3, + BUTTON_MULTI4, + BUTTON_MULTI5, + BUTTON_MULTI6, + BUTTON_VOLATILE, + BUTTON_PERSIST, + BUTTON_SEMIPERSIST, + BUTTON_OK, + BUTTON_CANCEL, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables: + ........................................................................*/ + RedrawType display; // requested redraw level + bool process; // loop while true + KeyNumType input; // user input + bool cancel = false; // true = user cancels + int i; // loop counter + EventType event_idx; // index for event list + TriggerClass::ActionType action_idx; // index for action list + char namebuf[5]; // name of this trigger + char databuf[10]; // for credit/time-based triggers + HousesType house; // house for this trigger + const char *eventnames[EVENT_COUNT + 1]; // names of events + const char *actionnames[TriggerClass::ACTION_COUNT + 1]; // names of actions + TriggerClass::PersistantType persistant; // trigger's persistence level + + /*........................................................................ + These flags enable various controls for each EventType. + ........................................................................*/ +// static char data_enabled[EVENT_COUNT] = {0,0,0,0,0,0,0,0,0,1,1,1,1,0,0}; +// static char house_enabled[EVENT_COUNT] = {1,0,0,0,0,1,1,1,1,1,1,1,1,1,1}; +// static char team_enabled[TriggerClass::ACTION_COUNT] = {0,0,0,1,1,0,1,0,0,0,0,0,0,0}; + + /*........................................................................ + Buttons + ........................................................................*/ + ControlClass *commands = NULL; // the button list + + ListClass eventlist(EVENT_LIST, + D_EVENT_X, D_EVENT_Y, D_EVENT_W, D_EVENT_H, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + Hires_Retrieve("BTN-UP.SHP"), + Hires_Retrieve("BTN-DN.SHP")); + + ListClass actionlist(ACTION_LIST, + D_ACTION_X, D_ACTION_Y, D_ACTION_W, D_ACTION_H, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + Hires_Retrieve("BTN-UP.SHP"), + Hires_Retrieve("BTN-DN.SHP")); + + EditClass name_edt(NAME_EDIT, namebuf, 5, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NAME_X, D_NAME_Y, D_NAME_W, D_NAME_H, EditClass::ALPHANUMERIC); + + EditClass data_edt(DATA_EDIT, databuf, 8, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_DATA_X, D_DATA_Y, D_DATA_W, D_DATA_H, EditClass::ALPHANUMERIC); + + TextButtonClass teambtn(BUTTON_TEAM, "Team", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_TEAM_X, D_TEAM_Y, D_TEAM_W, D_TEAM_H); + + TextButtonClass gdibtn(BUTTON_GDI, "GDI", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_GDI_X, D_GDI_Y, D_GDI_W, D_GDI_H); + + TextButtonClass nodbtn(BUTTON_NOD, "NOD", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NOD_X, D_NOD_Y, D_NOD_W, D_NOD_H); + + TextButtonClass neutralbtn(BUTTON_NEUTRAL, "Neutral", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NEU_X, D_NEU_Y, D_NEU_W, D_NEU_H); + + TextButtonClass multi1btn(BUTTON_MULTI1, "M1", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI1_X, D_MULTI1_Y, D_MULTI1_W, D_MULTI1_H); + + TextButtonClass multi2btn(BUTTON_MULTI2, "M2", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI2_X, D_MULTI2_Y, D_MULTI2_W, D_MULTI2_H); + + TextButtonClass multi3btn(BUTTON_MULTI3, "M3", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI3_X, D_MULTI3_Y, D_MULTI3_W, D_MULTI3_H); + + TextButtonClass multi4btn(BUTTON_MULTI4, "M4", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI4_X, D_MULTI4_Y, D_MULTI4_W, D_MULTI4_H); + + TextButtonClass volatilebtn(BUTTON_VOLATILE, "Volatile", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_VOLATILE_X, D_VOLATILE_Y, D_VOLATILE_W, D_VOLATILE_H); + + TextButtonClass persistbtn(BUTTON_PERSIST, "Persistant", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_PERSIST_X, D_PERSIST_Y, D_PERSIST_W, D_PERSIST_H); + + TextButtonClass semipersistbtn(BUTTON_SEMIPERSIST, "SemiPersistant", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_SEMIPERSIST_X, D_SEMIPERSIST_Y, D_SEMIPERSIST_W, D_SEMIPERSIST_H); + + TextButtonClass okbtn(BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + /* + ....................... Set default button states ........................ + */ + event_idx = CurTrigger->Event; // event list + if (event_idx == EVENT_NONE) event_idx = EVENT_FIRST; + + action_idx = CurTrigger->Action; // action list + if (action_idx == TriggerClass::ACTION_NONE) action_idx = TriggerClass::ACTION_FIRST; + + strcpy(namebuf,CurTrigger->Get_Name()); // Name + name_edt.Set_Text(namebuf,5); + + if (TriggerClass::Event_Need_Data(event_idx)) { + sprintf(databuf,"%ld",CurTrigger->Data); // Credits/Time + data_edt.Set_Text(databuf,8); + } + + house = CurTrigger->House; // House + + persistant = CurTrigger->IsPersistant; + + volatilebtn.Turn_Off(); + persistbtn.Turn_Off(); + semipersistbtn.Turn_Off(); + switch (CurTrigger->IsPersistant) { + case TriggerClass::VOLATILE: + volatilebtn.Turn_On(); + break; + + case TriggerClass::SEMIPERSISTANT: + semipersistbtn.Turn_On(); + break; + + case TriggerClass::PERSISTANT: + persistbtn.Turn_On(); + break; + } + + /* + ......................... Fill in the list boxes ......................... + */ + for (i = 0; i < EVENT_COUNT; i++) { + eventnames[i] = TriggerClass::Name_From_Event( (EventType)i); + eventlist.Add_Item(eventnames[i]); + } + eventlist.Set_Selected_Index(event_idx); + + for (i = 0; i < TriggerClass::ACTION_COUNT; i++) { + actionnames[i] = TriggerClass::Name_From_Action( (TriggerClass::ActionType)i); + actionlist.Add_Item(actionnames[i]); + } + actionlist.Set_Selected_Index(action_idx); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + display = REDRAW_ALL; + process = true; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + + /* + ...................... Display the dialog box ...................... + */ + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + /* + ....................... Draw the captions ....................... + */ + Fancy_Text_Print("Trigger Editor", D_DIALOG_CX, D_DIALOG_Y + D_MARGIN, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print("Events", D_EVENT_X + D_EVENT_W / 2, + D_EVENT_Y - D_TXT8_H, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print("Actions", D_ACTION_X + D_ACTION_W / 2, + D_ACTION_Y - D_TXT8_H, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print("Name", D_NAME_X - 5, D_NAME_Y, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if ((EventType)event_idx==EVENT_CREDITS) { // use 'Data' for Credits + Fancy_Text_Print("Credits", D_DATA_X - 5, D_DATA_Y, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + } else { + if ((EventType)event_idx==EVENT_TIME) { // use 'Data' for Time + Fancy_Text_Print("1/10 Min", D_DATA_X - 5, D_DATA_Y, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + } + + if (TriggerClass::Action_Need_Team(action_idx)) { + if (CurTrigger->Team) { + Fancy_Text_Print(CurTrigger->Team->IniName, + D_TEAM_X + D_TEAM_W + 5, D_TEAM_Y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } else { + Fancy_Text_Print( "!!!", + D_TEAM_X + D_TEAM_W + 5, D_TEAM_Y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + } + } + + /* + ..................... Rebuild the button list ...................... + */ + eventlist.Zap(); + actionlist.Zap(); + name_edt.Zap(); + data_edt.Zap(); + teambtn.Zap(); + gdibtn.Zap(); + nodbtn.Zap(); + neutralbtn.Zap(); + volatilebtn.Zap(); + persistbtn.Zap(); + semipersistbtn.Zap(); + okbtn.Zap(); + cancelbtn.Zap(); + + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + eventlist.Add_Tail(*commands); + actionlist.Add_Tail(*commands); + name_edt.Add_Tail(*commands); + volatilebtn.Add_Tail(*commands); + persistbtn.Add_Tail(*commands); + semipersistbtn.Add_Tail(*commands); + if (TriggerClass::Event_Need_Data(event_idx)) { + data_edt.Add_Tail(*commands); + sprintf(databuf,"%ld",CurTrigger->Data); + data_edt.Set_Text(databuf,8); + } + if (TriggerClass::Event_Need_House(event_idx)) { + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); + neutralbtn.Add_Tail(*commands); + Set_House_Buttons(house, commands, BUTTON_GDI); + } + if (TriggerClass::Action_Need_Team(action_idx)) teambtn.Add_Tail(*commands); + + /* + ........................ Redraw the buttons ........................ + */ + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (EVENT_LIST | KN_BUTTON): + if (eventlist.Current_Index() != event_idx) { + event_idx = EventType(eventlist.Current_Index()); + databuf[0] = 0; + CurTrigger->Data = 0; + if (!TriggerClass::Event_Need_House(event_idx)) { + CurTrigger->House = HOUSE_NONE; + } + display = REDRAW_ALL; + } + break; + + case (ACTION_LIST | KN_BUTTON): + if (actionlist.Current_Index() != action_idx) { + action_idx = TriggerClass::ActionType(actionlist.Current_Index()); + display = REDRAW_ALL; + } + break; + + case (NAME_EDIT | KN_BUTTON): + break; + + case (DATA_EDIT | KN_BUTTON): + break; + + case (BUTTON_GDI | KN_BUTTON): + case (BUTTON_NOD | KN_BUTTON): + case (BUTTON_NEUTRAL | KN_BUTTON): + case (BUTTON_MULTI1 | KN_BUTTON): + case (BUTTON_MULTI2 | KN_BUTTON): + case (BUTTON_MULTI3 | KN_BUTTON): + case (BUTTON_MULTI4 | KN_BUTTON): + case (BUTTON_MULTI5 | KN_BUTTON): + case (BUTTON_MULTI6 | KN_BUTTON): + house = (HousesType)( (input & (~KN_BUTTON)) - BUTTON_GDI); + Set_House_Buttons(house, commands, BUTTON_GDI); + break; + + case (BUTTON_TEAM | KN_BUTTON): + Handle_Teams("Select a Team"); + if (CurTeam) { + CurTrigger->Team = CurTeam; + } + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + display = REDRAW_ALL; + break; + + case (BUTTON_VOLATILE | KN_BUTTON): + persistant = TriggerClass::VOLATILE; + volatilebtn.Turn_On(); + persistbtn.Turn_Off(); + semipersistbtn.Turn_Off(); + break; + + case (BUTTON_PERSIST | KN_BUTTON): + persistant = TriggerClass::PERSISTANT; + volatilebtn.Turn_Off(); + persistbtn.Turn_On(); + semipersistbtn.Turn_Off(); + break; + + case (BUTTON_SEMIPERSIST | KN_BUTTON): + persistant = TriggerClass::SEMIPERSISTANT; + volatilebtn.Turn_Off(); + persistbtn.Turn_Off(); + semipersistbtn.Turn_On(); + break; + + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + default: + break; + } + } + + /* + ------------------------------ Save values ------------------------------- + */ + if (!cancel) { + + /* + .......................... Get list indices ........................... + */ + event_idx = EventType(eventlist.Current_Index()); + action_idx = TriggerClass::ActionType(actionlist.Current_Index()); + + /* + ......................... Set Event & Action .......................... + */ + CurTrigger->Event = EventType(event_idx); + CurTrigger->Action = TriggerClass::ActionType(action_idx); + + /* + .............................. Set name ............................... + */ + if (strlen(namebuf)==0) { + CurTrigger->Set_Name("____"); + } else { + CurTrigger->Set_Name(namebuf); + } + + /* + .............................. Set Data ............................... + */ + if (TriggerClass::Event_Need_Data(event_idx)) { + CurTrigger->Data = atol(databuf); + } + + /* + .............................. Set House .............................. + */ + if (TriggerClass::Event_Need_House(event_idx)) { + CurTrigger->House = house; + } else { + CurTrigger->House = HOUSE_NONE; + } + + /* + ........................... Set Persistence .......................... + */ + CurTrigger->IsPersistant = persistant; + } + + /* + --------------------------- Redraw the display --------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + if (cancel) { + return(-1); + } else { + return(0); + } +} + + +/*************************************************************************** + * MapEditClass::Import_Triggers -- lets user import triggers * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Triggers ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³x Name Event Action House ³³ ³ * + * ³ ³ Name Event Action House ÃÄ´ ³ * + * ³ ³x Name Event Action House ³ ³ ³ * + * ³ ³ Name Event Action House ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ÃÄ´ ³ * + * ³ ³ ³³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = user cancelled * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/29/1995 BRR : Created. * + *=========================================================================*/ +int MapEditClass::Import_Triggers(void) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + enum { + D_DIALOG_W = 480, + D_DIALOG_H = 290, + D_DIALOG_X = ((640 - D_DIALOG_W) / 2), + D_DIALOG_Y = ((400 - D_DIALOG_H) / 2), + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), + + D_TXT8_H = 22, + D_MARGIN = 14, + + D_LIST_W = 452, + D_LIST_H = 208, + D_LIST_X = D_DIALOG_X + D_MARGIN, + D_LIST_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H, + + D_OK_W = 90, + D_OK_H = 18, + D_OK_X = D_DIALOG_CX - D_OK_W - 5, + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_OK_H, + + D_CANCEL_W = 90, + D_CANCEL_H = 18, + D_CANCEL_X = D_DIALOG_CX + 5, + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_OK_H, + + }; + /*........................................................................ + Button enumerations: + ........................................................................*/ + enum { + TRIGGER_LIST=100, + BUTTON_OK, + BUTTON_CANCEL, + }; + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + /*........................................................................ + Dialog variables: + ........................................................................*/ + RedrawType display; // requested redraw level + bool process; // loop while true + KeyNumType input; // user input + bool cancel = false; + static int tabs[] = + {70, 220, 370, 420}; // list box tab stops + DynamicVectorClass trignames; // list of INI trigger names + char *inibuf; // working INI buffer + CCFileClass file; // file for reading the INI file + char buf[128]; // for reading an INI entry + char *tbuffer; // Accumulation buffer of trigger IDs. + int len; // Length of data in buffer. + TriggerClass *trigger; // Working trigger pointer. + char *item; // for adding to list box + char *eventptr; + char *actionptr; + char *houseptr; + int i; + /*........................................................................ + Buttons + ........................................................................*/ + ControlClass *commands = NULL; // the button list + + CheckListClass triggerlist (TRIGGER_LIST, + D_LIST_X, D_LIST_Y, D_LIST_W, D_LIST_H, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + Hires_Retrieve("BTN-UP.SHP"), + Hires_Retrieve("BTN-DN.SHP")); + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + Set_Logic_Page(SeenBuff); + + /*------------------------------------------------------------------------ + Read the MASTER.INI file + ------------------------------------------------------------------------*/ + /*........................................................................ + Read the file into the staging buffer + ........................................................................*/ + inibuf = new char [30000]; + memset(inibuf, '\0', 30000); + file.Set_Name("MASTER.INI"); + if (!file.Is_Available()) { + file.Close(); + delete [] inibuf; + return(-1); + } else { + file.Read(inibuf, 30000 - 1); + } + file.Close(); + + /*........................................................................ + Read all entry names in the Triggers section into a temp buffer + ........................................................................*/ + len = strlen(inibuf) + 2; + tbuffer = inibuf + len; + WWGetPrivateProfileString(TriggerClass::INI_Name(), NULL, NULL, tbuffer, + 30000 - len, inibuf); + + /*........................................................................ + For each entry in the INI section: + - Get the entry + - Generate a string describing the trigger + - Add that string to the list box + - Add a ptr to the INI entry name to our 'trignames' list + ........................................................................*/ + while (*tbuffer != '\0') { + WWGetPrivateProfileString(TriggerClass::INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, inibuf); + item = new char [60]; + + /* + ** Parse the INI entry + */ + eventptr = strtok(buf,","); + actionptr = strtok(NULL,","); + strtok(NULL,","); + houseptr = strtok(NULL,","); + + /* + ** Generate the descriptive string + */ + sprintf(item, " %s\t%s\t%s\t", tbuffer, eventptr, actionptr); + + /* + ** Add house name if needed + */ + if (TriggerClass::Event_Need_House(TriggerClass::Event_From_Name(eventptr))) { + HousesType house = HouseTypeClass::From_Name(houseptr); + if (house != HOUSE_NONE) { + strcat(item, HouseTypeClass::As_Reference(house).Suffix); + } else { + strcat(item, "!!!"); + } + } else { + strcat(item," "); + } + + /* + ** Add the item to the list box + */ + triggerlist.Add_Item(item); + + /* + ** Add the name to our internal name list + */ + trignames.Add(tbuffer); + + tbuffer += strlen(tbuffer)+1; + } + + /* + ............................ Create the list ............................. + */ + commands = &triggerlist; + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + + /* + ------------------------ Init tab stops for list ------------------------- + */ + triggerlist.Set_Tabs(tabs); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + display = REDRAW_ALL; + process = true; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + /* + ...................... Display the dialog box ...................... + */ + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + /* + ....................... Draw the captions ....................... + */ + Fancy_Text_Print("Import Triggers", D_DIALOG_CX, D_DIALOG_Y + D_MARGIN, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + /* + ........................ Redraw the buttons ........................ + */ + if (display >= REDRAW_BUTTONS) + commands->Flag_List_To_Redraw(); + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (TRIGGER_LIST | KN_BUTTON): + break; + + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + } + } + + /* + --------------------------- Redraw the display --------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /*........................................................................ + Re-parse the INI section; if any item is checked in the list box, create + that trigger for this scenario. + ........................................................................*/ + if (!cancel) { + tbuffer = inibuf + len; + i = 0; + while (*tbuffer != '\0') { + + /* + ** If this item is checked on the list, create a new trigger + ** and fill it in. + */ + if (triggerlist.Is_Checked(i)) { + WWGetPrivateProfileString(TriggerClass::INI_Name(), tbuffer, NULL, + buf, sizeof(buf)-1, inibuf); + + trigger = new TriggerClass(); + trigger->Fill_In(tbuffer, buf); + + if (trigger->House != HOUSE_NONE) + HouseTriggers[trigger->House].Add(trigger); + } + + tbuffer += strlen(tbuffer)+1; + i++; + } + } + + + /*........................................................................ + Clean up memory + ........................................................................*/ + trignames.Clear(); + while (triggerlist.Count()) { + item = (char *)triggerlist.Get_Item(0); + triggerlist.Remove_Item(item); + delete [] item; + } + delete [] inibuf; + + if (cancel) { + return(-1); + } else { + return(0); + } +} + + +/*************************************************************************** + * MapEditClass::Import_Teams -- lets the user import teams * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Teams ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³ Name House Class:Count,Class:Count ³³ ³ * + * ³ ³ Name House Class:Count,Class:Count ÃÄ´ ³ * + * ³ ³ Name House Class:Count,Class:Count ³ ³ ³ * + * ³ ³ Name House Class:Count,Class:Count ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ÃÄ´ ³ * + * ³ ³ ³³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = user cancelled * + * * + * WARNINGS: * + * Uses HIDBUFF. * + * * + * HISTORY: * + * 12/08/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Import_Teams(void) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + enum { + D_DIALOG_W = 528, + D_DIALOG_H = 290, + D_DIALOG_X = ((640 - D_DIALOG_W) / 2), + D_DIALOG_Y = ((400 - D_DIALOG_H) / 2), + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), + + D_TXT8_H = 22, + D_MARGIN = 14, + + D_LIST_W = 500, + D_LIST_H = 208, + D_LIST_X = D_DIALOG_X + D_MARGIN, + D_LIST_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H, + + D_OK_W = 90, + D_OK_H = 18, + D_OK_X = D_DIALOG_CX - D_OK_W - 5, + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_OK_H, + + D_CANCEL_W = 90, + D_CANCEL_H = 18, + D_CANCEL_X = D_DIALOG_CX + 5, + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_OK_H, + + TEAMTXT_LEN = 43, // max length of a team entry + }; + /*........................................................................ + Button enumerations: + ........................................................................*/ + enum { + TEAM_LIST=100, + BUTTON_OK, + BUTTON_CANCEL, + }; + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + /*........................................................................ + Dialog variables: + ........................................................................*/ + RedrawType display; // requested redraw level + bool process; // loop while true + KeyNumType input; // user input + bool cancel = false; + static int tabs[] = {120, 180}; // list box tab stops + DynamicVectorClass teamnames; // list of INI team names + char *inibuf; // working INI buffer + CCFileClass file; // file for reading the INI file + char buf[128]; // for reading an INI entry + char *tbuffer; // Accumulation buffer of team IDs. + int len; // Length of data in buffer. + TeamTypeClass *team; // Working team pointer. + char *item; // for adding to list box + char *houseptr; + char *classptr; + int numclasses; + int i; + /*........................................................................ + Buttons + ........................................................................*/ + ControlClass *commands = NULL; // the button list + + CheckListClass teamlist (TEAM_LIST, + D_LIST_X, D_LIST_Y, D_LIST_W, D_LIST_H, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + Hires_Retrieve("BTN-UP.SHP"), + Hires_Retrieve("BTN-DN.SHP")); + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + Set_Logic_Page(SeenBuff); + + /*------------------------------------------------------------------------ + Read the MASTER.INI file + ------------------------------------------------------------------------*/ + /*........................................................................ + Read the file into the staging buffer + ........................................................................*/ + inibuf = new char [30000]; + memset(inibuf, '\0', 30000); + file.Set_Name("MASTER.INI"); + if (!file.Is_Available()) { + file.Close(); + delete [] inibuf; + return(-1); + } else { + file.Read(inibuf, 30000 - 1); + } + + file.Close(); + /*........................................................................ + Read all entry names in the TeamTypes section into a temp buffer + ........................................................................*/ + len = strlen(inibuf) + 2; + tbuffer = inibuf + len; + WWGetPrivateProfileString(TeamTypeClass::INI_Name(), NULL, NULL, tbuffer, + 30000 - len, inibuf); + + /*........................................................................ + For each entry in the INI section: + - Get the entry + - Generate a string describing the team + - Add that string to the list box + - Add a ptr to the INI entry name to our 'teamnames' list + ........................................................................*/ + while (*tbuffer != '\0') { + WWGetPrivateProfileString(TeamTypeClass::INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, inibuf); + item = new char [60]; + + /* + ** Parse the INI entry + */ + houseptr = strtok(buf,","); + for (i = 0; i < 9; i++) { + strtok(NULL,","); + } + numclasses = atoi(strtok(NULL,",")); + + /* + ** Generate the descriptive string + */ + sprintf(item," %s\t",tbuffer); + HousesType house = HouseTypeClass::From_Name(houseptr); + if (house != HOUSE_NONE) { + strcat(item, HouseTypeClass::As_Reference(house).Suffix); + } else { + strcat(item, "!!!"); + } + strcat(item, "\t"); + + classptr = strtok(NULL,","); + for (i = 0; i < numclasses; i++) { + if (strlen(item) + strlen(classptr) < 60) { + strcat(item,classptr); + classptr = strtok(NULL,","); + } else { + break; + } + } + + /* + ** Add the item to the list box + */ + teamlist.Add_Item(item); + /* + ** Add the name to our internal name list + */ + teamnames.Add(tbuffer); + + tbuffer += strlen(tbuffer)+1; + } + + /* + ............................ Create the list ............................. + */ + commands = &teamlist; + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + + /* + ------------------------ Init tab stops for list ------------------------- + */ + teamlist.Set_Tabs(tabs); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + display = REDRAW_ALL; + process = true; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + /* + ...................... Display the dialog box ...................... + */ + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + /* + ....................... Draw the captions ....................... + */ + Fancy_Text_Print("Import Teams", D_DIALOG_CX, D_DIALOG_Y + D_MARGIN, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + /* + ........................ Redraw the buttons ........................ + */ + if (display >= REDRAW_BUTTONS) + commands->Flag_List_To_Redraw(); + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (TEAM_LIST | KN_BUTTON): + break; + + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + } + } + + /* + --------------------------- Redraw the display --------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /*........................................................................ + Re-parse the INI section; if any item is checked in the list box, create + that team for this scenario. + ........................................................................*/ + if (!cancel) { + tbuffer = inibuf + len; + i = 0; + while (*tbuffer != '\0') { + /* + ** If this item is checked on the list, create a new team + ** and fill it in. + */ + if (teamlist.Is_Checked(i)) { + WWGetPrivateProfileString(TeamTypeClass::INI_Name(), tbuffer, NULL, + buf, sizeof(buf)-1, inibuf); + + team = new TeamTypeClass(); + team->Fill_In(tbuffer,buf); + } + + tbuffer += strlen(tbuffer)+1; + i++; + } + } + + /*........................................................................ + Clean up memory + ........................................................................*/ + teamnames.Clear(); + while (teamlist.Count()) { + item = (char *)teamlist.Get_Item(0); + teamlist.Remove_Item(item); + delete [] item; + } + delete [] inibuf; + + if (cancel) { + return(-1); + } else { + return(0); + } +} + +#endif diff --git a/MAPEDIT.CPP b/MAPEDIT.CPP new file mode 100644 index 0000000..d6c40a1 --- /dev/null +++ b/MAPEDIT.CPP @@ -0,0 +1,1924 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\mapedit.cpv 2.18 16 Oct 1995 16:48:40 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPEDIT.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : October 20, 1994 * + * * + * Last Update : February 2, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * Map Editor overloaded routines & utility routines * + *-------------------------------------------------------------------------* + * Map Editor modules: * + * (Yes, they're all one huge class.) * + * mapedit.cpp: overloaded routines, utility routines * + * mapeddlg.cpp: map editor dialogs, most of the main menu options * + * mapedplc.cpp: object-placing routines * + * mapedsel.cpp: object-selection & manipulation routines * + * mapedtm.cpp: team-editing routines * + *-------------------------------------------------------------------------* + * Functions: * + * MapEditClass::MapEditClass -- class constructor * + * MapEditClass::One_Time -- one-time initialization * + * MapEditClass::Read_INI -- overloaded Read_INI function * + * MapEditClass::Clear_List -- clears the internal choosable object list * + * MapEditClass::Add_To_List -- adds a TypeClass to the choosable list * + * MapEditClass::AI -- The map editor's main logic * + * MapEditClass::Draw_It -- overloaded Redraw routine * + * MapEditClass::Main_Menu -- main menu processor for map editor * + * MapEditClass::AI_Menu -- menu of AI options * + * MapEditClass::Mouse_Moved -- checks for mouse motion * + * MapEditClass::Verify_House -- sees if given house can own given obj * + * MapEditClass::Cycle_House -- finds next valid house for object type * + * MapEditClass::Trigger_Needs_Team -- tells if a trigger needs a team * + * MapEditClass::Fatal -- exits with error message * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef SCENARIO_EDITOR + +/* +****************************** Globals/Externs ****************************** +*/ +/*........................................................................... +Array of all missions supported by the map editor +...........................................................................*/ +MissionType MapEditClass::MapEditMissions[] = { + MISSION_GUARD, + MISSION_STICKY, + MISSION_HARVEST, + MISSION_GUARD_AREA, + MISSION_RETURN, + MISSION_AMBUSH, + MISSION_HUNT, + MISSION_SLEEP, +}; +#define NUM_EDIT_MISSIONS (sizeof(MapEditClass::MapEditMissions) / sizeof(MapEditClass::MapEditMissions[0])) + + +/*........................................................................... +For menu processing +...........................................................................*/ +extern int UnknownKey; // in menus.cpp + +char MapEditClass::HealthBuf[20]; + + +/*************************************************************************** + * MapEditClass::MapEditClass -- class constructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/20/1994 BR : Created. * + *=========================================================================*/ +MapEditClass::MapEditClass(void) +{ + /* + ** Init data members. + */ + ScenVar = SCEN_VAR_A; + ObjCount = 0; + LastChoice = 0; + LastHouse = HOUSE_GOOD; + GrabbedObject = 0; + for (int i=0; i < NUM_EDIT_CLASSES; i++) { + NumType[i] = 0; + TypeOffset[i] = 0; + } + Waypoint[WAYPT_HOME] = 0; + CurrentCell = 0; + CurTrigger = NULL; + Changed = 0; + LMouseDown = 0; + BaseBuilding = 0; + BasePercent = 100; +} + + +/*************************************************************************** + * MapEditClass::One_Time -- one-time initialization * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/02/1995 BR : Created. * + *=========================================================================*/ +void MapEditClass::One_Time(void) +{ + MouseClass::One_Time(); + + /*------------------------------------------------------------------------ + Create the pop-up controls + ------------------------------------------------------------------------*/ + /*........................................................................ + The map: a single large "button" + ........................................................................*/ + //MapArea = new ControlClass(MAP_AREA,0,8,312,192, GadgetClass::LEFTPRESS | + //GadgetClass::LEFTRELEASE, false); + MapArea = new ControlClass(MAP_AREA,0,16,624,384, GadgetClass::LEFTPRESS | + GadgetClass::LEFTRELEASE, false); + + /*........................................................................ + House buttons + ........................................................................*/ + GDIButton = new TextButtonClass (POPUP_GDI, "GDI", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + POPUP_GDI_X, POPUP_GDI_Y, POPUP_GDI_W, POPUP_GDI_H); + + NODButton = new TextButtonClass (POPUP_NOD, "NOD", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + POPUP_NOD_X, POPUP_NOD_Y, POPUP_NOD_W, POPUP_NOD_H); + + NeutralButton = new TextButtonClass (POPUP_NEUTRAL, "Neutral", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + POPUP_NEUTRAL_X, POPUP_NEUTRAL_Y, POPUP_NEUTRAL_W, POPUP_NEUTRAL_H); + + Multi1Button = new TextButtonClass (POPUP_MULTI1, "M1", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + POPUP_MULTI1_X, POPUP_MULTI1_Y, POPUP_MULTI1_W, POPUP_MULTI1_H); + + Multi2Button = new TextButtonClass (POPUP_MULTI2, "M2", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + POPUP_MULTI2_X, POPUP_MULTI2_Y, POPUP_MULTI2_W, POPUP_MULTI2_H); + + Multi3Button = new TextButtonClass (POPUP_MULTI3, "M3", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + POPUP_MULTI3_X, POPUP_MULTI3_Y, POPUP_MULTI3_W, POPUP_MULTI3_H); + + Multi4Button = new TextButtonClass (POPUP_MULTI4, "M4", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + POPUP_MULTI4_X, POPUP_MULTI4_Y, POPUP_MULTI4_W, POPUP_MULTI4_H); + + /*........................................................................ + The mission list box + ........................................................................*/ + MissionList = new ListClass (POPUP_MISSIONLIST, + POPUP_MISSION_X, POPUP_MISSION_Y, POPUP_MISSION_W, POPUP_MISSION_H, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + Hires_Retrieve("BTN-UP.SHP"), + Hires_Retrieve("BTN-DN.SHP")); + + for (int i = 0; i < NUM_EDIT_MISSIONS; i++) { + MissionList->Add_Item (MissionClass::Mission_Name(MapEditMissions[i])); + } + + /*........................................................................ + The health bar + ........................................................................*/ + HealthGauge = new TriColorGaugeClass (POPUP_HEALTHGAUGE, + POPUP_HEALTH_X, POPUP_HEALTH_Y, POPUP_HEALTH_W, POPUP_HEALTH_H); + HealthGauge->Use_Thumb(true); + HealthGauge->Set_Maximum(0x100); + HealthGauge->Set_Red_Limit(0x3f - 1); + HealthGauge->Set_Yellow_Limit(0x7f - 1); + + /*........................................................................ + The health text label + ........................................................................*/ + HealthBuf[0] = 0; + HealthText = new TextLabelClass (HealthBuf, + POPUP_HEALTH_X + POPUP_HEALTH_W / 2, + POPUP_HEALTH_Y + POPUP_HEALTH_H + 1, + CC_GREEN, TPF_CENTER | TPF_FULLSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL); + + /*........................................................................ + The facing dial + ........................................................................*/ + FacingDial = new Dial8Class (POPUP_FACINGDIAL, POPUP_FACEBOX_X, + POPUP_FACEBOX_Y, POPUP_FACEBOX_W, POPUP_FACEBOX_H, (DirType)0); + + /*........................................................................ + The base percent-built slider & its label + ........................................................................*/ + BaseGauge = new GaugeClass (POPUP_BASEPERCENT, + POPUP_BASE_X, POPUP_BASE_Y, POPUP_BASE_W, POPUP_BASE_H); + BaseLabel = new TextLabelClass ("Base:", POPUP_BASE_X - 3, POPUP_BASE_Y, + CC_GREEN, TPF_RIGHT | TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL); + BaseGauge->Set_Maximum(100); + BaseGauge->Set_Value(BasePercent); +} + + +/*********************************************************************************************** + * MapeditClass::Init_IO -- Reinitializes the radar map at scenario start. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +void MapEditClass::Init_IO(void) +{ + /*------------------------------------------------------------------------ + For normal game mode, jump to the parent's Init routine. + ------------------------------------------------------------------------*/ + if (!Debug_Map) { + + MouseClass::Init_IO(); + + } else { + + /*------------------------------------------------------------------------ + For editor mode, add the map area to the button input list + ------------------------------------------------------------------------*/ + Buttons = 0; + Add_A_Button(*BaseGauge); + Add_A_Button(*BaseLabel); + Add_A_Button(*MapArea); + } +} + + +/*************************************************************************** + * MapEditClass::Read_INI -- overloaded Read_INI function * + * * + * Overloading this function gives the map editor a chance to initialize * + * certain values every time a new INI is read. * + * * + * INPUT: * + * buffer INI staging area * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/16/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Read_INI(char *buffer) +{ + /* + ------------------------ Invoke parent's Read_INI ------------------------ + */ + MouseClass::Read_INI(buffer); + + BasePercent = WWGetPrivateProfileInt("Basic","Percent",0,buffer); + BaseGauge->Set_Value(BasePercent); +} + + +/*************************************************************************** + * MapEditClass::Write_INI -- overloaded Read_INI function * + * * + * INPUT: * + * buffer INI staging area * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/16/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Write_INI(char *buffer) +{ + /* + ----------------------- Invoke parent's Write_INI ------------------------ + */ + MouseClass::Write_INI(buffer); + + /* + ** Save the base's percent-built value; this must be saved into the BASIC + ** section of the INI, since the Base section will be entirely erased + ** by the Base's Write_INI routine. + */ + WWWritePrivateProfileInt("Basic", "Percent", BasePercent, buffer); +} + + +/*************************************************************************** + * MapEditClass::Clear_List -- clears the internal choosable object list * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/20/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Clear_List(void) +{ + /*------------------------------------------------------------------------ + Set # object type ptrs to 0, set NumType for each type to 0 + ------------------------------------------------------------------------*/ + ObjCount = 0; + for (int i = 0; i < NUM_EDIT_CLASSES; i++) { + NumType[i] = 0; + } +} + + +/*************************************************************************** + * MapEditClass::Add_To_List -- adds a TypeClass to the choosable list * + * * + * Use this routine to add an object to the game object selection list. * + * This list is used by the Add_Object function. All items located in the * + * list will appear and be chooseable by that function. Make sure to * + * clear the list before adding a sequence of items to it. Clearing * + * the list is accomplished by the Clear_List() function. * + * * + * INPUT: * + * object ptr to ObjectTypeClass to add * + * * + * OUTPUT: * + * bool: was the object added to the list? A failure could occur if * + * NULL were passed in or the list is full. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/04/1994 JLB : Created. * + *=========================================================================*/ +bool MapEditClass::Add_To_List(ObjectTypeClass const *object) +{ + /* + ** Add the object if there's room. + */ + if (object && ObjCount < MAX_EDIT_OBJECTS) { + Objects[ObjCount++] = object; + + /* + ** Update type counters. + */ + switch (object->What_Am_I()) { + case RTTI_TEMPLATETYPE: + NumType[0]++; + break; + + case RTTI_OVERLAYTYPE: + NumType[1]++; + break; + + case RTTI_SMUDGETYPE: + NumType[2]++; + break; + + case RTTI_TERRAINTYPE: + NumType[3]++; + break; + + case RTTI_UNITTYPE: + NumType[4]++; + break; + + case RTTI_INFANTRYTYPE: + NumType[5]++; + break; + + case RTTI_AIRCRAFTTYPE: + NumType[6]++; + break; + + case RTTI_BUILDINGTYPE: + NumType[7]++; + break; + } + return(true); + } + + return(false); +} + + +/*************************************************************************** + * MapEditClass::AI -- The map editor's main logic * + * * + * This routine overloads the parent's (DisplayClass) AI function. * + * It checks for any input specific to map editing, and calls the parent * + * AI routine to handle scrolling and other mainstream map stuff. * + * * + * If this detects one of its special input keys, it sets 'input' to 0 * + * before calling the parent AI routine; this prevents input conflict. * + * * + * SUPPORTED INPUT: * + * General: * + * F2/RMOUSE: main menu * + * F6: toggles show-passable mode * + * HOME: go to the Home Cell (scenario's start position)* + * SHIFT-HOME: set the Home Cell to the current TacticalCell* + * ESC: exits to DOS * + * Object Placement: * + * INSERT: go into placement mode * + * ESC: exit placement mode * + * LEFT/RIGHT: prev/next placement object * + * PGUP/PGDN: prev/next placement category * + * HOME: 1st placement object (clear template) * + * h/H: toggle house of placement object * + * LMOUSE: place the placement object * + * MOUSE MOTION: "paint" with the placement object * + * Object selection: * + * LMOUSE: select & "grab" current object * + * If no object is present where the mouse is * + * clicked, the current object is de-selected * + * If the same object is clicked on, it stays * + * selected. Also displays the object-editing * + * gadgets. * + * LMOUSE RLSE: release currently-grabbed object * + * MOUSE MOTION: if an object is grabbed, moves the object * + * SHIFT|ALT|ARROW: moves object in that direction * + * DELETE deletes currently-selected object * + * Object-editing controls: * + * POPUP_GDI: makes GDI the owner of this object * + * POPUP_NOD: makes NOD the owner of this object * + * POPUP_MISSIONLIST: sets that mission for this object * + * POPUP_HEALTHGAUGE: sets that health value for this object * + * POPUP_FACINGDIAL: sets the object's facing * + * * + * Changed is set when you: * + * - place an object * + * - move a grabbed object * + * - delete an object * + * - size the map * + * - create a new scenario * + * Changed is cleared when you: * + * - Save the scenario * + * - Load a scenario * + * - Play the scenario * + * * + * INPUT: * + * input KN_ value, 0 if none * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/20/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::AI(KeyNumType &input, int x, int y) +{ + int rc; + MissionType mission; + int strength; + CELL cell; + int i; + int found; // for removing a waypoint label + int waypt_idx; // for labelling a waypoint + BaseNodeClass *node; // for removing from an AI Base + HousesType house; + + /*------------------------------------------------------------------------ + Trap 'F2' regardless of whether we're in game or editor mode + ------------------------------------------------------------------------*/ + if (Debug_Flag) { + if (/*(input == KN_F2 && Session == GAME_SOLO) ||*/ input == (KN_F2 | KN_CTRL_BIT)) { + ScenarioInit = 0; + + /* + ** If we're in editor mode & Changed is set, prompt for saving changes + */ + if (Debug_Map && Changed) { + rc = CCMessageBox().Process("Save Changes?", TXT_YES, TXT_NO); + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + /* + ........................ User wants to save ........................ + */ + if (rc == 0) { + + /* + ................ If save cancelled, abort game .................. + */ + if (Save_Scenario()!=0) { + input = KN_NONE; + } else { + Changed = 0; + Go_Editor(!Debug_Map); + } + } else { + + /* + .................... User doesn't want to save ..................... + */ + Go_Editor(!Debug_Map); + } + } else { + /* + ** If we're in game mode, set Changed to 0 (so if we didn't save our + ** changes above, they won't keep coming back to haunt us with continual + ** Save Changes? prompts!) + */ + if (!Debug_Map) { + Changed = 0; + } + Go_Editor(!Debug_Map); + } + } + } + + /*------------------------------------------------------------------------ + For normal game mode, jump to the parent's AI routine. + ------------------------------------------------------------------------*/ + if (!Debug_Map) { + MouseClass::AI(input, x, y); + return; + } + + ::Frame++; + + /*------------------------------------------------------------------------ + Do special mouse processing if the mouse is over the map + ------------------------------------------------------------------------*/ + if (Get_Mouse_X() > TacPixelX && Get_Mouse_X() < + TacPixelX + Lepton_To_Pixel(TacLeptonWidth) && + Get_Mouse_Y() > TacPixelY && Get_Mouse_Y() < + TacPixelY + Lepton_To_Pixel(TacLeptonHeight)) { + /*..................................................................... + When the mouse moves over a scrolling edge, ScrollClass changes its + shape to the appropriate arrow or NO symbol; it's our job to change it + back to normal (or whatever the shape is set to by Set_Default_Mouse()) + when it re-enters the map area. + .....................................................................*/ + if (CurTrigger) { + Override_Mouse_Shape(MOUSE_CAN_MOVE); + } else { + Override_Mouse_Shape(MOUSE_NORMAL); + } + } + + /*..................................................................... + Set 'ZoneCell' to track the mouse cursor around over the map. Do this + even if the map is scrolling. + .....................................................................*/ + if (Get_Mouse_X() >= TacPixelX && Get_Mouse_X() <= + TacPixelX + Lepton_To_Pixel(TacLeptonWidth) && + Get_Mouse_Y() >= TacPixelY && Get_Mouse_Y() <= + TacPixelY + Lepton_To_Pixel(TacLeptonHeight)) { + + cell = Click_Cell_Calc(Get_Mouse_X(), Get_Mouse_Y()); + if (cell != -1) { + Set_Cursor_Pos(cell); + if (PendingObject) { + Flag_To_Redraw(true); + } + } + } + + /*------------------------------------------------------------------------ + Check for mouse motion while left button is down. + ------------------------------------------------------------------------*/ + rc = Mouse_Moved(); + if (LMouseDown && rc) { + /*..................................................................... + "Paint" mode: place current object, and restart placement + .....................................................................*/ + if (PendingObject) { + Flag_To_Redraw(true); + if (Place_Object() == 0) { + Changed = 1; + Start_Placement(); + } + } else { + /*..................................................................... + Move the currently-grabbed object + .....................................................................*/ + if (GrabbedObject) { + GrabbedObject->Mark(MARK_CHANGE); + if (Move_Grabbed_Object() == 0) { + Changed = 1; + } + } + } + } + + /*------------------------------------------------------------------------ + Trap special editing keys; if one is detected, set 'input' to 0 to + prevent a conflict with parent's AI(). + ------------------------------------------------------------------------*/ + switch (input) { + /*--------------------------------------------------------------------- + F2/RMOUSE = pop up main menu + ---------------------------------------------------------------------*/ + case KN_RMOUSE: + /* + ..................... Turn off placement mode ...................... + */ + if (PendingObject) { + if (BaseBuilding) { + Cancel_Base_Building(); + } else { + Cancel_Placement(); + } + } + + /* + ................. Turn off trigger placement mode .................. + */ + if (CurTrigger) { + Stop_Trigger_Placement(); + } + + /* + .............. Unselect object & hide popup controls ............... + */ + if (CurrentObject.Count()) { + CurrentObject[0]->Unselect(); + Popup_Controls(); + } + Main_Menu(); + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + F6 = toggle passable/impassable display + ---------------------------------------------------------------------*/ + case KN_F6: + Debug_Passable = (Debug_Passable == false); + HiddenPage.Clear(); + Flag_To_Redraw(true); + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + INSERT = go into object-placement mode + ---------------------------------------------------------------------*/ + case KN_INSERT: + if (!PendingObject) { + /* + ......... Unselect current object, hide popup controls .......... + */ + if (CurrentObject.Count()) { + CurrentObject[0]->Unselect(); + Popup_Controls(); + } + /* + .................... Go into placement mode ..................... + */ + Start_Placement(); + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + ESC = exit placement mode, or exit to DOS + ---------------------------------------------------------------------*/ + case KN_ESC: + + /* + .................... Exit object placement mode .................... + */ + if (PendingObject) { + if (BaseBuilding) { + Cancel_Base_Building(); + } else { + Cancel_Placement(); + } + input = KN_NONE; + break; + } else { + + /* + ................... Exit trigger placement mode .................... + */ + if (CurTrigger) { + Stop_Trigger_Placement(); + input = KN_NONE; + break; + } else { + rc = CCMessageBox().Process("Exit Scenario Editor?", TXT_YES, TXT_NO); + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + .......... User doesn't want to exit; return to editor .......... + */ + if (rc==1) { + input = KN_NONE; + break; + } + + /* + ................. If changed, prompt for saving ................. + */ + if (Changed) { + rc = CCMessageBox().Process("Save Changes?", TXT_YES, TXT_NO); + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ..................... User wants to save ..................... + */ + if (rc == 0) { + + /* + .............. If save cancelled, abort exit .............. + */ + if (Save_Scenario()!=0) { + input = KN_NONE; + break; + } else { + Changed = 0; + } + } + } + } + } + Prog_End(); + exit (0); + break; + + /*--------------------------------------------------------------------- + LEFT = go to previous placement object + ---------------------------------------------------------------------*/ + case KN_LEFT: + if (PendingObject) { + Place_Prev(); + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + RIGHT = go to next placement object + ---------------------------------------------------------------------*/ + case KN_RIGHT: + if (PendingObject) { + Place_Next(); + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + PGUP = go to previous placement category + ---------------------------------------------------------------------*/ + case KN_PGUP: + if (PendingObject) { + Place_Prev_Category(); + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + PGDN = go to next placement category + ---------------------------------------------------------------------*/ + case KN_PGDN: + if (PendingObject) { + Place_Next_Category(); + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + HOME = jump to first placement object, or go to Home Cell + ---------------------------------------------------------------------*/ + case KN_HOME: + if (PendingObject) { + Place_Home(); + } else { + + /* + ....................... Set map position ........................ + */ + ScenarioInit++; + Set_Tactical_Position(Waypoint[WAYPT_HOME]); + ScenarioInit--; + + /* + ...................... Force map to redraw ...................... + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + SHIFT-HOME: set new Home Cell position + ---------------------------------------------------------------------*/ + case ((int)KN_HOME | (int)KN_SHIFT_BIT): + /* + ** Unflag the old Home Cell, if there are no other waypoints + ** pointing to it + */ + cell = Waypoint[WAYPT_HOME]; + + if (cell != -1) { + found = 0; + for (i = 0; i < WAYPT_COUNT; i++) { + if (i != WAYPT_HOME && Waypoint[i]==cell) { + found = 1; + } + } + + if (found==0) { + (*this)[cell].IsWaypoint = 0; + Flag_Cell(cell); + } + + } + + /* + ** Now set the new Home cell + */ + Waypoint[WAYPT_HOME] = Coord_Cell(TacticalCoord); + (*this)[Coord_Cell(TacticalCoord)].IsWaypoint = 1; + Flag_Cell(Coord_Cell(TacticalCoord)); + Changed = 1; + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + SHIFT-R: set new Reinforcement Cell position. Don't allow setting + the Reinf. Cell to the same as the Home Cell (for display purposes.) + ---------------------------------------------------------------------*/ + case ((int)KN_R | (int)KN_SHIFT_BIT): + if (CurrentCell==0 || CurrentCell==Waypoint[WAYPT_HOME]) { + break; + } + + /* + ** Unflag the old Reinforcement Cell, if there are no other waypoints + ** pointing to it + */ + cell = Waypoint[WAYPT_REINF]; + + if (cell != -1) { + found = 0; + for (i = 0; i < WAYPT_COUNT; i++) { + if (i != WAYPT_REINF && Waypoint[i]==cell) { + found = 1; + } + } + + if (found==0) { + (*this)[cell].IsWaypoint = 0; + Flag_Cell(cell); + } + + } + /* + ** Now set the new Reinforcement cell + */ + Waypoint[WAYPT_REINF] = CurrentCell; + (*this)[CurrentCell].IsWaypoint = 1; + Flag_Cell(CurrentCell); + Changed = 1; + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + ALT-Letter: Label a waypoint cell + ---------------------------------------------------------------------*/ + case ((int)KN_A | (int)KN_ALT_BIT): + case ((int)KN_B | (int)KN_ALT_BIT): + case ((int)KN_C | (int)KN_ALT_BIT): + case ((int)KN_D | (int)KN_ALT_BIT): + case ((int)KN_E | (int)KN_ALT_BIT): + case ((int)KN_F | (int)KN_ALT_BIT): + case ((int)KN_G | (int)KN_ALT_BIT): + case ((int)KN_H | (int)KN_ALT_BIT): + case ((int)KN_I | (int)KN_ALT_BIT): + case ((int)KN_J | (int)KN_ALT_BIT): + case ((int)KN_K | (int)KN_ALT_BIT): + case ((int)KN_L | (int)KN_ALT_BIT): + case ((int)KN_M | (int)KN_ALT_BIT): + case ((int)KN_N | (int)KN_ALT_BIT): + case ((int)KN_O | (int)KN_ALT_BIT): + case ((int)KN_P | (int)KN_ALT_BIT): + case ((int)KN_Q | (int)KN_ALT_BIT): + case ((int)KN_R | (int)KN_ALT_BIT): + case ((int)KN_S | (int)KN_ALT_BIT): + case ((int)KN_T | (int)KN_ALT_BIT): + case ((int)KN_U | (int)KN_ALT_BIT): + case ((int)KN_V | (int)KN_ALT_BIT): + case ((int)KN_W | (int)KN_ALT_BIT): + case ((int)KN_X | (int)KN_ALT_BIT): + case ((int)KN_Y | (int)KN_ALT_BIT): + case ((int)KN_Z | (int)KN_ALT_BIT): + if (CurrentCell != 0) { + waypt_idx = KN_To_KA(input & 0xff) - KA_a; + /*............................................................... + Unflag cell for this waypoint if there is one + ...............................................................*/ + cell = Waypoint[waypt_idx]; + if (cell != -1) { + if (Waypoint[WAYPT_HOME] != cell && + Waypoint[WAYPT_REINF] != cell) + (*this)[cell].IsWaypoint = 0; + Flag_Cell(cell); + } + Waypoint[waypt_idx] = CurrentCell; + (*this)[CurrentCell].IsWaypoint = 1; + Changed = 1; + Flag_Cell(CurrentCell); + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + ALT-1-4: Designate a cell as a capture-the-flag cell. + ---------------------------------------------------------------------*/ + case ((int)KN_1 | (int)KN_ALT_BIT): + case ((int)KN_2 | (int)KN_ALT_BIT): + case ((int)KN_3 | (int)KN_ALT_BIT): + case ((int)KN_4 | (int)KN_ALT_BIT): + /*------------------------------------------------------------------ + If there's a current cell, place the flag & waypoint there. + ------------------------------------------------------------------*/ + if (CurrentCell != 0) { + waypt_idx = (KN_To_KA(input & 0xff) - KA_1); + house = (HousesType)(HOUSE_MULTI1 + waypt_idx); + if (HouseClass::As_Pointer(house)) { + HouseClass::As_Pointer(house)->Flag_Attach(CurrentCell,true); + } + } else { + /*------------------------------------------------------------------ + If there's a current object, attach the flag to it and clear the + waypoint. + ------------------------------------------------------------------*/ + if (CurrentObject[0] != 0) { + waypt_idx = (KN_To_KA(input & 0xff) - KA_1); + house = (HousesType)(HOUSE_MULTI1 + waypt_idx); + if (HouseClass::As_Pointer(house) && CurrentObject[0]->What_Am_I() == RTTI_UNIT) { + HouseClass::As_Pointer(house)->Flag_Attach((UnitClass *)CurrentObject[0], true); + } + } + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + ALT-Space: Remove a waypoint designation + ---------------------------------------------------------------------*/ + case ((int)KN_SPACE | (int)KN_ALT_BIT): + if (CurrentCell != 0) { + /*............................................................... + Loop through letter waypoints; if this cell is one of them, + clear that waypoint. + ...............................................................*/ + for (i = 0 ; i < 26; i++) { + if (Waypoint[i]==CurrentCell) + Waypoint[i] = -1; + } + + /*............................................................... + Loop through flag home values; if this cell is one of them, clear + that waypoint. + ...............................................................*/ + for (i = 0; i < MAX_PLAYERS; i++) { + house = (HousesType)(HOUSE_MULTI1 + i); + if (HouseClass::As_Pointer(house) && + CurrentCell == HouseClass::As_Pointer(house)->FlagHome) + HouseClass::As_Pointer(house)->Flag_Remove(As_Target(CurrentCell),true); + } + + /*............................................................... + If there are no more waypoints on this cell, clear the cell's + waypoint designation. + ...............................................................*/ + if (Waypoint[WAYPT_HOME]!=CurrentCell && + Waypoint[WAYPT_REINF]!=CurrentCell) + (*this)[CurrentCell].IsWaypoint = 0; + Changed = 1; + Flag_Cell(CurrentCell); + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + 'H' = toggle current placement object's house + ---------------------------------------------------------------------*/ + case KN_H: + case ((int)KN_H | (int)KN_SHIFT_BIT): + if (PendingObject) { + Toggle_House(); + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + Left-mouse click: + Button DOWN: + - Toggle LMouseDown + - If we're in placement mode, try to place the current object + - If success, re-enter placement mode + - Otherwise, try to select an object, and "grab" it if there is one + - If no object, then select that cell as the "current" cell + Button UP: + - Toggle LMouseDown + - release any grabbed object + ---------------------------------------------------------------------*/ + case ((int)MAP_AREA | (int)KN_BUTTON): + /* + ------------------------- Left Button DOWN ------------------------- + */ + if (Keyboard::Down(KN_LMOUSE)) { + LMouseDown = 1; + /* + ............... Placement mode: place an object ................. + */ + if (PendingObject) { + if (Place_Object()==0) { + Changed = 1; + Start_Placement(); + } + } else { + /* + ....................... Place a trigger ......................... + */ + if (CurTrigger) { + Place_Trigger(); + Changed = 1; + } else { + /* + ................. Select an object or a cell ................. + .................. Check for double-click .................... + */ + if (CurrentObject.Count() && + ( (TickCount.Time() - LastClickTime) < 15)) { + ; // stub + + } else { + /* + ................ Single-click: select object ................. + */ + if (Select_Object()==0) { + CurrentCell = 0; + Grab_Object(); + } else { + /* + ................ No object: select the cell .................. + */ + CurrentCell = Click_Cell_Calc(_Kbd->MouseQX,_Kbd->MouseQY); + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + } + } + } + } + LastClickTime = TickCount.Time(); + input = KN_NONE; + } else { + + /* + -------------------------- Left Button UP -------------------------- + */ + LMouseDown = 0; + GrabbedObject = 0; + input = KN_NONE; + } + break; + + /*--------------------------------------------------------------------- + SHIFT-ALT-Arrow: move the current object + ---------------------------------------------------------------------*/ + case (int)KN_UP | (int)KN_ALT_BIT | (int)KN_SHIFT_BIT: + case (int)KN_DOWN | (int)KN_ALT_BIT | (int)KN_SHIFT_BIT: + case (int)KN_LEFT | (int)KN_ALT_BIT | (int)KN_SHIFT_BIT: + case (int)KN_RIGHT | (int)KN_ALT_BIT | (int)KN_SHIFT_BIT: + if (CurrentObject.Count()) { + CurrentObject[0]->Move(KN_To_Facing(input)); + Changed = 1; + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + DELETE: delete currently-selected object + ---------------------------------------------------------------------*/ + case KN_DELETE: + /*.................................................................. + Delete currently-selected object's trigger, or the object + ..................................................................*/ + if (CurrentObject.Count()) { + + /* + ........................ Delete trigger ......................... + */ + if (CurrentObject[0]->Trigger) { + CurrentObject[0]->Trigger = NULL; + } else { + /* + ** If the current object is part of the AI's Base, remove it + ** from the Base's Node list. + */ + if (CurrentObject[0]->What_Am_I()==RTTI_BUILDING && + Base.Is_Node((BuildingClass *)CurrentObject[0])) { + node = Base.Get_Node((BuildingClass *)CurrentObject[0]); + Base.Nodes.Delete(*node); + } + + /* + ................... Delete current object .................... + */ + delete CurrentObject[0]; + + /* + .................. Hide the popup controls ................... + */ + Popup_Controls(); + } + + /* + ........................ Force a redraw ......................... + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Changed = 1; + } else { + /* + ................. Remove trigger from current cell ................. + */ + if (CurrentCell) { + if ((*this)[CurrentCell].IsTrigger) { + (*this)[CurrentCell].IsTrigger = 0; + CellTriggers[CurrentCell] = NULL; + /* + ...................... Force a redraw ........................ + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Changed = 1; + } + } + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + TAB: select next object on the map + ---------------------------------------------------------------------*/ + case KN_TAB: + Select_Next(); + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + Object-Editing button: House Button + ---------------------------------------------------------------------*/ + case (POPUP_GDI | KN_BUTTON): + case (POPUP_NOD | KN_BUTTON): + case (POPUP_NEUTRAL | KN_BUTTON): + case (POPUP_MULTI1 | KN_BUTTON): + case (POPUP_MULTI2 | KN_BUTTON): + case (POPUP_MULTI3 | KN_BUTTON): + case (POPUP_MULTI4 | KN_BUTTON): + /*.................................................................. + Convert input value into a house value; assume HOUSE_GOOD is 0 + ..................................................................*/ + house = (HousesType)( (input & (~KN_BUTTON)) - POPUP_GDI); + /*.................................................................. + If that house doesn't own this object, try to transfer it + ..................................................................*/ + if (CurrentObject[0]->Owner()!=house) { + if (Change_House(house)) { + Changed = 1; + } + } + Set_House_Buttons(CurrentObject[0]->Owner(), Buttons, POPUP_GDI); + HiddenPage.Clear(); + Flag_To_Redraw(true); + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + Object-Editing button: Mission + ---------------------------------------------------------------------*/ + case (POPUP_MISSIONLIST | KN_BUTTON): + if (CurrentObject[0]->Is_Techno()) { + /* + ........................ Set new mission ........................ + */ + mission = MapEditMissions[MissionList->Current_Index()]; + if (CurrentObject[0]->Get_Mission() != mission) { + ((TechnoClass *)CurrentObject[0])->Set_Mission(mission); + Changed = 1; + } + } + Flag_To_Redraw(true); + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + Object-Editing button: Health + ---------------------------------------------------------------------*/ + case (POPUP_HEALTHGAUGE | KN_BUTTON): + if (CurrentObject[0]->Is_Techno()) { + /* + .......... Derive strength from current gauge reading ........... + */ + strength = Fixed_To_Cardinal( + (unsigned)CurrentObject[0]->Class_Of().MaxStrength, + (unsigned)HealthGauge->Get_Value()); + + /* + ........................... Clip to 1 ........................... + */ + if (strength <= 0) { + strength = 1; + } + + /* + ....................... Set new strength ........................ + */ + if (strength != CurrentObject[0]->Strength) { + CurrentObject[0]->Strength = strength; + HiddenPage.Clear(); + Flag_To_Redraw(true); + Changed = 1; + } + + /* + ....................... Update text label ....................... + */ + sprintf(HealthBuf,"%d",strength); + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + Object-Editing button: Facing + ---------------------------------------------------------------------*/ + case (POPUP_FACINGDIAL | KN_BUTTON): + if (CurrentObject[0]->Is_Techno()) { + /* + ........................ Set new facing ......................... + */ + if (FacingDial->Get_Direction() != + ((TechnoClass *)CurrentObject[0])->PrimaryFacing.Get()) { + /* + ..................... Set body's facing ...................... + */ + ((TechnoClass *)CurrentObject[0])->PrimaryFacing.Set(FacingDial->Get_Direction()); + + /* + ............. Set turret facing, if there is one ............. + */ + if (CurrentObject[0]->What_Am_I()==RTTI_UNIT) { + ((UnitClass *)CurrentObject[0])->SecondaryFacing.Set(FacingDial->Get_Direction()); + } + + HiddenPage.Clear(); + Flag_To_Redraw(true); + Changed = 1; + } + } + input = KN_NONE; + break; + + /*--------------------------------------------------------------------- + Object-Editing button: Facing + ---------------------------------------------------------------------*/ + case (POPUP_BASEPERCENT | KN_BUTTON): + if (BaseGauge->Get_Value() != BasePercent) { + BasePercent = BaseGauge->Get_Value(); + Build_Base_To(BasePercent); + HiddenPage.Clear(); + Flag_To_Redraw(true); + } + input = KN_NONE; + break; + + case (KN_LMOUSE): + input = KN_NONE; + break; + + default: + break; + } + + /* + ------------------------ Call parent's AI routine ------------------------ + */ + MouseClass::AI(input, x, y); +} + + +/*************************************************************************** + * MapEditClass::Draw_It -- overloaded Redraw routine * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/17/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Draw_It(bool forced) +{ + char const *label; + char buf[40]; + char const *tptr; + + MouseClass::Draw_It(forced); + + if (!Debug_Map) { + return; + } + + // + // Erase scrags at top of screen + // + LogicPage->Fill_Rect(0, 0, 640, 16, BLACK); + + /* + ** Display the total value of all Tiberium on the map. + */ + Fancy_Text_Print("Tiberium=%ld ", 0, 0, CC_GREEN, BLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, TotalValue); + + /*------------------------------------------------------------------------ + If there are no object controls displayed, just invoke parent's Redraw + and return. + ------------------------------------------------------------------------*/ + if (!Buttons) { + return; + } + + /*------------------------------------------------------------------------ + Otherwise, if 'display' is set, invoke the parent's Redraw to refresh + the HIDPAGE; then, update the buttons & text labels onto HIDPAGE; + then invoke the parent's Redraw to blit the HIDPAGE to SEENPAGE. + ------------------------------------------------------------------------*/ + if (forced) { + + /* + ....................... Update the text labels ........................ + */ + if (CurrentObject.Count()) { + /* + ------------------ Display the object's name & ID ------------------ + */ + label = Text_String(CurrentObject[0]->Full_Name()); + tptr = label; + sprintf(buf,"%s (%d)",tptr,CurrentObject[0]->As_Target()); + + /* + ......................... print the label .......................... + */ + Fancy_Text_Print (buf, 320, 0, CC_TAN, TBLACK, + TPF_CENTER | TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL); + } + } +} + + +/*************************************************************************** + * MapEditClass::Mouse_Moved -- checks for mouse motion * + * * + * Reports whether the mouse has moved or not. This varies based on the * + * type of object currently selected. If there's an infantry object * + * selected, mouse motion counts even within a cell; for all other types,* + * mouse motion counts only if the mouse changes cells. * + * * + * The reason this routine is needed is to prevent Paint-Mode from putting* + * gobs of trees and such into the same cell if the mouse moves just * + * a little bit. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/08/1994 BR : Created. * + *=========================================================================*/ +bool MapEditClass::Mouse_Moved(void) +{ + static int old_mx = 0; + static int old_my = 0; + static CELL old_zonecell = 0; + const ObjectTypeClass * objtype = NULL; + bool retcode = false; + + /* + -------------------------- Return if no motion --------------------------- + */ + if (old_mx == Get_Mouse_X() && old_my == Get_Mouse_Y()) { + return(false); + } + + /* + ---------------------- Get a ptr to ObjectTypeClass ---------------------- + */ + if (PendingObject) { + objtype = PendingObject; + } else { + if (GrabbedObject) { + objtype = &GrabbedObject->Class_Of(); + } else { + old_mx = Get_Mouse_X(); + old_my = Get_Mouse_Y(); + old_zonecell = ZoneCell; + return(false); + } + } + + /* + --------------------- Check for motion based on type --------------------- + */ + /* + ............... Infantry: mouse moved if any motion at all ............... + */ + if (objtype->What_Am_I() == RTTI_INFANTRYTYPE) { + retcode = true; + } else { + /* + ................ Others: mouse moved only if cell changed ................ + */ + if (old_zonecell!=ZoneCell) { + retcode = true; + } else { + retcode = false; + } + } + + old_mx = Get_Mouse_X(); + old_my = Get_Mouse_Y(); + old_zonecell = ZoneCell; + return(retcode); +} + + +/*************************************************************************** + * MapEditClass::Main_Menu -- main menu processor for map editor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/20/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Main_Menu(void) +{ + char const *_menus[MAX_MAIN_MENU_NUM + 1]; + int selection; // option the user picks + bool process; // menu stays up while true + int rc; + + /* + --------------------------- Fill in menu items --------------------------- + */ + _menus[0] = "New Scenario"; + _menus[1] = "Load Scenario"; + _menus[2] = "Save Scenario"; + _menus[3] = "Size Map"; + _menus[4] = "Add Game Object"; + _menus[5] = "Scenario Options"; + _menus[6] = "AI Options"; + _menus[7] = "Play Scenario"; + _menus[8] = NULL; + + /* + ----------------------------- Main Menu loop ----------------------------- + */ + Override_Mouse_Shape(MOUSE_NORMAL); // display default mouse cursor + process = true; + while (process) { + + /* + ................ Invoke game callback, to update music ................ + */ + Call_Back(); + + /* + ............................. Invoke menu ............................. + */ + Hide_Mouse(); // Do_Menu assumes the mouse is already hidden + selection = Do_Menu(&_menus[0], true); + Show_Mouse(); + if (UnknownKey==KN_ESC || UnknownKey==KN_LMOUSE || UnknownKey==KN_RMOUSE) { + break; + } + + /* + .......................... Process selection .......................... + */ + switch (selection) { + /* + ........................... New scenario ........................... + */ + case 0: + if (Changed) { + rc = CCMessageBox().Process("Save Changes?", TXT_YES, TXT_NO); + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + if (rc==0) { + if (Save_Scenario()!=0) { + break; + } else { + Changed = 0; + } + } + } + if (New_Scenario()==0) { + CarryOverMoney = 0; + Changed = 1; + } + process = false; + break; + + /* + .......................... Load scenario ........................... + */ + case 1: + if (Changed) { + rc = CCMessageBox().Process("Save Changes?", TXT_YES, TXT_NO); + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + if (rc==0) { + if (Save_Scenario()!=0) { + break; + } else { + Changed = 0; + } + } + } + if (Load_Scenario()==0) { + CarryOverMoney = 0; + Changed = 0; + } + process = false; + break; + + /* + .......................... Save scenario ........................... + */ + case 2: + if (Save_Scenario() == 0) { + Changed = 0; + } + process = false; + break; + + /* + .......................... Edit map size ........................... + */ + case 3: + if (Size_Map(MapCellX, MapCellY, MapCellWidth, MapCellHeight)==0) { + process = false; + Changed = 1; + } + break; + + /* + .......................... Add an object ........................... + */ + case 4: + if (Placement_Dialog() == 0) { + Start_Placement(); + process = false; + } + break; + + /* + ......................... Scenario options ......................... + */ + case 5: + if (Scenario_Dialog() == 0) { + Changed = 1; + process = false; + } + break; + + /* + .......................... Other options ........................... + */ + case 6: + AI_Menu(); + process = false; + break; + + /* + ...................... Test-drive this scenario .................... + */ + case 7: + if (Changed) { + rc = CCMessageBox().Process("Save Changes?", TXT_YES, TXT_NO); + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + if (rc==0) { + if (Save_Scenario()!=0) { + break; + } else { + Changed = 0; + } + } + } + Changed = 0; + Debug_Map = false; + Start_Scenario(ScenarioName); + return; + } + } + + /*------------------------------------------------------------------------ + Restore the display: + - Clear HIDPAGE to erase any spurious drawing done by the menu system + - Invoke Flag_To_Redraw to tell DisplayClass to re-render the whole screen + - Invoke Redraw() to update the display + ------------------------------------------------------------------------*/ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::AI_Menu -- menu of AI options * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::AI_Menu(void) +{ + int selection; // option the user picks + bool process; // menu stays up while true + char const *_menus[MAX_AI_MENU_NUM + 1]; + + /* + -------------------------- Fill in menu strings -------------------------- + */ + _menus[0] = "Pre-Build a Base"; + _menus[1] = "Import Triggers"; + _menus[2] = "Edit Triggers"; + _menus[3] = "Import Teams"; + _menus[4] = "Edit Teams"; + _menus[5] = NULL; + + /* + ----------------------------- Main Menu loop ----------------------------- + */ + Override_Mouse_Shape(MOUSE_NORMAL); // display default mouse cursor + process = true; + while (process) { + + /* + ................ Invoke game callback, to update music ................ + */ + Call_Back(); + + /* + ............................. Invoke menu ............................. + */ + Hide_Mouse(); // Do_Menu assumes the mouse is already hidden + selection = Do_Menu(&_menus[0], true); + Show_Mouse(); + if (UnknownKey==KN_ESC || UnknownKey==KN_LMOUSE || UnknownKey==KN_RMOUSE) { + break; + } + + /* + .......................... Process selection .......................... + */ + switch (selection) { + /* + ......................... Pre-Build a Base ......................... + */ + case 0: + Start_Base_Building(); + process = false; + break; + + /* + ......................... Import Triggers .......................... + */ + case 1: + if (Import_Triggers()==0) + process = false; + break; + + /* + ......................... Trigger Editing .......................... + */ + case 2: + Handle_Triggers(); + /* + ................ Go into trigger placement mode ................. + */ + if (CurTrigger) { + Start_Trigger_Placement(); + } + process = false; + break; + + /* + ........................... Import Teams ........................... + */ + case 3: + if (Import_Teams()==0) + process = false; + break; + + /* + ........................... Team Editing ........................... + */ + case 4: + Handle_Teams("Teams"); + process = false; + break; + } + } +} + + +/*************************************************************************** + * MapEditClass::Verify_House -- is this objtype ownable by this house? * + * * + * INPUT: * + * house house to check * + * objtype ObjectTypeClass to check * + * * + * OUTPUT: * + * 0 = isn't ownable, 1 = it is * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/16/1994 BR : Created. * + *=========================================================================*/ +bool MapEditClass::Verify_House(HousesType house, ObjectTypeClass const *objtype) +{ + /* + --------------- Verify that new house can own this object ---------------- + */ + return((objtype->Get_Ownable() & (1 << house)) != 0); +} + + +/*************************************************************************** + * MapEditClass::Cycle_House -- finds next valid house for object type * + * * + * INPUT: * + * objtype ObjectTypeClass ptr to get house for * + * curhouse current house value to start with * + * * + * OUTPUT: * + * HousesType that's valid for this object type * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/23/1994 BR : Created. * + *=========================================================================*/ +HousesType MapEditClass::Cycle_House(HousesType curhouse, + ObjectTypeClass const *objtype) +{ + HousesType count; // prevents an infinite loop + + /*------------------------------------------------------------------------ + Loop through all house types, starting with the one after 'curhouse'; + return the first one that's valid + ------------------------------------------------------------------------*/ + count = HOUSE_NONE; + while (1) { + + /* + .......................... Go to next house ........................... + */ + curhouse++; + if (curhouse == HOUSE_COUNT) { + curhouse = HOUSE_FIRST; + } + + /* + ................ Count # iterations; don't go forever ................. + */ + count++; + if (count == HOUSE_COUNT) { + curhouse = HOUSE_NONE; + break; + } + + /* + ................... Break if this is a valid house .................... + */ + if (HouseClass::As_Pointer(curhouse) && Verify_House(curhouse,objtype)) { + break; + } + } + + return(curhouse); +} + + +/*************************************************************************** + * MapEditClass::Fatal -- exits with error message * + * * + * INPUT: * + * code tells which message to display; this minimizes the * + * use of character strings in the code. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/12/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Fatal(int txt) +{ + Prog_End(); + printf("%s\n",txt); + exit(EXIT_FAILURE); +} + + +bool MapEditClass::Scroll_Map(DirType facing, int & distance, bool really) +{ + if (Debug_Map) { + /* + ** The popup gadgets require the entire map to be redrawn if we scroll. + */ + if (really) { + Flag_To_Redraw(true); + } + } + return(MouseClass::Scroll_Map(facing, distance, really)); +} + + +void MapEditClass::Detach(ObjectClass * object) +{ + if (GrabbedObject == object) { + GrabbedObject = 0; + } +} + + +#endif + +#include "mapedsel.cpp" diff --git a/MAPEDIT.H b/MAPEDIT.H new file mode 100644 index 0000000..f69d78c --- /dev/null +++ b/MAPEDIT.H @@ -0,0 +1,355 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\mapedit.h_v 2.19 16 Oct 1995 16:46:36 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPEDIT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 14, 1994 * + * * + * Last Update : May 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * This class is derived from the normal display map class. It exists * + * only to allow editing and adding items to the map. * + *---------------------------------------------------------------------------------------------* + * House-setting functions: The editor contains several house maintenance routines: * + * Verify_House: tells if the given ObjectType can be owned by the given HousesType * + * Cycle_House: Finds the next valid house for the given ObjectType; used when a new object * + * can't be owned by the current editor HousesType. * + * Change_House: attempts to change the owner of the currently-selected object * + * Toggle_House: cycles the HousesType of a pending placement object * + * Set_House_Buttons: sets house buttons in accordance with the given HousesType * + *---------------------------------------------------------------------------------------------* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MAPEDIT_H +#define MAPEDIT_H + +/* +********************************* Includes ********************************** +*/ +#include "function.h" + +/* +********************************** Defines ********************************** +*/ +/*........................................................................... +This is the maximum # of ObjectTypeClasses the editor has to deal with. +...........................................................................*/ +enum MapEdit1Enum { + MAX_EDIT_OBJECTS = // max # of ObjectTypeClasses allowed + (int)TEMPLATE_COUNT + + (int)OVERLAY_COUNT + + (int)SMUDGE_COUNT + + (int)TERRAIN_COUNT + + (int)UNIT_COUNT + + (int)INFANTRY_COUNT + + (int)AIRCRAFT_COUNT + + (int)STRUCT_COUNT, + + MAX_TEAM_CLASSES = // max # ObjectTypeClasses for a team + (int)UNIT_COUNT + + (int)INFANTRY_COUNT + + (int)AIRCRAFT_COUNT, + +// NUM_EDIT_MISSIONS = 6, // # missions that can be assigned an object + + NUM_EDIT_CLASSES = 8, // # different classes (templates, terrain, etc) + + MAX_MAIN_MENU_NUM = 8, + MAX_MAIN_MENU_LEN = 20, + + MAX_AI_MENU_NUM = 6, + MAX_AI_MENU_LEN = 20, + + POPUP_GDI_W = 100, + POPUP_GDI_H = 18, + POPUP_GDI_X = 20, + POPUP_GDI_Y = 320, + + POPUP_NOD_W = 100, + POPUP_NOD_H = 18, + POPUP_NOD_X = 20, + POPUP_NOD_Y = 338, + + POPUP_NEUTRAL_W = 100, + POPUP_NEUTRAL_H = 18, + POPUP_NEUTRAL_X = 20, + POPUP_NEUTRAL_Y = 356, + + POPUP_MULTI1_W = 50, + POPUP_MULTI1_H = 18, + POPUP_MULTI1_X = 20, + POPUP_MULTI1_Y = 320, + + POPUP_MULTI2_W = 50, + POPUP_MULTI2_H = 18, + POPUP_MULTI2_X = 70, + POPUP_MULTI2_Y = 320, + + POPUP_MULTI3_W = 50, + POPUP_MULTI3_H = 18, + POPUP_MULTI3_X = 20, + POPUP_MULTI3_Y = 330, + + POPUP_MULTI4_W = 50, + POPUP_MULTI4_H = 18, + POPUP_MULTI4_X = 70, + POPUP_MULTI4_Y = 338, + + POPUP_MISSION_W = 160, + POPUP_MISSION_H = 80, + POPUP_MISSION_X = 140, + POPUP_MISSION_Y = 300, + + POPUP_FACEBOX_W = 60, + POPUP_FACEBOX_H = 60, + POPUP_FACEBOX_X = 320, + POPUP_FACEBOX_Y = 320, + + POPUP_HEALTH_W = 100, + POPUP_HEALTH_H = 20, + POPUP_HEALTH_X = 400, + POPUP_HEALTH_Y = 340, + + POPUP_BASE_W = 100, + POPUP_BASE_H = 16, + POPUP_BASE_X = 600 - POPUP_BASE_W, + POPUP_BASE_Y = 0, +}; + +/*........................................................................... +These are the button ID's for the pop-up object-editing gizmos. +The house button ID's must be sequential, with a 1-to-1 correspondence to +the HousesType values. +...........................................................................*/ +enum MapEditButtonIDEnum{ + POPUP_GDI=500, // GDI house button + POPUP_NOD, // NOD house button + POPUP_NEUTRAL, // Neutral house button + POPUP_HOUSE_JP, // not used + POPUP_MULTI1, // Multiplayer 1 house button + POPUP_MULTI2, // Multiplayer 2 house button + POPUP_MULTI3, // Multiplayer 3 house button + POPUP_MULTI4, // Multiplayer 4 house button + POPUP_MULTI5, // Multiplayer 4 house button + POPUP_MULTI6, // Multiplayer 4 house button + POPUP_MISSIONLIST, // list box for missions + POPUP_HEALTHGAUGE, // health of object + POPUP_FACINGDIAL, // object's facing + POPUP_BASEPERCENT, // Base's percent-built slider + MAP_AREA, // map as a click-able thingy + BUTTON_FLAG=0x8000 +}; + + +/* +******************************* Declarations ******************************** +*/ +class TeamTypeClass; + +/* +***************************** Class Declaration ***************************** +*/ +class MapEditClass : public MouseClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /* + ............................. mapedit.cpp ............................. + */ + MapEditClass(void); + virtual void One_Time(void); // One-time init + virtual void Init_IO(void); // Inits button list + virtual void AI(KeyNumType &input, int x, int y); + virtual void Draw_It(bool forced = true); + virtual bool Scroll_Map(DirType facing, int & distance, bool really=true); +// virtual void Flag_To_Redraw(bool complete); + virtual void Read_INI(char *buffer); + virtual void Write_INI(char *buffer); + virtual void Detach(ObjectClass * object); + void Clear_List(void); + bool Add_To_List(ObjectTypeClass const *object); + void Main_Menu(void); + void AI_Menu(void); + bool Mouse_Moved(void); + bool Verify_House(HousesType house, ObjectTypeClass const * objtype); + HousesType Cycle_House(HousesType curhouse, ObjectTypeClass const * objtype); +// int Trigger_Needs_Team(TriggerClass *trigger); + void Fatal(int txt); + + /* + ............................ mapeddlg.cpp ............................. + */ + int New_Scenario(void); + int Load_Scenario(void); + int Save_Scenario(void); + int Pick_Scenario(char const * caption, int *scen_nump, + ScenarioPlayerType *playerp, ScenarioDirType *dirp, + ScenarioVarType *varp, int multi); + int Size_Map(int x, int y, int w, int h); + int Scenario_Dialog(void); + void Handle_Triggers(void); + int Select_Trigger(void); + int Edit_Trigger(void); + int Import_Triggers(void); + int Import_Teams(void); + /* + ............................ mapedplc.cpp ............................. + */ + int Placement_Dialog(void); + void Start_Placement(void); + int Place_Object(void); + void Cancel_Placement(void); + void Place_Next(void); + void Place_Prev(void); + void Place_Next_Category(void); + void Place_Prev_Category(void); + void Place_Home(void); + void Toggle_House(void); + void Set_House_Buttons(HousesType house, GadgetClass *btnlist, int base_id); + void Start_Trigger_Placement(void); + void Stop_Trigger_Placement(void); + void Place_Trigger(void); + void Start_Base_Building(void); + void Cancel_Base_Building(void); + void Build_Base_To(int percent); + + /* + ............................ mapedsel.cpp ............................. + */ + int Select_Object(void); + void Select_Next(void); + void Popup_Controls(void); + void Grab_Object(void); + int Move_Grabbed_Object(void); + bool Change_House(HousesType newhouse); + + /* + ............................. mapedtm.cpp ............................. + */ + void Draw_Member(TechnoTypeClass const * ptr, int index, + int quant, HousesType house, int pic_x, int pic_y); + void Handle_Teams(char const * caption); + int Select_Team(char const * caption); + int Edit_Team(void); + int Team_Members(HousesType house); + void Build_Mission_List(int missioncount, TeamMissionStruct *missions, + char missionbuf[TeamTypeClass::MAX_TEAM_MISSIONS][20], ListClass *list); + + /* + --------------------------- Private Interface ---------------------------- + */ + private: + /*..................................................................... + This is the last-requested variation of a loaded/saved/new scenario. + .....................................................................*/ + ScenarioVarType ScenVar; + + /*..................................................................... + Array of all TypeClasses the user can add to the map; cleared by + Clear_List(), added to by Add_To_List() + .....................................................................*/ + ObjectTypeClass const * Objects[MAX_EDIT_OBJECTS]; + int ObjCount; // # of objects in the Objects array + + /*..................................................................... + Last-selected object to place, and last-selected house of object + .....................................................................*/ + int LastChoice; // index of item user picked last + HousesType LastHouse; // house of last item picked + + /*..................................................................... + Variables for grabbing/moving objects + .....................................................................*/ + ObjectClass * GrabbedObject; // object "grabbed" with mouse + CELL GrabOffset; // offset to grabbed obj's upper-left + unsigned long LastClickTime; // time of last LMOUSE click + + /*..................................................................... + Number of each type of object in Objects, so we can switch categories + .....................................................................*/ + int NumType[NUM_EDIT_CLASSES]; // # of each type of class: + // 0 = Template + // 1 = Overlay + // 2 = Smudge + // 3 = Terrain + // 4 = Unit + // 5 = Infantry + // 6 = Aircraft + // 7 = Building + + /*..................................................................... + The offset of each type of object within the Objects[] array + .....................................................................*/ + int TypeOffset[NUM_EDIT_CLASSES]; // offsets within Objects[] + + /*..................................................................... + The "current" trigger for point-and-click trigger setting + .....................................................................*/ + TriggerClass * CurTrigger; // current trigger + + /*..................................................................... + The "current" team type for editing & associating with a trigger + .....................................................................*/ + TeamTypeClass * CurTeam; // current team + + /*..................................................................... + Bitfields for flags & such + .....................................................................*/ + int Changed : 1; // 1 = changes are unsaved + int LMouseDown : 1; // 1 = left mouse is held down + int BaseBuilding : 1; // 1 = we're in base-building mode + + /*..................................................................... + Variables for pre-building a base + .....................................................................*/ + int BasePercent; // Percentage the base will be built + + /*..................................................................... + Variables for supporting the object-editing controls at screen bottom + .....................................................................*/ + TextButtonClass *GDIButton; + TextButtonClass *NODButton; + TextButtonClass *NeutralButton; + TextButtonClass *Multi1Button; + TextButtonClass *Multi2Button; + TextButtonClass *Multi3Button; + TextButtonClass *Multi4Button; + ListClass *MissionList; + TriColorGaugeClass *HealthGauge; + Dial8Class *FacingDial; + ControlClass *MapArea; + TextLabelClass *HealthText; + static char HealthBuf[20]; + GaugeClass *BaseGauge; + TextLabelClass *BaseLabel; + static MissionType MapEditMissions[]; +}; + +#endif diff --git a/MAPEDPLC.CPP b/MAPEDPLC.CPP new file mode 100644 index 0000000..fe4687e --- /dev/null +++ b/MAPEDPLC.CPP @@ -0,0 +1,1999 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\mapedplc.cpv 2.16 16 Oct 1995 16:51:00 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPEDPLC.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : November 18, 1994 * + * * + * Last Update : July 4, 1995 [JLB] * + * * + *-------------------------------------------------------------------------* + * Object-placement routines * + *-------------------------------------------------------------------------* + * Functions: * + * MapEditClass::Placement_Dialog -- adds an object to the scenario * + * MapEditClass::Start_Placement -- enters placement mode * + * MapEditClass::Place_Object -- attempts to place the current object * + * MapEditClass::Cancel_Placement -- cancels placement mode * + * MapEditClass::Place_Next -- while placing object, goes to next * + * MapEditClass::Place_Prev -- while placing object, goes to previous * + * MapEditClass::Place_Next_Category -- places next object category * + * MapEditClass::Place_Prev_Category -- places previous object category * + * MapEditClass::Place_Home -- homes the placement object * + * MapEditClass::Toggle_House -- toggles current placement object's house* + * MapEditClass::Set_House_Buttons -- toggles house buttons for btn list * + * MapEditClass::Start_Trigger_Placement -- enters trigger placement mode* + * MapEditClass::Stop_Trigger_Placement -- exits trigger placement mode * + * MapEditClass::Place_Trigger -- assigns trigger to object or cell * + * MapEditClass::Start_Base_Building -- starts base-building mode * + * MapEditClass::Cancel_Base_Building -- stops base-building mode * + * MapEditClass::Build_Base_To -- builds the AI base to the given percent* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef SCENARIO_EDITOR + + +/*************************************************************************** + * MapEditClass::Placement_Dialog -- adds an object to the scenario * + * * + * This function sets LastChoice & LastHouse to the values chosen * + * by the user. It's up to the caller to call Start_Placement to enter * + * placement mode. * + * This routine does not modify PendingObject or PendingHouse. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ [GDI] [NOD] [Neutral] ³ * + * ³ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ [Template] ³ * + * ³ ³ ³ [Overlay ] ³ * + * ³ ³ ³ [Smudge ] ³ * + * ³ ³ ³ [Terrain ] ³ * + * ³ ³ (Object picture) ³ [Unit ] ³ * + * ³ ³ ³ [Infantry] ³ * + * ³ ³ ³ [Aircraft] ³ * + * ³ ³ ³ [Building] ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÚÄÄÄÄÄÄ¿ ³ * + * ³ [<-] [->] ³(Grid)³ ³ * + * ³ ³ ³ ³ * + * ³ [OK] [Cancel] ÀÄÄÄÄÄÄÙ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Placement_Dialog(void) +{ + HousesType house; + + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + enum { + D_DIALOG_W = 480, + D_DIALOG_H = 360, + D_DIALOG_X = ((640 - D_DIALOG_W) / 2), + D_DIALOG_Y = ((400 - D_DIALOG_H) / 2), + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), + + D_TXT8_H = 22, + D_MARGIN = 14, + + D_PICTURE_W = 304, // must be divisible by 8! + D_PICTURE_H = 210, + D_PICTURE_X = D_DIALOG_X + 16, // must start on a byte boundary! + D_PICTURE_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H + D_MARGIN, + D_PICTURE_CX = D_PICTURE_X + D_PICTURE_W / 2, + + D_GDI_W = 90, + D_GDI_H = 18, + D_GDI_X = D_DIALOG_X + D_MARGIN, + D_GDI_Y = D_DIALOG_Y + D_MARGIN, + + D_NOD_W = 90, + D_NOD_H = 18, + D_NOD_X = D_GDI_X + D_GDI_W, + D_NOD_Y = D_DIALOG_Y + D_MARGIN, + + D_NEUTRAL_W = 90, + D_NEUTRAL_H = 18, + D_NEUTRAL_X = D_NOD_X + D_NOD_W, + D_NEUTRAL_Y = D_DIALOG_Y + D_MARGIN, + + D_MULTI1_W = 44, + D_MULTI1_H = 18, + D_MULTI1_X = D_GDI_X, + D_MULTI1_Y = D_GDI_Y, + + D_MULTI2_W = 44, + D_MULTI2_H = 18, + D_MULTI2_X = D_MULTI1_X + D_MULTI1_W, + D_MULTI2_Y = D_GDI_Y, + + D_MULTI3_W = 44, + D_MULTI3_H = 18, + D_MULTI3_X = D_MULTI2_X + D_MULTI2_W, + D_MULTI3_Y = D_GDI_Y, + + D_MULTI4_W = 44, + D_MULTI4_H = 18, + D_MULTI4_X = D_MULTI3_X + D_MULTI3_W, + D_MULTI4_Y = D_GDI_Y, + + D_LEFT_W = 90, + D_LEFT_H = 18, + D_LEFT_X = D_PICTURE_CX - 5 - D_LEFT_W, + D_LEFT_Y = D_PICTURE_Y + D_PICTURE_H + D_MARGIN, + + D_RIGHT_W = 90, + D_RIGHT_H = 18, + D_RIGHT_X = D_PICTURE_CX + 5, + D_RIGHT_Y = D_PICTURE_Y + D_PICTURE_H + D_MARGIN, + + D_TEMPLATE_W = 140, + D_TEMPLATE_H = 18, + D_TEMPLATE_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_TEMPLATE_W, + D_TEMPLATE_Y = D_PICTURE_Y, + + D_OVERLAY_W = 140, + D_OVERLAY_H = 18, + D_OVERLAY_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_OVERLAY_W, + D_OVERLAY_Y = D_TEMPLATE_Y + D_TEMPLATE_H, + + D_SMUDGE_W = 140, + D_SMUDGE_H = 18, + D_SMUDGE_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_SMUDGE_W, + D_SMUDGE_Y = D_OVERLAY_Y + D_OVERLAY_H, + + D_TERRAIN_W = 140, + D_TERRAIN_H = 18, + D_TERRAIN_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_TERRAIN_W, + D_TERRAIN_Y = D_SMUDGE_Y + D_SMUDGE_H, + + D_UNIT_W = 140, + D_UNIT_H = 18, + D_UNIT_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_UNIT_W, + D_UNIT_Y = D_TERRAIN_Y + D_TERRAIN_H, + + D_INFANTRY_W = 140, + D_INFANTRY_H = 18, + D_INFANTRY_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_INFANTRY_W, + D_INFANTRY_Y = D_UNIT_Y + D_UNIT_H, + + D_AIRCRAFT_W = 140, + D_AIRCRAFT_H = 18, + D_AIRCRAFT_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_AIRCRAFT_W, + D_AIRCRAFT_Y = D_INFANTRY_Y + D_INFANTRY_H, + + D_BUILDING_W = 140, + D_BUILDING_H = 18, + D_BUILDING_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_BUILDING_W, + D_BUILDING_Y = D_AIRCRAFT_Y + D_AIRCRAFT_H, + + D_OK_W = 90, + D_OK_H = 18, + D_OK_X = D_PICTURE_CX - D_OK_W - 5, + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - D_MARGIN, + + D_CANCEL_W = 90, + D_CANCEL_H = 18, + D_CANCEL_X = D_PICTURE_CX + 5, + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_CANCEL_H - D_MARGIN, + + }; + /*........................................................................ + Grid Dimensions + ........................................................................*/ + enum { + GRIDSIZE = 10, + GRIDBLOCK_W = 6, + GRIDBLOCK_H = 6, + D_GRID_X = D_DIALOG_X + D_DIALOG_W - (GRIDSIZE * GRIDBLOCK_W) - D_MARGIN, + D_GRID_Y = D_DIALOG_Y + D_DIALOG_H - (GRIDSIZE * GRIDBLOCK_H) - D_MARGIN, + }; + /*........................................................................ + Button enumerations: + ........................................................................*/ + enum { + BUTTON_GDI=100, + BUTTON_NOD, + BUTTON_NEUTRAL, + BUTTON_JP, // placeholder + BUTTON_MULTI1, + BUTTON_MULTI2, + BUTTON_MULTI3, + BUTTON_MULTI4, + BUTTON_NEXT, + BUTTON_PREV, + BUTTON_OK, + BUTTON_CANCEL, + BUTTON_TEMPLATE, + BUTTON_OVERLAY, + BUTTON_SMUDGE, + BUTTON_TERRAIN, + BUTTON_UNIT, + BUTTON_INFANTRY, + BUTTON_AIRCRAFT, + BUTTON_BUILDING, + }; + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_OBJECT, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display; // display level + bool process; // loop while true + bool cancel = false; // true = user cancels + const ObjectTypeClass * curobj; // Working object pointer. + int x,y; // for drawing the grid + KeyNumType input; // user input + short const *occupy; // ptr into object's OccupyList + int cell; // cell index for parsing OccupyList + int i; + int typeindex; // index of class type + /*........................................................................ + Buttons + ........................................................................*/ + ControlClass *commands; + + TextButtonClass gdibtn (BUTTON_GDI, "GDI", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_GDI_X, D_GDI_Y, D_GDI_W, D_GDI_H); + + TextButtonClass nodbtn (BUTTON_NOD, "NOD", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NOD_X, D_NOD_Y, D_NOD_W, D_NOD_H); + + TextButtonClass neutbtn (BUTTON_NEUTRAL, "Neutral", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NEUTRAL_X, D_NEUTRAL_Y, D_NEUTRAL_W, D_NEUTRAL_H); + + TextButtonClass multi1btn (BUTTON_MULTI1, "M1", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI1_X, D_MULTI1_Y, D_MULTI1_W, D_MULTI1_H); + + TextButtonClass multi2btn (BUTTON_MULTI2, "M2", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI2_X, D_MULTI2_Y, D_MULTI2_W, D_MULTI2_H); + + TextButtonClass multi3btn (BUTTON_MULTI3, "M3", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI3_X, D_MULTI3_Y, D_MULTI3_W, D_MULTI3_H); + + TextButtonClass multi4btn (BUTTON_MULTI4, "M4", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI4_X, D_MULTI4_Y, D_MULTI4_W, D_MULTI4_H); + + TextButtonClass nextbtn (BUTTON_NEXT, TXT_RIGHT, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_RIGHT_X, D_RIGHT_Y, D_RIGHT_W, D_RIGHT_H); + + TextButtonClass prevbtn (BUTTON_PREV, TXT_LEFT, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_LEFT_X, D_LEFT_Y, D_LEFT_W, D_LEFT_H); + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + TextButtonClass templatebtn (BUTTON_TEMPLATE, "Template", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_TEMPLATE_X, D_TEMPLATE_Y, D_TEMPLATE_W, D_TEMPLATE_H); + + TextButtonClass overlaybtn (BUTTON_OVERLAY, "Overlay", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_OVERLAY_X, D_OVERLAY_Y, D_OVERLAY_W, D_OVERLAY_H); + + TextButtonClass smudgebtn (BUTTON_SMUDGE, "Smudge", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_SMUDGE_X, D_SMUDGE_Y, D_SMUDGE_W, D_SMUDGE_H); + + TextButtonClass terrainbtn (BUTTON_TERRAIN, "Terrain", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_TERRAIN_X, D_TERRAIN_Y, D_TERRAIN_W, D_TERRAIN_H); + + TextButtonClass unitbtn (BUTTON_UNIT, "Unit", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_UNIT_X, D_UNIT_Y, D_UNIT_W, D_UNIT_H); + + TextButtonClass infantrybtn (BUTTON_INFANTRY, "Infantry", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_INFANTRY_X, D_INFANTRY_Y, D_INFANTRY_W, D_INFANTRY_H); + + TextButtonClass aircraftbtn (BUTTON_AIRCRAFT, "Aircraft", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_AIRCRAFT_X, D_AIRCRAFT_Y, D_AIRCRAFT_W, D_AIRCRAFT_H); + + TextButtonClass buildingbtn (BUTTON_BUILDING, "Building", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_BUILDING_X, D_BUILDING_Y, D_BUILDING_W, D_BUILDING_H); + + /*------------------------------------------------------------------------ + Initialize addable objects list; we must do this every time in case one + of the object pools has become exhausted; that object won't be available + for adding. (Skip aircraft, since they won't be used in the editor.) + ------------------------------------------------------------------------*/ + Clear_List(); + TemplateTypeClass::Prep_For_Add(); + OverlayTypeClass::Prep_For_Add(); + SmudgeTypeClass::Prep_For_Add(); + TerrainTypeClass::Prep_For_Add(); + UnitTypeClass::Prep_For_Add(); + InfantryTypeClass::Prep_For_Add(); + BuildingTypeClass::Prep_For_Add(); + + /*........................................................................ + Compute offset of each class type in the Objects array + ........................................................................*/ + TypeOffset[0] = 0; + for (i=1; i= ObjCount) { + LastChoice = 0; + } + curobj = Objects[LastChoice]; // current object to choose + + commands = &neutbtn; + if (ScenPlayer == SCEN_PLAYER_MPLAYER) { + multi1btn.Add_Tail(*commands); + multi2btn.Add_Tail(*commands); + multi3btn.Add_Tail(*commands); + multi4btn.Add_Tail(*commands); + } else { + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); + } + nextbtn.Add_Tail(*commands); + prevbtn.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + templatebtn.Add_Tail(*commands); + overlaybtn.Add_Tail(*commands); + smudgebtn.Add_Tail(*commands); + terrainbtn.Add_Tail(*commands); + unitbtn.Add_Tail(*commands); + infantrybtn.Add_Tail(*commands); + aircraftbtn.Add_Tail(*commands); + buildingbtn.Add_Tail(*commands); + + /*........................................................................ + If the current house isn't valid for the current object type, cycle to + the next house. + ........................................................................*/ + if (!Verify_House(LastHouse,curobj)) { + LastHouse = Cycle_House(LastHouse,curobj); + } + + /* + ..................... Set the buttons for this house ..................... + */ + Set_House_Buttons(LastHouse, commands, BUTTON_GDI); + + /* + -------------------------- Main processing loop -------------------------- + */ + display = REDRAW_ALL; + process = true; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + /* + ---------------------- Refresh display if needed ---------------------- + */ + if (display > REDRAW_NONE) { + /* + ---------------------- Display the dialog box ---------------------- + */ + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + } + + /*------------------------------------------------------------------ + Display the current object: + - save the current window dimensions + - adjust the window size to the actual drawable area + - draw the shape + - reset the window dimensions + ------------------------------------------------------------------*/ + if (display >= REDRAW_OBJECT) { + WindowList[WINDOW_EDITOR][WINDOWX] = D_PICTURE_X >> 3; + WindowList[WINDOW_EDITOR][WINDOWY] = D_PICTURE_Y; + WindowList[WINDOW_EDITOR][WINDOWWIDTH] = D_PICTURE_W >> 3; + WindowList[WINDOW_EDITOR][WINDOWHEIGHT] = D_PICTURE_H; + Change_Window((int)WINDOW_EDITOR); + Draw_Box(D_PICTURE_X, D_PICTURE_Y, D_PICTURE_W, D_PICTURE_H, + BOXSTYLE_GREEN_DOWN, true); + curobj->Display(WinW<<2, WinH>>1, WINDOW_EDITOR, LastHouse); + + /* + ........................ Erase the grid ......................... + */ + LogicPage->Fill_Rect(D_GRID_X - GRIDBLOCK_W * 2, D_GRID_Y, + D_GRID_X + GRIDSIZE * GRIDBLOCK_W, + D_GRID_Y + GRIDSIZE * GRIDBLOCK_H, BLACK); + + /* + .............. Draw a box for every cell occupied ............... + */ + occupy = curobj->Occupy_List(); + while ( (*occupy) != REFRESH_EOL) { + cell = (*occupy); + occupy++; + x = D_GRID_X + ((cell % MAP_CELL_W) * GRIDBLOCK_W); + y = D_GRID_Y + ((cell / MAP_CELL_W) * GRIDBLOCK_H); + LogicPage->Fill_Rect(x, y, x + GRIDBLOCK_W - 1, y + GRIDBLOCK_H - 1, + CC_BRIGHT_GREEN); + } + + /* + ..................... Draw the grid itself ...................... + */ + for (y = 0; y <= GRIDSIZE; y++) { + for (x = 0; x <= GRIDSIZE; x++) { + LogicPage->Draw_Line(D_GRID_X + x * GRIDBLOCK_W, D_GRID_Y, + D_GRID_X + x * GRIDBLOCK_W, + D_GRID_Y + GRIDSIZE * GRIDBLOCK_H, CC_GREEN_SHADOW); + } + LogicPage->Draw_Line(D_GRID_X, D_GRID_Y + y * GRIDBLOCK_H, + D_GRID_X + GRIDSIZE * GRIDBLOCK_W, D_GRID_Y + y * GRIDBLOCK_H, + CC_GREEN_SHADOW); + } + + /*............................................................... + Print the object's label from the class's Full_Name(). + Warning: Text_String returns an EMS pointer, so standard string + functions won't work! + ...............................................................*/ + Fancy_Text_Print (curobj->Full_Name(), + D_PICTURE_CX, D_PICTURE_Y + D_MARGIN, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + + /* + -------------------------- Redraw buttons -------------------------- + */ + if (display >= REDRAW_BUTTONS) { + /*............................................................... + Figure out which class category we're in & highlight that button + This updates 'typeindex', which is used below, and it also updates + the category button states. + ...............................................................*/ + i = 0; + for (typeindex = 0; typeindex < NUM_EDIT_CLASSES; typeindex++) { + i += NumType[typeindex]; + if (LastChoice < i) break; + } + templatebtn.Turn_Off(); + overlaybtn.Turn_Off(); + smudgebtn.Turn_Off(); + terrainbtn.Turn_Off(); + unitbtn.Turn_Off(); + infantrybtn.Turn_Off(); + aircraftbtn.Turn_Off(); + buildingbtn.Turn_Off(); + switch (typeindex + BUTTON_TEMPLATE) { + case BUTTON_TEMPLATE: + templatebtn.Turn_On(); + break; + + case BUTTON_OVERLAY: + overlaybtn.Turn_On(); + break; + + case BUTTON_SMUDGE: + smudgebtn.Turn_On(); + break; + + case BUTTON_TERRAIN: + terrainbtn.Turn_On(); + break; + + case BUTTON_UNIT: + unitbtn.Turn_On(); + break; + + case BUTTON_INFANTRY: + infantrybtn.Turn_On(); + break; + + case BUTTON_AIRCRAFT: + aircraftbtn.Turn_On(); + break; + + case BUTTON_BUILDING: + buildingbtn.Turn_On(); + break; + } + } + + /* + .......................... Redraw buttons .......................... + */ + commands->Draw_All(); + Show_Mouse(); + display = REDRAW_NONE; + + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ------------------------- Process user input -------------------------- + */ + switch (input) { + /* + ---------------------------- GDI House ----------------------------- + */ + case (BUTTON_GDI | KN_BUTTON): + case (BUTTON_NOD | KN_BUTTON): + case (BUTTON_NEUTRAL | KN_BUTTON): + case (BUTTON_MULTI1 | KN_BUTTON): + case (BUTTON_MULTI2 | KN_BUTTON): + case (BUTTON_MULTI3 | KN_BUTTON): + case (BUTTON_MULTI4 | KN_BUTTON): + house = (HousesType)( (input & (~KN_BUTTON)) - BUTTON_GDI); + /* + ............... ignore if invalid for this object ............... + */ + if (!Verify_House(house,curobj)) { + Set_House_Buttons(LastHouse, commands, BUTTON_GDI); + break; + } + + /* + ...................... Set flags & buttons ...................... + */ + LastHouse = house; + Set_House_Buttons(LastHouse, commands, BUTTON_GDI); + display = REDRAW_OBJECT; + break; + + /* + --------------------------- Next in list --------------------------- + */ + case (KN_RIGHT): + case (BUTTON_NEXT | KN_BUTTON): + /* + ..................... Increment to next obj ..................... + */ + LastChoice++; + if (LastChoice == ObjCount) { + LastChoice = 0; + } + curobj = Objects[LastChoice]; + + /* + .................... Get valid house for obj .................... + */ + if (!Verify_House(LastHouse,curobj)) { + LastHouse = Cycle_House(LastHouse,curobj); + Set_House_Buttons(LastHouse, commands, BUTTON_GDI); + } + + nextbtn.Turn_Off(); + display = REDRAW_OBJECT; + break; + + /* + ------------------------- Previous in list ------------------------- + */ + case (KN_LEFT): + case (BUTTON_PREV | KN_BUTTON): + /* + ..................... Decrement to prev obj ..................... + */ + LastChoice--; + if (LastChoice < 0) { + LastChoice = ObjCount-1; + } + curobj = Objects[LastChoice]; + + /* + .................... Get valid house for obj .................... + */ + if (!Verify_House(LastHouse,curobj)) { + LastHouse = Cycle_House(LastHouse,curobj); + Set_House_Buttons(LastHouse, commands, BUTTON_GDI); + } + + prevbtn.Turn_Off(); + display = REDRAW_OBJECT; + break; + + /* + ----------------------- Select a class type ------------------------ + */ + case (BUTTON_TEMPLATE | KN_BUTTON): + case (BUTTON_OVERLAY | KN_BUTTON): + case (BUTTON_SMUDGE | KN_BUTTON): + case (BUTTON_TERRAIN | KN_BUTTON): + case (BUTTON_UNIT | KN_BUTTON): + case (BUTTON_INFANTRY | KN_BUTTON): + case (BUTTON_AIRCRAFT | KN_BUTTON): + case (BUTTON_BUILDING | KN_BUTTON): + /* + ...................... Find index of class ...................... + */ + typeindex = input - (BUTTON_TEMPLATE | KN_BUTTON); + + /* + ............ If no objects of that type, do nothing ............. + */ + if (NumType[typeindex]==0) { + display = REDRAW_BUTTONS; // force to reset button states + break; + } + + /* + ...................... Set current object ....................... + */ + LastChoice = TypeOffset[typeindex]; + curobj = Objects[LastChoice]; + + /* + .................... Get valid house for obj .................... + */ + if (!Verify_House(LastHouse,curobj)) { + LastHouse = Cycle_House(LastHouse,curobj); + Set_House_Buttons(LastHouse, commands, BUTTON_GDI); + } + + display = REDRAW_OBJECT; + break; + + /* + -------------------------- Next category --------------------------- + */ + case KN_PGDN: + typeindex++; + if (typeindex==NUM_EDIT_CLASSES) { + typeindex = 0; + } + + /* + ...................... Set current object ....................... + */ + LastChoice = TypeOffset[typeindex]; + curobj = Objects[LastChoice]; + + /* + .................... Get valid house for obj .................... + */ + if (!Verify_House(LastHouse,curobj)) { + LastHouse = Cycle_House(LastHouse,curobj); + Set_House_Buttons(LastHouse, commands, BUTTON_GDI); + } + + display = REDRAW_OBJECT; + break; + + /* + ------------------------ Previous category ------------------------- + */ + case KN_PGUP: + typeindex--; + if (typeindex < 0) { + typeindex = NUM_EDIT_CLASSES - 1; + } + + /* + ...................... Set current object ....................... + */ + LastChoice = TypeOffset[typeindex]; + curobj = Objects[LastChoice]; + + /* + .................... Get valid house for obj .................... + */ + if (!Verify_House(LastHouse,curobj)) { + LastHouse = Cycle_House(LastHouse,curobj); + Set_House_Buttons(LastHouse, commands, BUTTON_GDI); + } + + display = REDRAW_OBJECT; + break; + + /* + ------------------------ Jump to 1st choice ------------------------ + */ + case KN_HOME: + LastChoice = 0; + /* + ...................... Set current object ....................... + */ + curobj = Objects[LastChoice]; + /* + .................... Get valid house for obj .................... + */ + if (!Verify_House(LastHouse,curobj)) { + LastHouse = Cycle_House(LastHouse,curobj); + Set_House_Buttons(LastHouse, commands, BUTTON_GDI); + } + display = REDRAW_OBJECT; + break; + + /* + -------------------------------- OK -------------------------------- + */ + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + cancel = false; + process = false; + break; + + /* + ------------------------------ Cancel ------------------------------ + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + default: + break; + } + + } + + /* + --------------------------- Redraw the display --------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + if (cancel) { + return(-1); + } + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Start_Placement -- enters placement mode * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/04/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Start_Placement(void) +{ + int i; + + /*------------------------------------------------------------------------ + Initialize addable objects list; we must do this every time in case one + of the object pools has become exhausted; that object won't be available + for adding. + ------------------------------------------------------------------------*/ + Clear_List(); + TemplateTypeClass::Prep_For_Add(); + OverlayTypeClass::Prep_For_Add(); + SmudgeTypeClass::Prep_For_Add(); + TerrainTypeClass::Prep_For_Add(); + UnitTypeClass::Prep_For_Add(); + InfantryTypeClass::Prep_For_Add(); + //AircraftTypeClass::Prep_For_Add(); + BuildingTypeClass::Prep_For_Add(); + /*........................................................................ + Compute offset of each class type in the Objects array + ........................................................................*/ + TypeOffset[0] = 0; + for (i=1; i= ObjCount) + LastChoice = ObjCount - 1; + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(LastHouse)); + } else { + if (LastChoice < TypeOffset[7]) + LastChoice = TypeOffset[7]; + if (LastChoice >= ObjCount) + LastChoice = ObjCount - 1; + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse = Base.House; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(LastHouse)); + } + + + /* + ------------------- Error if no more objects available ------------------- + */ + if (!PendingObjectPtr) { + CCMessageBox().Process("No more objects of this type available."); + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + PendingObject = NULL; + if (BaseBuilding) + Cancel_Base_Building(); + return; + } + + /* + ------------------------ Set the placement cursor ------------------------ + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(PendingObject->Occupy_List()); +} + + +/*************************************************************************** + * MapEditClass::Place_Object -- attempts to place the current object * + * * + * Placement of "real" objects is simply checked via their Unlimbo routine.* + * Placement of templates is more complex: * + * - for every cell in the template's OccupyList, check for objects * + * already in that cell by looking at the cell's OccupyList & * + * OverlapList * + * - "lift" all the objects in the cell by Mark'ing them * + * - temporarily place the template in that cell * + * - try to Unlimbo all the objects that were in the cell. If any * + * objects fail to Unlimbo onto that template, the template cannot * + * be placed here * + * * + * It is assumed that the object being placed is a "new" object; the * + * object's strength & mission are not set during Unlimbo. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = unable to place * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/04/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Place_Object(void) +{ + CELL template_cell; // cell being checked for template + COORDINATE obj_coord; // coord of occupier object + int okflag; // OK to place a template? + short const *occupy; // ptr into template's OccupyList + ObjectClass *occupier; // occupying object + TemplateType save_ttype; // for saving cell's TType + unsigned char save_ticon; // for saving cell's TIcon + BaseNodeClass node; // for adding to an AI Base + + /*------------------------------------------------------------------------ + Placing a template: + - first lift up any objects in the cell + - place the template, and try to replace the objects; if they won't go + back, the template can't go there + ------------------------------------------------------------------------*/ + //ScenarioInit++; + if (PendingObject->What_Am_I() == RTTI_TEMPLATETYPE) { + + /* + .......... Loop through all cells this template will occupy ........... + */ + okflag = true; + occupy = PendingObject->Occupy_List(); + while ((*occupy) != REFRESH_EOL) { + + /* + ................. Check this cell for an occupier .................. + */ + template_cell = (ZoneCell+ZoneOffset) + (*occupy); + if ((*this)[template_cell].Cell_Occupier()) { + occupier = (*this)[template_cell].Cell_Occupier(); + + /* + .................. Save object's coordinates .................... + */ + obj_coord = occupier->Coord; + + /* + ................... Place the object in limbo ................... + */ + occupier->Mark(MARK_UP); + + /* + ................ Set the cell's template values ................. + */ + save_ttype = (*this)[template_cell].TType; + save_ticon = (*this)[template_cell].TIcon; + (*this)[template_cell].TType = + ((TemplateTypeClass *)PendingObject)->Type; + (*this)[template_cell].TIcon = Cell_X(*occupy) + Cell_Y(*occupy) * + ((TemplateTypeClass *)PendingObject)->Width; + (*this)[template_cell].Recalc_Attributes(); + /* + ................ Try to put the object back down ................ + */ + if (occupier->Can_Enter_Cell(Coord_Cell(obj_coord)) != MOVE_OK) { + okflag = false; + } + + /* + .............. Put everything back the way it was ............... + */ + (*this)[template_cell].TType = save_ttype; + (*this)[template_cell].TIcon = save_ticon; + (*this)[template_cell].Recalc_Attributes(); + + /* + .......... Major error if can't replace the object now .......... + */ + occupier->Mark(MARK_DOWN); + } + occupy++; + } + + /* + ......... If it's still OK after ALL THAT, place the template ......... + */ + if (okflag) { + if (PendingObjectPtr->Unlimbo(Cell_Coord(ZoneCell + ZoneOffset))) { + /*............................................................... + Loop through all cells occupied by this template, and clear the + smudge & overlay. + ...............................................................*/ + occupy = PendingObject->Occupy_List(); + while ((*occupy) != REFRESH_EOL) { + /* + ............... Get cell for this occupy item ................ + */ + template_cell = (ZoneCell+ZoneOffset) + (*occupy); + + /* + ................... Clear smudge & overlay ................... + */ + (*this)[template_cell].Overlay = OVERLAY_NONE; + (*this)[template_cell].OverlayData = 0; + (*this)[template_cell].Smudge = SMUDGE_NONE; + + /* + ............ make adjacent cells recalc attrib's ............. + */ + (*this)[template_cell].Recalc_Attributes(); + (*this)[template_cell].Wall_Update(); + (*this)[template_cell].Concrete_Calc(); + + occupy++; + } + + /* + ......................... Set flags etc ......................... + */ + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + Set_Cursor_Shape(0); + //ScenarioInit--; + TotalValue = Overpass(); + Flag_To_Redraw(false); + return(0); + } + + /* + ** Failure to deploy results in a returned failure code. + */ + //ScenarioInit--; + return(-1); + } + + /* + ........................ Not OK; return error ......................... + */ + //ScenarioInit--; + return(-1); + } + + /*------------------------------------------------------------------------ + Placing infantry: Infantry can go into cell sub-positions, so find the + sub-position closest to the mouse & put him there + ------------------------------------------------------------------------*/ + if (PendingObject->What_Am_I() == RTTI_INFANTRYTYPE) { + /* + ....................... Find cell sub-position ........................ + */ + if (Is_Spot_Free(Pixel_To_Coord(Get_Mouse_X(),Get_Mouse_Y()))) { + obj_coord = Closest_Free_Spot(Pixel_To_Coord(Get_Mouse_X(), + Get_Mouse_Y())); + } else { + obj_coord = NULL; + } + + /* + ................ No free spots; don't place the object ................ + */ + if (obj_coord == NULL) { + //ScenarioInit--; + return(-1); + } + + /* + ......................... Unlimbo the object .......................... + */ + if (PendingObjectPtr->Unlimbo(obj_coord)) { + ((InfantryClass *)PendingObjectPtr)->Set_Occupy_Bit(obj_coord); +// Map[Coord_Cell(obj_coord)].Flag.Composite |= +// (1 << CellClass::Spot_Index(obj_coord)); + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + Set_Cursor_Shape(0); + //ScenarioInit--; + return(0); + } + + //ScenarioInit--; + return(-1); + } + + /*------------------------------------------------------------------------ + Placing an object + ------------------------------------------------------------------------*/ + if (PendingObjectPtr->Unlimbo(Cell_Coord(ZoneCell + ZoneOffset))) { + + /* + ** Update the Tiberium computation if we're placing an overlay + */ + if (PendingObject->What_Am_I() == RTTI_OVERLAYTYPE && + ((OverlayTypeClass *)PendingObject)->IsTiberium) { + + TotalValue = Overpass(); + Flag_To_Redraw(false); + } + + /* + ** If we're building a base, add this building to the base's Node list. + */ + if (BaseBuilding && PendingObject->What_Am_I() == RTTI_BUILDINGTYPE) { + node.Type = ((BuildingTypeClass *)PendingObject)->Type; + node.Coord = PendingObjectPtr->Coord; + Base.Nodes.Add(node); + } + + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + Set_Cursor_Shape(0); + //ScenarioInit--; + return(0); + } + + return(-1); +} + + +/*************************************************************************** + * MapEditClass::Cancel_Placement -- cancels placement mode * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/04/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Cancel_Placement(void) +{ + /* + ---------------------- Delete the placement object ----------------------- + */ + delete PendingObjectPtr; + PendingObject = 0; + PendingObjectPtr = 0; + PendingHouse = HOUSE_NONE; + + /* + -------------------------- Restore cursor shape -------------------------- + */ + Set_Cursor_Shape(0); + + /* + ----------------- Redraw the map to erase old leftovers ------------------ + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Place_Next -- while placing object, goes to next * + * * + * - Deletes the current 'PendingObjectPtr' * + * - Increments LastChoice * + * - Tries to create a new 'PendingObjectPtr'; if fails, keeps * + * incrementing until it gets it * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/03/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Next(void) +{ + delete PendingObjectPtr; + PendingObjectPtr = NULL; + PendingObject = NULL; + + /* + ------------------ Loop until we create a valid object ------------------- + */ + while (!PendingObjectPtr) { + /* + ................. Go to next object in Objects list ................... + */ + LastChoice++; + if (LastChoice == ObjCount) { + /* + ** If we're in normal placement mode, wrap to the 1st object; + ** if we're in base-building mode, wrap to the 1st building + */ + if (!BaseBuilding) { + LastChoice = 0; + } else { + LastChoice = TypeOffset[7]; + } + } + + /* + ................... Get house for this object type .................... + */ + if (!Verify_House(LastHouse,Objects[LastChoice])) { + /* + ** If we're in normal placement mode, change the current + ** placement house to the one that can own this object. + ** If we're building a base, skip ahead to the next object if the + ** base's house can't own this one. + */ + if (!BaseBuilding) { + LastHouse = Cycle_House(LastHouse,Objects[LastChoice]); + } else { + continue; + } + } + + /* + ....................... Create placement object ....................... + */ + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(PendingHouse)); + if (!PendingObjectPtr) { + PendingObject = NULL; + } + } + + /* + ------------------------ Set the new cursor shape ------------------------ + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(0); + Set_Cursor_Shape(PendingObject->Occupy_List()); + + /* + ----------------- Redraw the map to erase old leftovers ------------------ + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Place_Prev -- while placing object, goes to previous * + * * + * - Deletes the current 'PendingObjectPtr' * + * - Decrements LastChoice * + * - Tries to create a new 'PendingObjectPtr'; if fails, keeps * + * decrementing until it gets it * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/03/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Prev(void) +{ + delete PendingObjectPtr; + PendingObjectPtr = NULL; + PendingObject = NULL; + + /* + ------------------ Loop until we create a valid object ------------------- + */ + while (!PendingObjectPtr) { + + /* + ................. Go to prev object in Objects list .................. + */ + LastChoice--; + /* + ** If we're in normal placement mode, wrap at the 1st object. + ** If we're building a base, wrap at the 1st building. + */ + if (!BaseBuilding) { + if (LastChoice < 0) + LastChoice = ObjCount - 1; + } else { + if (LastChoice < TypeOffset[7]) + LastChoice = ObjCount - 1; + } + + /* + ................... Get house for this object type .................... + */ + if (!Verify_House(LastHouse,Objects[LastChoice])) { + /* + ** If we're in normal placement mode, change the current + ** placement house to the one that can own this object. + ** If we're building a base, skip ahead to the next object if the + ** base's house can't own this one. + */ + if (!BaseBuilding) { + LastHouse = Cycle_House(LastHouse,Objects[LastChoice]); + } else { + continue; + } + } + + /* + ....................... Create placement object ....................... + */ + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(PendingHouse)); + if (!PendingObjectPtr) { + PendingObject = NULL; + } + } + + /* + ------------------------ Set the new cursor shape ------------------------ + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(0); + Set_Cursor_Shape(PendingObject->Occupy_List()); + + /* + ----------------- Redraw the map to erase old leftovers ------------------ + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Place_Next_Category -- places next category of object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/03/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Next_Category(void) +{ + int i; + + /* + ** Don't allow this command if we're building a base; the only valid + ** category for base-building is buildings. + */ + if (BaseBuilding) { + return; + } + + delete PendingObjectPtr; + PendingObjectPtr = NULL; + PendingObject = NULL; + + /* + ------------------ Go to next category in Objects list ------------------- + */ + i = LastChoice; + while (Objects[i]->What_Am_I() == Objects[LastChoice]->What_Am_I()) { + i++; + if (i == ObjCount) { + i = 0; + } + } + LastChoice = i; + + /* + ------------------ Loop until we create a valid object ------------------- + */ + while (!PendingObjectPtr) { + + /* + ................... Get house for this object type .................... + */ + if (!Verify_House(LastHouse,Objects[LastChoice])) { + LastHouse = Cycle_House(LastHouse,Objects[LastChoice]); + } + + /* + ....................... Create placement object ....................... + */ + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(PendingHouse)); + + /* + .................. If this one failed, try the next ................... + */ + if (!PendingObjectPtr) { + PendingObject = NULL; + LastChoice++; + if (LastChoice == ObjCount) { + LastChoice = 0; + } + } + } + + /* + ------------------------ Set the new cursor shape ------------------------ + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(0); + Set_Cursor_Shape(PendingObject->Occupy_List()); + + /* + ----------------- Redraw the map to erase old leftovers ------------------ + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Place_Prev_Category -- places previous category of object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/03/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Prev_Category(void) +{ + int i; + + /* + ** Don't allow this command if we're building a base; the only valid + ** category for base-building is buildings. + */ + if (BaseBuilding) { + return; + } + + delete PendingObjectPtr; + PendingObjectPtr = NULL; + PendingObject = NULL; + + /* + ------------------ Go to prev category in Objects list ------------------- + */ + i = LastChoice; + /* + ..................... Scan for the previous category ..................... + */ + while (Objects[i]->What_Am_I() == Objects[LastChoice]->What_Am_I()) { + i--; + if (i < 0) { + i = ObjCount - 1; + } + } + /* + .................... Scan for start of this category ..................... + */ + LastChoice = i; + while (Objects[i]->What_Am_I() == Objects[LastChoice]->What_Am_I()) { + LastChoice = i; + i--; + if (i < 0) { + i = ObjCount - 1; + } + } + + /* + ------------------ Loop until we create a valid object ------------------- + */ + while (!PendingObjectPtr) { + /* + ................... Get house for this object type .................... + */ + if (!Verify_House(LastHouse,Objects[LastChoice])) { + LastHouse = Cycle_House(LastHouse,Objects[LastChoice]); + } + + /* + ....................... Create placement object ....................... + */ + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(PendingHouse)); + + /* + .................. If this one failed, try the next ................... + */ + if (!PendingObjectPtr) { + PendingObject = NULL; + LastChoice--; + if (LastChoice < 0) { + LastChoice = ObjCount - 1; + } + } + } + + /* + ------------------------ Set the new cursor shape ------------------------ + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(0); + Set_Cursor_Shape(PendingObject->Occupy_List()); + + /* + ----------------- Redraw the map to erase old leftovers ------------------ + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Place_Home -- homes the placement object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/03/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Home(void) +{ + delete PendingObjectPtr; + PendingObjectPtr = NULL; + PendingObject = NULL; + + /* + ** Don't allow this command if we're building a base; the only valid + ** category for base-building is buildings. + */ + if (BaseBuilding) { + return; + } + + /* + ------------------ Loop until we create a valid object ------------------- + */ + LastChoice = 0; + while (!PendingObjectPtr) { + /* + ................... Get house for this object type .................... + */ + if (!Verify_House(LastHouse,Objects[LastChoice])) { + LastHouse = Cycle_House(LastHouse,Objects[LastChoice]); + } + + /* + ....................... Create placement object ....................... + */ + PendingObject = Objects[LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(PendingHouse)); + + /* + .................. If this one failed, try the next ................... + */ + if (!PendingObjectPtr) { + PendingObject = NULL; + LastChoice++; + if (LastChoice == ObjCount) { + LastChoice = 0; + } + } + } + + /* + ------------------------ Set the new cursor shape ------------------------ + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(0); + Set_Cursor_Shape(PendingObject->Occupy_List()); + + /* + ----------------- Redraw the map to erase old leftovers ------------------ + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Toggle_House -- toggles current placement object's house * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/04/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Toggle_House(void) +{ + TechnoClass *tp; + + /* + ** Don't allow this command if we're building a base; the only valid + ** house for base-building is the one assigned to the base. + */ + if (BaseBuilding) { + return; + } + + /*------------------------------------------------------------------------ + Only techno objects can be owned by a house; return if not a techno + ------------------------------------------------------------------------*/ + if (!PendingObjectPtr->Is_Techno()) { + return; + } + + /*------------------------------------------------------------------------ + Select the house that will own this object + ------------------------------------------------------------------------*/ + LastHouse = Cycle_House(PendingObjectPtr->Owner(), PendingObject); + + /*------------------------------------------------------------------------ + Change the house + ------------------------------------------------------------------------*/ + tp = (TechnoClass *)PendingObjectPtr; + tp->House = HouseClass::As_Pointer(LastHouse); + + /*------------------------------------------------------------------------ + Set house variables to new house + ------------------------------------------------------------------------*/ + PendingHouse = LastHouse; +} + + +/*************************************************************************** + * MapEditClass::Set_House_Buttons -- toggles house buttons for btn list * + * * + * Looks in the given button list for the given GDI, NOD & Neutral button * + * id's. Sets the On/Off state of the buttons based on the given house, * + * only if that button is found in the list. * + * * + * INPUT: * + * house house to set buttons to * + * btnlist ptr to button list to search * + * base_id button ID for GDI; assumes other id's are sequential* + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/23/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Set_House_Buttons(HousesType house, GadgetClass *btnlist, int base_id) +{ + HousesType h; + int id; + TextButtonClass *btn; + + /* + ** Loop through all houses, searching the button list for each one. + */ + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + + /* + ** Compute the desired button ID; get a pointer to the button + */ + id = (int)h + base_id; + btn = (TextButtonClass *)btnlist->Extract_Gadget(id); + if (btn) { + + /* + ** If this house value is the desired one, turn the button on; + ** otherwise, turn it off. + */ + if (h == house) { + btn->Turn_On(); + } else { + btn->Turn_Off(); + } + } + } +} + + +/*************************************************************************** + * MapEditClass::Start_Trigger_Placement -- enters trigger placement mode * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Start_Trigger_Placement(void) +{ + Set_Default_Mouse(MOUSE_CAN_MOVE); + Override_Mouse_Shape(MOUSE_CAN_MOVE); +} + + +/*************************************************************************** + * MapEditClass::Stop_Trigger_Placement -- exits trigger placement mode * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Stop_Trigger_Placement(void) +{ + CurTrigger = NULL; + Set_Default_Mouse(MOUSE_NORMAL); + Override_Mouse_Shape(MOUSE_NORMAL); +} + + +/*************************************************************************** + * MapEditClass::Place_Trigger -- assigns trigger to object or cell * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Trigger(void) +{ + ObjectClass * object=NULL; // Generic object clicked on. + int x,y; + CELL cell; // Cell that was selected. + + /* + -------------------- See if an object was clicked on --------------------- + */ + x = _Kbd->MouseQX; + y = _Kbd->MouseQY; + + /* + ............................ Get cell for x,y ............................ + */ + cell = Click_Cell_Calc(x, y); + + /* + ............... Convert x,y to offset from cell upper-left ............... + */ + x = (x-TacPixelX) % ICON_PIXEL_W; + y = (y-TacPixelY) % ICON_PIXEL_H; + + /* + ......................... Get object at that x,y ......................... + */ + object = Cell_Object(cell, x, y); + + /* + ---------------------- Assign trigger to an object ----------------------- + */ + if (object && TriggerClass::Event_Need_Object(CurTrigger->Event)) { + object->Trigger = CurTrigger; + } else { + + /* + ------------------------ Assign trigger to a cell ------------------------ + */ + if (CurTrigger->Event <= EVENT_OBJECTFIRST) { + Map[cell].IsTrigger = 1; + CellTriggers[cell] = CurTrigger; + } + } + + /* + -------------------------- Force map to redraw --------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); +} + + +/*************************************************************************** + * MapEditClass::Start_Base_Building -- starts base-building mode * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Start_Base_Building(void) +{ + /* + ** Fully build the base so the user can edit it + */ + Build_Base_To(100); + + /* + ** Start placement mode + */ + BaseBuilding = 1; + Start_Placement(); + + /* + ** Force map to redraw + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); +} + + +/*************************************************************************** + * MapEditClass::Cancel_Base_Building -- stops base-building mode * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Cancel_Base_Building(void) +{ + /* + ** Build the base to the proper amount + */ + Build_Base_To(BasePercent); + + /* + ** Cancel placement mode + */ + Cancel_Placement(); + BaseBuilding = 0; + + /* + ** Force map to redraw + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); +} + + +/*************************************************************************** + * MapEditClass::Build_Base_To -- builds the AI base to the given percent * + * * + * INPUT: * + * percent percentage to build base to * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Build_Base_To(int percent) +{ + int i; + int num_buildings; + BuildingTypeClass const *objtype; + BuildingClass *obj; + + //ScenarioInit++; + + /* + ** Completely dismantle the base, so we start at a known point + */ + for (i = 0; i < Base.Nodes.Count(); i++) { + if (Base.Is_Built(i)) { + obj = Base.Get_Building(i); + delete obj; + } + } + + /* + ** Compute number of buildings to build + */ + num_buildings = (Base.Nodes.Count() * percent) / 100; + + /* + ** Build the base to the desired amount + */ + for (i = 0; i < num_buildings; i++) { + /* + ** Get a ptr to the type of building to build, create one, and unlimbo it. + */ + objtype = &BuildingTypeClass::As_Reference(Base.Nodes[i].Type); + obj = (BuildingClass *)objtype->Create_One_Of(HouseClass::As_Pointer(Base.House)); + /* + ** If unlimbo fails, error out + */ + if (!obj->Unlimbo(Base.Nodes[i].Coord)) { + delete obj; + CCMessageBox().Process("Unable to build base!"); + return; + } + } + + //ScenarioInit--; +} + + +#endif diff --git a/MAPEDSEL.CPP b/MAPEDSEL.CPP new file mode 100644 index 0000000..e850b4b --- /dev/null +++ b/MAPEDSEL.CPP @@ -0,0 +1,607 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\mapedsel.cpv 2.18 16 Oct 1995 16:49:58 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPEDSEL.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : November 18, 1994 * + * * + * Last Update : February 2, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * Object-selection & manipulation routines * + *-------------------------------------------------------------------------* + * Functions: * + * MapEditClass::Select_Object -- selects an object for processing * + * MapEditClass::Select_Next -- selects next object on the map * + * MapEditClass::Popup_Controls -- shows/hides the pop-up object controls* + * MapEditClass::Grab_Object -- grabs the current object * + * MapEditClass::Move_Grabbed_Object -- moves the grabbed object * + * MapEditClass::Change_House -- changes CurrentObject's house * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef SCENARIO_EDITOR + + +/*************************************************************************** + * Select_Object -- selects an object for processing * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = object selected, -1 = none * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/04/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Select_Object(void) +{ + ObjectClass *object=NULL; // Generic object clicked on. + int x,y; + CELL cell; // Cell that was selected. + int rc=0; + + /* + -------------------- See if an object was clicked on --------------------- + */ + x = _Kbd->MouseQX; + y = _Kbd->MouseQY; + + /* + ............................ Get cell for x,y ............................ + */ + cell = Click_Cell_Calc(x, y); + + /* + ............... Convert x,y to offset from cell upper-left ............... + */ + x = (x-TacPixelX) % ICON_PIXEL_W; + y = (y-TacPixelY) % ICON_PIXEL_H; + + /* + ......................... Get object at that x,y ......................... + */ + object = Cell_Object(cell, x, y); + + /* + ----------------- If no object, unselect the current one ----------------- + */ + if (!object) { + if (CurrentObject.Count()) { + /* + ................... Unselect all current objects ................... + */ + Unselect_All(); + + /* + ..................... Turn off object controls ..................... + */ + Popup_Controls(); + } + rc = -1; + } else { + + /* + ------------------ Select object only if it's different ------------------ + */ + if (!CurrentObject.Count() || (CurrentObject.Count() && object != CurrentObject[0])) { + /* + ..................... Unselect all current objects .................... + */ + Unselect_All(); + object->Select(); + + /* + ................... Set mouse shape back to normal .................... + */ + Set_Default_Mouse(MOUSE_NORMAL); + Override_Mouse_Shape(MOUSE_NORMAL); + + /* + ....................... Show the popup controls ....................... + */ + Popup_Controls(); + } + } + + /* + -------------------------- Force map to redraw --------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + + return(rc); +} + + +/*************************************************************************** + * MapEditClass::Select_Next -- selects next object on the map * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/22/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Select_Next(void) +{ + ObjectClass * obj; + CELL obj_cell; + int smap_w; // screen map width in icons + int smap_h; // screen map height in icons + int cell_x; // cell-x of next object + int cell_y; // cell-y of next object + int tcell_x; // cell-x of TacticalCell + int tcell_y; // cell-y of TacticalCell + + /* + ----------------------- Get next object on the map ----------------------- + */ + obj = Map.Next_Object(CurrentObject[0]); + + if (obj) { + /* + ............... Unselect current object if there is one ............... + */ + Unselect_All(); + + /* + ......................... Select this object .......................... + */ + obj->Select(); + } + + /* + --------------------- Restore mouse shape to normal ---------------------- + */ + Set_Default_Mouse(MOUSE_NORMAL); + Override_Mouse_Shape(MOUSE_NORMAL); + + /* + -------------------------- Show pop-up controls -------------------------- + */ + Popup_Controls(); + + /* + ---------------- Make sure object is shown on the screen ----------------- + */ + /* + ..................... compute screen map dimensions ...................... + */ + smap_w = Lepton_To_Cell(TacLeptonWidth); + smap_h = Lepton_To_Cell(TacLeptonHeight); + + /* + ...................... compute x,y of object's cell ...................... + */ + obj_cell = Coord_Cell(CurrentObject[0]->Coord); + cell_x = Cell_X(obj_cell); + cell_y = Cell_Y(obj_cell); + tcell_x = Coord_XCell(TacticalCoord); + tcell_y = Coord_YCell(TacticalCoord); + + /* + ................... If object is off-screen, move map .................... + */ + if (cell_x < tcell_x) { + tcell_x = cell_x; + } else { + if (cell_x >= tcell_x + smap_w) { + tcell_x = cell_x - smap_w + 1; + } + } + + if (cell_y < tcell_y) { + tcell_y = cell_y; + } else { + if (cell_y >= tcell_y + smap_h) { + tcell_y = cell_y - smap_h + 1; + } + } + + ScenarioInit++; + Set_Tactical_Position(XY_Coord(Cell_To_Lepton(tcell_x), Cell_To_Lepton(tcell_y))); + ScenarioInit--; + + /* + -------------------------- Force map to redraw --------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); +} + + +/*************************************************************************** + * MapEditClass::Popup_Controls -- shows/hides the pop-up object controls * + * * + * Call this routine whenever the CurrentObject changes. The routine will * + * selectively enable or disable the popup controls based on whether * + * CurrentObject is NULL, or if it's a Techno object, or what type of * + * Techno object it is. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/22/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Popup_Controls(void) +{ + const TechnoTypeClass * objtype = NULL; + HousesType owner; // object's current owner + int mission_index; // object's current mission + int strength; // object's 0-255 strength value + int i; + + /*------------------------------------------------------------------------ + Remove all buttons from GScreen's button list (so none of them provide + input any more); then, destroy the list by Zapping each button. Then, + we'll have to add at least the MapArea button back to the Input button + list before we return, plus any other buttons to process input for. We + always must add MapArea LAST in the list, so it doesn't intercept the + other buttons' input. + ------------------------------------------------------------------------*/ + Remove_A_Button(*GDIButton); + Remove_A_Button(*NODButton); + Remove_A_Button(*NeutralButton); + Remove_A_Button(*Multi1Button); + Remove_A_Button(*Multi2Button); + Remove_A_Button(*Multi3Button); + Remove_A_Button(*Multi4Button); + Remove_A_Button(*MissionList); + Remove_A_Button(*HealthGauge); + Remove_A_Button(*HealthText); + Remove_A_Button(*FacingDial); + Remove_A_Button(*BaseGauge); + Remove_A_Button(*BaseLabel); + Remove_A_Button(*MapArea); + + /* + ------------------ If no current object, hide the list ------------------- + */ + if (!CurrentObject.Count()) { + Add_A_Button(*BaseGauge); + Add_A_Button(*BaseLabel); + Add_A_Button(*MapArea); + return; + } + + /* + --------------- If not Techno, no need for editing buttons --------------- + */ + if (!CurrentObject[0]->Is_Techno()) { + Add_A_Button(*BaseGauge); + Add_A_Button(*BaseLabel); + Add_A_Button(*MapArea); + return; + } + + objtype = (TechnoTypeClass const *)&CurrentObject[0]->Class_Of(); + + /* + ---------------------- Get object's current values ----------------------- + */ + owner = CurrentObject[0]->Owner(); + mission_index = 0; + for (i = 0; i < NUM_EDIT_MISSIONS; i++) { + if (CurrentObject[0]->Get_Mission() == MapEditMissions[i]) { + mission_index = i; + } + } + strength = CurrentObject[0]->Health_Ratio(); + + + /* + ----------------------------- House buttons ------------------------------ + */ + if (ScenPlayer == SCEN_PLAYER_MPLAYER) { + if (Verify_House(HOUSE_NEUTRAL, &CurrentObject[0]->Class_Of())) { + Add_A_Button(*NeutralButton); + } + if (Verify_House(HOUSE_MULTI1, &CurrentObject[0]->Class_Of())) { + Add_A_Button(*Multi1Button); + } + if (Verify_House(HOUSE_MULTI2, &CurrentObject[0]->Class_Of())) { + Add_A_Button(*Multi2Button); + } + if (Verify_House(HOUSE_MULTI3, &CurrentObject[0]->Class_Of())) { + Add_A_Button(*Multi3Button); + } + if (Verify_House(HOUSE_MULTI4, &CurrentObject[0]->Class_Of())) { + Add_A_Button(*Multi4Button); + } + } else { + if (Verify_House(HOUSE_NEUTRAL, &CurrentObject[0]->Class_Of())) { + Add_A_Button(*NeutralButton); + } + if (Verify_House(HOUSE_BAD, &CurrentObject[0]->Class_Of())) { + Add_A_Button(*NODButton); + } + if (Verify_House(HOUSE_GOOD, &CurrentObject[0]->Class_Of())) { + Add_A_Button(*GDIButton); + } + } + + /* + ........................ Set house button states ......................... + */ + if (Buttons) { + Set_House_Buttons(owner, Buttons, POPUP_GDI); + } + + switch (objtype->What_Am_I()) { + case RTTI_UNITTYPE: + case RTTI_INFANTRYTYPE: + case RTTI_AIRCRAFTTYPE: + MissionList->Set_Selected_Index(mission_index); + HealthGauge->Set_Value(strength); + sprintf(HealthBuf, "%d", CurrentObject[0]->Strength); + FacingDial->Set_Direction(((TechnoClass *)CurrentObject[0])->PrimaryFacing); + + /* + ** Make the list. + */ + Add_A_Button(*MissionList); + Add_A_Button(*HealthGauge); + Add_A_Button(*HealthText); + Add_A_Button(*FacingDial); + break; + + case RTTI_BUILDINGTYPE: + HealthGauge->Set_Value(strength); + sprintf(HealthBuf, "%d", CurrentObject[0]->Strength); + Add_A_Button(*HealthGauge); + Add_A_Button(*HealthText); + + if (objtype->IsTurretEquipped) { + FacingDial->Set_Direction(((TechnoClass *) CurrentObject[0])->PrimaryFacing); + Add_A_Button(*FacingDial); + } + break; + } + + /*------------------------------------------------------------------------ + Add the map area last, so it's "underneath" the other buttons, and won't + intercept input for those buttons. + ------------------------------------------------------------------------*/ + Add_A_Button(*BaseGauge); + Add_A_Button(*BaseLabel); + Add_A_Button(*MapArea); +} + + +/*************************************************************************** + * MapEditClass::Grab_Object -- grabs the current object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/07/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Grab_Object(void) +{ + CELL cell; + + if (CurrentObject.Count()) { + GrabbedObject = CurrentObject[0]; + + /*------------------------------------------------------------------------ + Find out which cell 'ZoneCell' is in relation to the object's current cell + ------------------------------------------------------------------------*/ + cell = Coord_Cell(GrabbedObject->Coord); + GrabOffset = cell - ZoneCell; + } +} + + +/*************************************************************************** + * MapEditClass::Move_Grabbed_Object -- moves the grabbed object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = object moved, -1 = it didn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/07/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Move_Grabbed_Object(void) +{ + COORDINATE new_coord = 0; + int retval = -1; + + /* + --------------------------- Lift up the object --------------------------- + */ + GrabbedObject->Mark(MARK_UP); + + /*------------------------------------------------------------------------ + If infantry, use a free spot in this cell + ------------------------------------------------------------------------*/ + if (GrabbedObject->Is_Infantry()) { + + if (Is_Spot_Free(Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()))) { + new_coord = Closest_Free_Spot(Pixel_To_Coord(Get_Mouse_X(), + Get_Mouse_Y())); + /*.................................................................. + Clear the occupied bit in this infantry's cell. + ..................................................................*/ + ((InfantryClass *)GrabbedObject)->Clear_Occupy_Bit(GrabbedObject->Coord); +// Map[Coord_Cell(GrabbedObject->Coord)].Flag.Composite &= +// ~(1 << CellClass::Spot_Index(GrabbedObject->Coord)); + } else { + new_coord = NULL; + } + + } else { + + /*------------------------------------------------------------------------ + Non-infantry: use cell's center coordinate + ------------------------------------------------------------------------*/ + new_coord = Cell_Coord(ZoneCell + GrabOffset); + + if (GrabbedObject->What_Am_I() == RTTI_BUILDING || + GrabbedObject->What_Am_I() == RTTI_TERRAIN) { + + new_coord &= 0xFF00FF00L; + } + + /* + ................ Try to place object at new coordinate ................ + */ + if (GrabbedObject->Can_Enter_Cell(Coord_Cell(new_coord)) != MOVE_OK) { + new_coord = NULL; + } + } + if (new_coord != NULL) { + /* + ** If this object is part of the AI's Base list, change the coordinate + ** in the Base's Node list. + */ + if (GrabbedObject->What_Am_I()==RTTI_BUILDING && + Base.Get_Node((BuildingClass *)GrabbedObject)) + Base.Get_Node((BuildingClass *)GrabbedObject)->Coord = new_coord; + + GrabbedObject->Coord = new_coord; + retval = 0; + } + GrabbedObject->Mark(MARK_DOWN); + + /*------------------------------------------------------------------------ + For infantry, set the bit in its new cell marking that spot as occupied. + ------------------------------------------------------------------------*/ + if (GrabbedObject->Is_Infantry()) { + ((InfantryClass *)GrabbedObject)->Set_Occupy_Bit(new_coord); +// Map[Coord_Cell(new_coord)].Flag.Composite |= +// (1 << CellClass::Spot_Index(new_coord)); + } + + /*------------------------------------------------------------------------ + Re-select the object, and reset the mouse pointer + ------------------------------------------------------------------------*/ + Set_Default_Mouse(MOUSE_NORMAL); + Override_Mouse_Shape(MOUSE_NORMAL); + + Flag_To_Redraw(true); + + return(retval); +} + + +/*************************************************************************** + * MapEditClass::Change_House -- changes CurrentObject's house * + * * + * INPUT: * + * newhouse house to change to * + * * + * OUTPUT: * + * 1 = house was changed, 0 = it wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/17/1994 BR : Created. * + *=========================================================================*/ +bool MapEditClass::Change_House(HousesType newhouse) +{ + TechnoClass *tp; + + /*------------------------------------------------------------------------ + Return if no current object + ------------------------------------------------------------------------*/ + if (!CurrentObject.Count()) { + return(false); + } + + /*------------------------------------------------------------------------ + Only techno objects can be owned by a house; return if not a techno + ------------------------------------------------------------------------*/ + if (!CurrentObject[0]->Is_Techno()) { + return(false); + } + + /*------------------------------------------------------------------------ + You can't change the house if the object is part of the AI's Base. + ------------------------------------------------------------------------*/ + if (CurrentObject[0]->What_Am_I()==RTTI_BUILDING && Base.Is_Node((BuildingClass *)CurrentObject[0])) { + return(false); + } + + /*------------------------------------------------------------------------ + Verify that the target house exists + ------------------------------------------------------------------------*/ + if (HouseClass::As_Pointer(newhouse)==NULL) { + return(false); + } + + /*------------------------------------------------------------------------ + Verify that this is a valid owner + ------------------------------------------------------------------------*/ + if (!Verify_House(newhouse, &CurrentObject[0]->Class_Of())) { + return(false); + } + + /*------------------------------------------------------------------------ + Change the house + ------------------------------------------------------------------------*/ + tp = (TechnoClass *)CurrentObject[0]; + tp->House = HouseClass::As_Pointer(newhouse); + + return(true); +} + + +#endif diff --git a/MAPEDTM.CPP b/MAPEDTM.CPP new file mode 100644 index 0000000..5edc2dc --- /dev/null +++ b/MAPEDTM.CPP @@ -0,0 +1,2077 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\mapedtm.cpv 2.18 16 Oct 1995 16:52:16 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPEDTM.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 7, 1994 * + * * + * Last Update : April 9, 1996 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * MapEditClass::Handle_Teams -- main team-dialog-handling function * + * MapEditClass::Select_Team -- user selects a team from a list * + * MapEditClass::Edit_Team -- user edits a team's options * + * MapEditClass::Team_Members -- user picks makeup of a team * + * MapEditClass::Build_Mission_list -- fills in mission list box * + * MapEditClass::Draw_Member -- Draws a member of the team dialog box. * + * MapEditClass::Team_Members -- Team members dialog * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef SCENARIO_EDITOR + + +/*************************************************************************** + * MapEditClass::Handle_Teams -- main team-dialog-handling function * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/08/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Handle_Teams(char const * caption) +{ + int rc; + + /*------------------------------------------------------------------------ + Team dialog processing loop: + - Invoke the team selection dialog. If a team's selected, break + & return + - If user wants to edit the current team, do so + - If user wants to create new team, new a TeamTypeClass & edit it + - If user wants to delete team, delete the current team + - Keep looping until 'OK' + ------------------------------------------------------------------------*/ + for (;;) { + + /* + ............................. Select team ............................. + */ + rc = Select_Team(caption); + + /* + ............................. 'OK'; break ............................. + */ + if (rc == 0) { + break; + } else { + + /* + ............................... 'Edit' ................................ + */ + if (rc == 1 && CurTeam) { + if (Edit_Team()==0) { + Changed = 1; + } + } else { + + /* + ................................ 'New' ................................ + */ + if (rc == 2) { + /* + ........................ Create a new team ......................... + */ + CurTeam = new TeamTypeClass(); + if (CurTeam) { + /* + ................... delete it if user cancels ................... + */ + if (Edit_Team()==-1) { + delete CurTeam; + CurTeam = NULL; + } else { + Changed = 1; + } + } else { + + /* + ................. Unable to create; issue warning .................. + */ + CCMessageBox().Process("No more teams available."); + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + } + } else { + + /* + .............................. 'Delete' ............................... + */ + if (rc==3) { + if (CurTeam) { + CurTeam->Remove(); + CurTeam = NULL; + } + } + } + } + } + } +} + + +/*************************************************************************** + * MapEditClass::Select_Team -- user selects a team from a list * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Teams ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³ Name House Class:Count,Class:Count ³³ ³ * + * ³ ³ Name House Class:Count,Class:Count ÃÄ´ ³ * + * ³ ³ Name House Class:Count,Class:Count ³ ³ ³ * + * ³ ³ Name House Class:Count,Class:Count ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ÃÄ´ ³ * + * ³ ³ ³³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ ³ * + * ³ [Edit] [New] [Delete] [OK] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, 1 = Edit, 2 = New, 3 = Delete * + * * + * WARNINGS: * + * Uses HIDBUFF. * + * * + * HISTORY: * + * 12/08/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Select_Team(char const * caption) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + enum { + D_DIALOG_W = 528, // dialog width + D_DIALOG_H = 290, // dialog height + D_DIALOG_X = ((640 - D_DIALOG_W) / 2), // centered x-coord + D_DIALOG_Y = ((400 - D_DIALOG_H) / 2), // centered y-coord + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), // coord of x-center + + D_TXT8_H = 22, // ht of 8-pt text + D_MARGIN = 14, // margin width/height + + D_LIST_W = 500, + D_LIST_H = 208, + D_LIST_X = D_DIALOG_X + D_MARGIN, + D_LIST_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H, + + D_EDIT_W = 90, + D_EDIT_H = 18, + D_EDIT_X = D_DIALOG_X + (D_DIALOG_W / 8) - (D_EDIT_W / 2), + D_EDIT_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_EDIT_H, + + D_NEW_W = 90, + D_NEW_H = 18, + D_NEW_X = D_DIALOG_X + (D_DIALOG_W / 8) * 3 - (D_NEW_W / 2), + D_NEW_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_NEW_H, + + D_DELETE_W = 90, + D_DELETE_H = 18, + D_DELETE_X = D_DIALOG_X + (D_DIALOG_W / 8) * 5 - (D_DELETE_W / 2), + D_DELETE_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_DELETE_H, + + D_OK_W = 90, + D_OK_H = 18, + D_OK_X = D_DIALOG_X + (D_DIALOG_W / 8) * 7 - (D_OK_W / 2), + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_OK_H, + + TEAMTXT_LEN = 43, // max length of a team entry + }; + + /*........................................................................ + Button enumerations: + ........................................................................*/ + enum { + TEAM_LIST=100, + BUTTON_EDIT, + BUTTON_NEW, + BUTTON_DELETE, + BUTTON_OK, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display; // requested redraw level + bool process; // loop while true + char *teamtext[TEAMTYPE_MAX + 1]; // text for defined teams + KeyNumType input; // user input + bool edit_team = false; // true = user wants to edit + bool new_team = false; // true = user wants to new + bool del_team = false; // true = user wants to new + int i; // loop counters + int j; + int def_idx; // default list index + static int tabs[] = {120, 180}; // list box tab stops + char txt[10]; +// int housetxt; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands = NULL; // the button list + + ListClass teamlist (TEAM_LIST, + D_LIST_X, D_LIST_Y, D_LIST_W, D_LIST_H, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + Hires_Retrieve("BTN-UP.SHP"), + Hires_Retrieve("BTN-DN.SHP")); + + TextButtonClass editbtn (BUTTON_EDIT, "Edit", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_EDIT_X, D_EDIT_Y, D_EDIT_W, D_EDIT_H); + + TextButtonClass newbtn (BUTTON_NEW, "New", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NEW_X, D_NEW_Y, D_NEW_W, D_NEW_H); + + TextButtonClass deletebtn (BUTTON_DELETE, "Delete", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_DELETE_X, D_DELETE_Y, D_DELETE_W, D_DELETE_H); + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + /* + ........................... Fill in team names ........................... + */ + def_idx = 0; + for (i = 0; i < TeamTypes.Count(); i++) { + /* + ................... Generate string for this team ..................... + */ + //teamtext[i] = (char *)HidPage.Get_Graphic_Buffer()->Get_Buffer() + TEAMTXT_LEN * i; + teamtext[i] = new char[255]; + + /* + ........................ Fill in name & house ......................... + */ + strcpy(teamtext[i],TeamTypes.Ptr(i)->IniName); + strcat(teamtext[i],"\t"); + strcat(teamtext[i], HouseTypeClass::As_Reference(TeamTypes.Ptr(i)->House).Suffix); + strcat(teamtext[i],"\t"); + + /* + ................ Fill in class & count for all classes ................ + */ + for (j = 0; j < TeamTypes.Ptr(i)->ClassCount; j++) { + sprintf (txt,"%s:%d", TeamTypes.Ptr(i)->Class[j]->IniName, TeamTypes.Ptr(i)->DesiredNum[j]); + + /*.................................................................. + Add entry if there's room; break otherwise + (+ 3 for the ", " and the NULL; +3 again for the "..." for the next + entry) + ..................................................................*/ + if (strlen(txt) + strlen(teamtext[i]) + 6 < TEAMTXT_LEN) { + if (j > 0) { + strcat(teamtext[i],", "); + } + strcat(teamtext[i],txt); + } else { + strcat(teamtext[i], "..."); + break; + } + } + + /* + .................. Set def_idx if this is CurTeam ..................... + */ + if (TeamTypes.Ptr(i)==CurTeam) { + def_idx = i; + } + + /* + ........................... Add to list box ........................... + */ + teamlist.Add_Item(teamtext[i]); + } + + /* + ....................... Set CurTeam if it isn't .......................... + */ + if (TeamTypes.Count()==0) { + CurTeam = NULL; + } else { + if (!CurTeam) { + CurTeam = TeamTypes.Ptr(def_idx); + } + } + + /* + ............................ Create the list ............................. + */ + commands = &teamlist; + editbtn.Add_Tail(*commands); + newbtn.Add_Tail(*commands); + deletebtn.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + + /* + ------------------------ Init tab stops for list ------------------------- + */ + teamlist.Set_Tabs(tabs); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + display = REDRAW_ALL; + process = true; + while (process) { + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + + /* + ...................... Display the dialog box ...................... + */ + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + /* + ....................... Draw the captions ....................... + */ + Fancy_Text_Print(caption, D_DIALOG_CX, D_DIALOG_Y + D_MARGIN, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + /* + ........................ Redraw the buttons ........................ + */ + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (TEAM_LIST | KN_BUTTON): + def_idx = teamlist.Current_Index(); + if (def_idx < TeamTypes.Count()) + CurTeam = TeamTypes.Ptr(def_idx); + break; + + case (BUTTON_EDIT | KN_BUTTON): + if (CurTeam) { // only allow if there's one selected + process = false; + edit_team = true; + } + break; + + case (BUTTON_NEW | KN_BUTTON): + process = false; + new_team = true; + break; + + case (BUTTON_DELETE | KN_BUTTON): + process = false; + del_team = true; + break; + + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + } + } + + /* + --------------------------- Redraw the display --------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + for (i = 0; i < TeamTypes.Count(); i++) { + delete [] teamtext[i]; + } + if (edit_team) return(1); + if (new_team) return(2); + if (del_team) return(3); + return(0); +} + + +/*************************************************************************** + * MapEditClass::Edit_Team -- user edits a team's options * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Team Editor ³ * + * ³ ³ * + * ³ Name ______ [Roundabout] ³ * + * ³ Priority ______ [ GDI ] [Learning ] ³ * + * ³ Max Num ______ [ NOD ] [Suicide ] ³ * + * ³ Init Num ______ [Autocreate] ³ * + * ³ Fear ______ [Mercenary ] ³ * + * ³ [Prebuild ] ³ * + * ³ [Reinforce ] ³ * + * ³ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³ ³^³ ³ ³^³ ³ * + * ³ ³ ÃÄ´ [Add >>] ³ ÃÄ´ ³ * + * ³ ³ ³ ³ [Insert] ³ ³ ³ ³ * + * ³ ³ ³ ³ [Delete] ³ ³ ³ ³ * + * ³ ³ ÃÄ´ ____ ³ ÃÄ´ ³ * + * ³ ³ ³v³ ³ ³v³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ ³ * + * ³ [Members] [Cancel] [OK] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * CurTeam must NOT be NULL when this function is called. * + * This routine invokes the Members dialog, which uses HIDBUFF. * + * * + * HISTORY: * + * 12/08/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Edit_Team(void) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + enum { + D_DIALOG_W = 516, + D_DIALOG_H = 376, + D_DIALOG_X = ((640 - D_DIALOG_W) / 2), + D_DIALOG_Y = ((400 - D_DIALOG_H) / 2), + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), + + D_TXT8_H = 22, + D_MARGIN = 14, + + D_NAME_W = 120, + D_NAME_H = 18, + D_NAME_X = D_DIALOG_X + D_MARGIN + 100, + D_NAME_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H, + + D_PRIORITY_W = 120, + D_PRIORITY_H = 18, + D_PRIORITY_X = D_DIALOG_X + D_MARGIN + 100, + D_PRIORITY_Y = D_NAME_Y + D_NAME_H, + + D_MAXNUM_W = 120, + D_MAXNUM_H = 18, + D_MAXNUM_X = D_DIALOG_X + D_MARGIN + 100, + D_MAXNUM_Y = D_PRIORITY_Y + D_PRIORITY_H, + + D_INITNUM_W = 120, + D_INITNUM_H = 18, + D_INITNUM_X = D_DIALOG_X + D_MARGIN + 100, + D_INITNUM_Y = D_MAXNUM_Y + D_MAXNUM_H, + + D_FEAR_W = 120, + D_FEAR_H = 18, + D_FEAR_X = D_DIALOG_X + D_MARGIN + 100, + D_FEAR_Y = D_INITNUM_Y + D_INITNUM_H, + + D_GDI_W = 100, + D_GDI_H = 18, + D_GDI_X = D_NAME_X + D_NAME_W + D_MARGIN, + D_GDI_Y = D_NAME_Y + D_NAME_H + D_NAME_H / 2, + + D_NOD_W = 100, + D_NOD_H = 18, + D_NOD_X = D_NAME_X + D_NAME_W + D_MARGIN, + D_NOD_Y = D_GDI_Y + D_GDI_H, + + D_NEU_W = 100, + D_NEU_H = 18, + D_NEU_X = D_NAME_X + D_NAME_W + D_MARGIN, + D_NEU_Y = D_NOD_Y + D_NOD_H, + + D_MULTI1_W = 50, + D_MULTI1_H = 18, + D_MULTI1_X = D_GDI_X, + D_MULTI1_Y = D_GDI_Y, + + D_MULTI2_W = 50, + D_MULTI2_H = 18, + D_MULTI2_X = D_GDI_X + D_MULTI2_W, + D_MULTI2_Y = D_GDI_Y, + + D_MULTI3_W = 50, + D_MULTI3_H = 18, + D_MULTI3_X = D_NOD_X, + D_MULTI3_Y = D_NOD_Y, + + D_MULTI4_W = 50, + D_MULTI4_H = 18, + D_MULTI4_X = D_NOD_X + D_MULTI4_W, + D_MULTI4_Y = D_NOD_Y, + + D_ROUNDABOUT_W = 130, + D_ROUNDABOUT_H = 18, + D_ROUNDABOUT_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_ROUNDABOUT_W, + D_ROUNDABOUT_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H - 10, + + D_LEARNING_W = D_ROUNDABOUT_W, + D_LEARNING_H = 18, + D_LEARNING_X = D_ROUNDABOUT_X, + D_LEARNING_Y = D_ROUNDABOUT_Y + D_ROUNDABOUT_H, + + D_SUICIDE_W = D_ROUNDABOUT_W, + D_SUICIDE_H = 18, + D_SUICIDE_X = D_ROUNDABOUT_X, + D_SUICIDE_Y = D_LEARNING_Y + D_LEARNING_H, + + D_AUTOCREATE_W = D_ROUNDABOUT_W, + D_AUTOCREATE_H = 18, + D_AUTOCREATE_X = D_ROUNDABOUT_X, + D_AUTOCREATE_Y = D_SUICIDE_Y + D_SUICIDE_H, + + D_MERCENARY_W = D_ROUNDABOUT_W, + D_MERCENARY_H = 18, + D_MERCENARY_X = D_ROUNDABOUT_X, + D_MERCENARY_Y = D_AUTOCREATE_Y + D_AUTOCREATE_H, + + D_PREBUILT_W = D_ROUNDABOUT_W, + D_PREBUILT_H = 18, + D_PREBUILT_X = D_ROUNDABOUT_X, + D_PREBUILT_Y = D_MERCENARY_Y + D_MERCENARY_H, + + D_REINFORCE_W = D_ROUNDABOUT_W, + D_REINFORCE_H = 18, + D_REINFORCE_X = D_ROUNDABOUT_X, + D_REINFORCE_Y = D_PREBUILT_Y + D_PREBUILT_H, + + D_MISSION1_W = 180, + D_MISSION1_H = 128, + D_MISSION1_X = D_DIALOG_X + D_MARGIN, + D_MISSION1_Y = D_REINFORCE_Y + D_REINFORCE_H + D_MARGIN, + + D_MISSION2_W = 180, + D_MISSION2_H = 128, + D_MISSION2_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_MISSION2_W, + D_MISSION2_Y = D_MISSION1_Y, + + D_ADD_W = 100, + D_ADD_H = 18, + D_ADD_X = D_MISSION1_X + D_MISSION1_W + D_MARGIN, + D_ADD_Y = D_MISSION1_Y + D_ADD_H, + + D_INSERT_W = 100, + D_INSERT_H = 18, + D_INSERT_X = D_MISSION1_X + D_MISSION1_W + D_MARGIN, + D_INSERT_Y = D_ADD_Y + D_ADD_H, + + D_DEL_W = 100, + D_DEL_H = 18, + D_DEL_X = D_MISSION1_X + D_MISSION1_W + D_MARGIN, + D_DEL_Y = D_INSERT_Y + D_INSERT_H, + + D_ARG_W = 100, + D_ARG_H = 18, + D_ARG_X = D_MISSION1_X + D_MISSION1_W + D_MARGIN, + D_ARG_Y = D_DEL_Y + D_DEL_H, + + D_MEMBERS_W = 100, + D_MEMBERS_H = 18, + D_MEMBERS_X = D_DIALOG_X + (D_DIALOG_W / 6) - D_MEMBERS_W / 2, + D_MEMBERS_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_MEMBERS_H, + + D_CANCEL_W = 100, + D_CANCEL_H = 18, + D_CANCEL_X = D_DIALOG_X + (D_DIALOG_W / 6) * 3 - D_CANCEL_W / 2, + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_CANCEL_H, + + D_OK_W = 100, + D_OK_H = 18, + D_OK_X = D_DIALOG_X + (D_DIALOG_W / 6) * 5 - D_OK_W / 2, + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_MARGIN - D_OK_H, + }; + + /*........................................................................ + Button enumerations: + ........................................................................*/ + enum { + BUTTON_NAME=100, + BUTTON_RECRUIT, + BUTTON_MAXNUM, + BUTTON_INITNUM, + BUTTON_FEAR, + BUTTON_GDI, + BUTTON_NOD, + BUTTON_NEU, + BUTTON_JP, // placeholder + BUTTON_MULTI1, + BUTTON_MULTI2, + BUTTON_MULTI3, + BUTTON_MULTI4, + BUTTON_MULTI5, + BUTTON_MULTI6, + BUTTON_ROUNDABOUT, + BUTTON_LEARNING, + BUTTON_SUICIDE, + BUTTON_AUTO, + BUTTON_MERCENARY, + BUTTON_PREBUILT, + BUTTON_REINFORCE, + BUTTON_MISSION1, + BUTTON_MISSION2, + BUTTON_ADD, + BUTTON_INSERT, + BUTTON_DEL, + BUTTON_ARG, + BUTTON_MEMBERS, + BUTTON_OK, + BUTTON_CANCEL, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables: + ........................................................................*/ + RedrawType display; // requested redraw level + bool process; // loop while true + KeyNumType input; + bool cancel = false; // true = user cancels + char name_buf[12]; + char recr_buf[4]; + char maxnum_buf[4]; + char initnum_buf[4]; + char fear_buf[4]; + HousesType house; + int roundabout; + int learning; + int suicide; + int autocreate; + int mercenary; + int prebuilt; + int reinforce; + int missioncount; + TeamMissionStruct missions[TeamTypeClass::MAX_TEAM_MISSIONS]; + char missionbuf[TeamTypeClass::MAX_TEAM_MISSIONS][20]; + int curmission; // currently-selected mission index + + char arg_buf[4] = {0}; + static int tabs[] = {130, 180}; // list box tab stops + int i,j; + + /*........................................................................ + Buttons: + ........................................................................*/ + ControlClass *commands; + EditClass name_edt (BUTTON_NAME, + name_buf, 8, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NAME_X, D_NAME_Y, D_NAME_W, D_NAME_H, EditClass::ALPHANUMERIC); + + EditClass recr_edt (BUTTON_RECRUIT, + recr_buf, 3, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_PRIORITY_X, D_PRIORITY_Y, D_PRIORITY_W, D_PRIORITY_H, EditClass::NUMERIC); + + EditClass maxnum_edt (BUTTON_MAXNUM, + maxnum_buf, 3, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MAXNUM_X, D_MAXNUM_Y, D_MAXNUM_W, D_MAXNUM_H, EditClass::NUMERIC); + + EditClass initnum_edt (BUTTON_INITNUM, + initnum_buf, 3, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_INITNUM_X, D_INITNUM_Y, D_INITNUM_W, D_INITNUM_H, EditClass::NUMERIC); + + EditClass fear_edt (BUTTON_FEAR, + fear_buf, 3, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_FEAR_X, D_FEAR_Y, D_FEAR_W, D_FEAR_H, EditClass::NUMERIC); + + TextButtonClass gdibtn (BUTTON_GDI, "GDI", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_GDI_X, D_GDI_Y, D_GDI_W, D_GDI_H); + + TextButtonClass nodbtn (BUTTON_NOD, "NOD", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NOD_X, D_NOD_Y, D_NOD_W, D_NOD_H); + + TextButtonClass neubtn (BUTTON_NEU, "NEUTRAL", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_NEU_X, D_NEU_Y, D_NEU_W, D_NEU_H); + + TextButtonClass multi1btn (BUTTON_MULTI1, "M1", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI1_X, D_MULTI1_Y, D_MULTI1_W, D_MULTI1_H); + + TextButtonClass multi2btn (BUTTON_MULTI2, "M2", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI2_X, D_MULTI2_Y, D_MULTI2_W, D_MULTI2_H); + + TextButtonClass multi3btn (BUTTON_MULTI3, "M3", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI3_X, D_MULTI3_Y, D_MULTI3_W, D_MULTI3_H); + + TextButtonClass multi4btn (BUTTON_MULTI4, "M4", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI4_X, D_MULTI4_Y, D_MULTI4_W, D_MULTI4_H); + + TextButtonClass roundbtn (BUTTON_ROUNDABOUT, "Roundabout", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_ROUNDABOUT_X, D_ROUNDABOUT_Y, D_ROUNDABOUT_W, D_ROUNDABOUT_H); + + TextButtonClass learnbtn (BUTTON_LEARNING, "Learning", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_LEARNING_X, D_LEARNING_Y, D_LEARNING_W, D_LEARNING_H); + + TextButtonClass suicidebtn (BUTTON_SUICIDE, "Suicide", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_SUICIDE_X, D_SUICIDE_Y, D_SUICIDE_W, D_SUICIDE_H); + + TextButtonClass autocreatebtn (BUTTON_AUTO, "Autocreate", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_AUTOCREATE_X, D_AUTOCREATE_Y, D_AUTOCREATE_W, D_AUTOCREATE_H); + + TextButtonClass mercbtn (BUTTON_MERCENARY, "Mercenary", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MERCENARY_X, D_MERCENARY_Y, D_MERCENARY_W, D_MERCENARY_H); + + TextButtonClass prebuiltbtn (BUTTON_PREBUILT, "Prebuild", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_PREBUILT_X, D_PREBUILT_Y, D_PREBUILT_W, D_PREBUILT_H); + + TextButtonClass reinforcebtn (BUTTON_REINFORCE, "Reinforce", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_REINFORCE_X, D_REINFORCE_Y, D_REINFORCE_W, D_REINFORCE_H); + + ListClass missionlist1 (BUTTON_MISSION1, + D_MISSION1_X, D_MISSION1_Y, D_MISSION1_W, D_MISSION1_H, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + Hires_Retrieve("BTN-UP.SHP"), + Hires_Retrieve("BTN-DN.SHP")); + + ListClass missionlist2 (BUTTON_MISSION2, + D_MISSION2_X, D_MISSION2_Y, D_MISSION2_W, D_MISSION2_H, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + Hires_Retrieve("BTN-UP.SHP"), + Hires_Retrieve("BTN-DN.SHP")); + + TextButtonClass addbtn (BUTTON_ADD, "Add >>", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_ADD_X, D_ADD_Y, D_ADD_W, D_ADD_H); + + TextButtonClass insertbtn (BUTTON_INSERT, "Insert", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_INSERT_X, D_INSERT_Y, D_INSERT_W, D_INSERT_H); + + TextButtonClass delbtn (BUTTON_DEL, "Delete", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_DEL_X, D_DEL_Y, D_DEL_W, D_DEL_H); + + EditClass arg_edt (BUTTON_ARG, arg_buf, 4, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_ARG_X, D_ARG_Y, D_ARG_W, D_ARG_H, EditClass::ALPHANUMERIC); + + TextButtonClass membersbtn (BUTTON_MEMBERS, "Members", + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MEMBERS_X, D_MEMBERS_Y, D_MEMBERS_W, D_MEMBERS_H); + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + /* + ........................... Copy team's state ............................ + */ + strcpy(name_buf,CurTeam->IniName); + sprintf(recr_buf,"%d",CurTeam->RecruitPriority); + sprintf(maxnum_buf,"%d",CurTeam->MaxAllowed); + sprintf(initnum_buf,"%d",CurTeam->InitNum); + sprintf(fear_buf,"%d",CurTeam->Fear); + roundabout = CurTeam->IsRoundAbout; + learning = CurTeam->IsLearning; + suicide = CurTeam->IsSuicide; + house = CurTeam->House; + autocreate = CurTeam->IsAutocreate; + mercenary = CurTeam->IsMercenary; + prebuilt = CurTeam->IsPrebuilt; + reinforce = CurTeam->IsReinforcable; + + /* + ......................... Fill in mission lists .......................... + */ + for (i = 0; i < TMISSION_COUNT; i++) { + missionlist1.Add_Item(TeamTypeClass::Name_From_Mission((TeamMissionType)i)); + } + + missioncount = CurTeam->MissionCount; + for (i = 0; i < missioncount; i++) { + missions[i] = CurTeam->MissionList[i]; + } + Build_Mission_List(missioncount, missions, missionbuf, &missionlist2); + + curmission = 0; + if (missioncount) { + if (missions[curmission].Mission == TMISSION_MOVE || missions[curmission].Mission == TMISSION_UNLOAD) { + sprintf(arg_buf,"%c",missions[curmission].Argument + 'A'); + } else { + sprintf(arg_buf,"%d",missions[curmission].Argument); + } + } + missionlist2.Set_Tabs(tabs); + + /* + ......................... Init the button states ......................... + */ + name_edt.Set_Text(name_buf,8); + recr_edt.Set_Text(recr_buf,3); + maxnum_edt.Set_Text(maxnum_buf,3); + initnum_edt.Set_Text(initnum_buf,3); + fear_edt.Set_Text(fear_buf,3); + arg_edt.Set_Text(arg_buf,3); + + if (roundabout) { + roundbtn.Turn_On(); + } + if (learning) { + learnbtn.Turn_On(); + } + if (suicide) { + suicidebtn.Turn_On(); + } + if (autocreate) { + autocreatebtn.Turn_On(); + } + if (mercenary) { + mercbtn.Turn_On(); + } + if (reinforce) { + reinforcebtn.Turn_On(); + } + if (prebuilt) { + prebuiltbtn.Turn_On(); + } + + /* + ............................ Create the list ............................. + */ + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + membersbtn.Add_Tail(*commands); + + name_edt.Add_Tail(*commands); + recr_edt.Add_Tail(*commands); + maxnum_edt.Add_Tail(*commands); + initnum_edt.Add_Tail(*commands); + fear_edt.Add_Tail(*commands); + + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); + neubtn.Add_Tail(*commands); + + roundbtn.Add_Tail(*commands); + learnbtn.Add_Tail(*commands); + suicidebtn.Add_Tail(*commands); + autocreatebtn.Add_Tail(*commands); + mercbtn.Add_Tail(*commands); + prebuiltbtn.Add_Tail(*commands); + reinforcebtn.Add_Tail(*commands); + + missionlist1.Add_Tail(*commands); + missionlist2.Add_Tail(*commands); + addbtn.Add_Tail(*commands); + insertbtn.Add_Tail(*commands); + delbtn.Add_Tail(*commands); + arg_edt.Add_Tail(*commands); + + Set_House_Buttons (house, commands, BUTTON_GDI); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + display = REDRAW_ALL; + process = true; + while (process) { + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + + /* + ...................... Display the dialog box ...................... + */ + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + /* + ....................... Draw the captions ....................... + */ + Fancy_Text_Print("Team Edit", D_DIALOG_CX, D_DIALOG_Y + D_MARGIN, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print("Name", D_NAME_X - 5, D_NAME_Y, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print("Priority", D_PRIORITY_X - 5, D_PRIORITY_Y, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print("Max Num", D_MAXNUM_X - 5, D_MAXNUM_Y, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print("Init Num", D_INITNUM_X - 5, D_INITNUM_Y, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print("Fear", D_FEAR_X - 5, D_FEAR_Y, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + /* + ........................ Redraw the buttons ........................ + */ + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (BUTTON_NAME | KN_BUTTON): + break; + + case (BUTTON_RECRUIT | KN_BUTTON): + break; + + case (BUTTON_MAXNUM | KN_BUTTON): + break; + + case (BUTTON_INITNUM | KN_BUTTON): + break; + + case (BUTTON_FEAR | KN_BUTTON): + break; + + /*.................................................................. + Toggle RoundAbout + ..................................................................*/ + case (BUTTON_ROUNDABOUT | KN_BUTTON): + if (roundabout) { + roundabout = 0; + roundbtn.Turn_Off(); + } else { + roundabout = 1; + roundbtn.Turn_On(); + } + break; + + /*.................................................................. + Toggle Learning + ..................................................................*/ + case (BUTTON_LEARNING | KN_BUTTON): + if (learning) { + learning = 0; + learnbtn.Turn_Off(); + } else { + learning = 1; + learnbtn.Turn_On(); + } + break; + + /*.................................................................. + Toggle Suicide + ..................................................................*/ + case (BUTTON_SUICIDE | KN_BUTTON): + if (suicide) { + suicide = 0; + suicidebtn.Turn_Off(); + } else { + suicide = 1; + suicidebtn.Turn_On(); + } + break; + + /*.................................................................. + Toggle Spy + ..................................................................*/ + case (BUTTON_AUTO | KN_BUTTON): + if (autocreate) { + autocreate = 0; + autocreatebtn.Turn_Off(); + } else { + autocreate = 1; + autocreatebtn.Turn_On(); + } + break; + + /*.................................................................. + Toggle Mercenary + ..................................................................*/ + case (BUTTON_MERCENARY | KN_BUTTON): + if (mercenary) { + mercenary = 0; + mercbtn.Turn_Off(); + } else { + mercenary = 1; + mercbtn.Turn_On(); + } + break; + + case (BUTTON_PREBUILT | KN_BUTTON): + if (prebuilt) { + prebuilt = 0; + prebuiltbtn.Turn_Off(); + } else { + prebuilt = 1; + prebuiltbtn.Turn_On(); + } + break; + + case (BUTTON_REINFORCE | KN_BUTTON): + if (reinforce) { + reinforce = 0; + reinforcebtn.Turn_Off(); + } else { + reinforce = 1; + reinforcebtn.Turn_On(); + } + break; + + /*.................................................................. + Select a Mission on the left-hand mission list + ..................................................................*/ + case (BUTTON_MISSION1 | KN_BUTTON): + break; + + /*.................................................................. + Select a Mission on the right-hand mission list; update the Argument + field to reflect the current value + ..................................................................*/ + case (BUTTON_MISSION2 | KN_BUTTON): + if (missionlist2.Count() > 0 && + missionlist2.Current_Index() != curmission) { + curmission = missionlist2.Current_Index(); + if (missions[curmission].Mission==TMISSION_MOVE || missions[curmission].Mission == TMISSION_UNLOAD) { + sprintf(arg_buf,"%c",missions[curmission].Argument + 'A'); + } else { + sprintf(arg_buf,"%d",missions[curmission].Argument); + } + arg_edt.Set_Text(arg_buf,3); + } + break; + + /*.................................................................. + Copy mission from left list box to right list box + ..................................................................*/ + case (BUTTON_ADD | KN_BUTTON): + case (BUTTON_INSERT | KN_BUTTON): + if (missioncount < TeamTypeClass::MAX_TEAM_MISSIONS) { + /* + ** Set 'i' to the position we're going to add into; this will + ** be just AFTER the current item if we're adding, and it will + ** be the current item if we're inserting. + */ + if (input == (BUTTON_ADD | KN_BUTTON)) { + i = missionlist2.Current_Index() + 1; + if (i < 0) { + i = 0; + } + if (i > missioncount) { + i = missioncount; + } + } else { + i = missionlist2.Current_Index(); + if (i < 0) { + i = 0; + } + if (i >= missioncount && missioncount > 0) { + i = missioncount - 1; + } + } + + /* + ** Move all other missions forward in the array + */ + for (j = missioncount; j > i; j--) { + missions[j] = missions[j - 1]; + } + + /* + ** Set the Mission value based on 1st list box's index + */ + missions[i].Mission = (TeamMissionType)(TMISSION_FIRST + missionlist1.Current_Index()); + + /* + ** Set the missions argument field + */ + if (missions[i].Mission == TMISSION_MOVE || missions[i].Mission == TMISSION_UNLOAD) { + missions[i].Argument = toupper(arg_buf[0]) - 'A'; + } else { + missions[i].Argument = atoi(arg_buf); + } + missioncount++; + + /* + ** Rebuild the list box from scratch + */ + Build_Mission_List(missioncount, missions, missionbuf, &missionlist2); + + /* + ** Update the list's current item index + */ + missionlist2.Set_Selected_Index(i); + } + break; + + /*.................................................................. + Delete mission from right-hand list box + ..................................................................*/ + case (BUTTON_DEL | KN_BUTTON): + if (missioncount > 0) { + i = missionlist2.Current_Index(); + if (i < 0 || i >= missioncount) { + break; + } + + /* + ** Move all missions back in the array + */ + for (j = i; j < missioncount - 1; j++) { + missions[j] = missions[j + 1]; + } + missioncount--; + + /* + ** Rebuild the list box from scratch + */ + Build_Mission_List(missioncount, missions, missionbuf, &missionlist2); + + /* + ** Update the list's current item index + */ + if (i >= missioncount) { + i--; + if (i < 0) { + i = 0; + } + missionlist2.Set_Selected_Index(i); + } + } + break; + + /*.................................................................. + Set house + ..................................................................*/ + case (BUTTON_GDI | KN_BUTTON): + case (BUTTON_NOD | KN_BUTTON): + case (BUTTON_NEU | KN_BUTTON): + case (BUTTON_MULTI1 | KN_BUTTON): + case (BUTTON_MULTI2 | KN_BUTTON): + case (BUTTON_MULTI3 | KN_BUTTON): + case (BUTTON_MULTI4 | KN_BUTTON): + house = (HousesType)( (input & (~KN_BUTTON)) - BUTTON_GDI); + Set_House_Buttons(house, commands, BUTTON_GDI); + break; + + /*.................................................................. + Invoke the members dialog + ..................................................................*/ + case (BUTTON_MEMBERS | KN_BUTTON): + /* + .................... Take editor focus away ..................... + */ + membersbtn.Turn_Off(); + + /* + ....................... Invoke the dialog ....................... + */ + Team_Members(house); + + /* + ............................ Redraw ............................. + */ + display = REDRAW_ALL; + break; + + /*.................................................................. + OK: return + ..................................................................*/ + case (BUTTON_OK | KN_BUTTON): + cancel = false; + process = false; + break; + + /*.................................................................. + Cancel: return + ..................................................................*/ + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + /*.................................................................. + Pass all other events to the currently-active text editor + ..................................................................*/ + default: + break; + } + } + + /* + --------------------------- Redraw the display --------------------------- + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ------------------------- If cancel, just return ------------------------- + */ + if (cancel) { + return(-1); + } + + /* + ------------------------ Save selections & return ------------------------ + */ + CurTeam->Set_Name(name_buf); + CurTeam->RecruitPriority = atoi(recr_buf); + CurTeam->MaxAllowed = atoi(maxnum_buf); + CurTeam->InitNum = atoi(initnum_buf); + CurTeam->IsRoundAbout = roundabout; + CurTeam->IsLearning = learning; + CurTeam->IsSuicide = suicide; + CurTeam->IsAutocreate = autocreate; + CurTeam->IsPrebuilt = prebuilt; + CurTeam->IsReinforcable = reinforce; + CurTeam->IsMercenary = mercenary; + CurTeam->House = house; + CurTeam->MissionCount = missioncount; + for (i = 0 ; i < missioncount; i++) { + CurTeam->MissionList[i] = missions[i]; + } + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Team_Members -- user picks makeup of a team * + * * + * Team members are rendered in a 24 x 24 area; the Window coordinates * + * have to be set to this area when the object's 'Display()' routine is * + * called. Thus, the dialog's window coords have to be divisible by * + * 24. The height of the dialog is computed based on how many objects * + * there are in it. * + * * + * 10 pixels are left between rows of objects, so the # of that type of * + * object can be displayed underneath the object. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Team Members ³ * + * ³ ³ * + * ³ ÚÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄ¿ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÀÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÙ ³ * + * ³ [OK] [Cancel] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * house house to display objects for * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * CurTeam must NOT be NULL when this function is called. * + * This routine uses HIDBUFF for data storage. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + *=========================================================================*/ +#define TEENSY_WEENSY +/* +** Dialog & button dimensions +*/ +enum { + D_DIALOG_W = 608, + D_DIALOG_X = ((640 - D_DIALOG_W) / 2), + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), + + D_TXT6_H = 14, + D_MARGIN = 14, + +#ifdef TEENSY_WEENSY + //D_PICTURE_W = 32, + //D_PICTURE_H = 24, + D_PICTURE_W = 64, // 9 pictures / row, 16 pixel margin on each side + D_PICTURE_H = 48, +#else + //D_PICTURE_W = 32, + //D_PICTURE_H = 30, + D_PICTURE_W = 64, + D_PICTURE_H = 60, +#endif + D_ROW_H = (D_PICTURE_H + 6), + + D_OK_W = 100, + D_OK_H = 18, + D_OK_X = D_DIALOG_CX - 10 - D_OK_W, + D_OK_Y = 0, + + D_CANCEL_W = 100, + D_CANCEL_H = 18, + D_CANCEL_X = D_DIALOG_CX + 10, + D_CANCEL_Y = 0, + +}; + +/*************************************************************************** + * MapEditClass::Team_Members -- Team members dialog * + * * + * INPUT: * + * house house to show members for * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/09/1996 BRR : Created. * + *=========================================================================*/ +int MapEditClass::Team_Members(HousesType house) +{ + /* + ** Button enumerations: + */ + enum { + BUTTON_OK = 100, + BUTTON_CANCEL, + }; + + /* + ** Redraw values: in order from "top" to "bottom" layer of the dialog + ** (highest enum is the lowest layer). Each section of the map checks + ** the requested redraw level to see if it's supposed to draw; if it's + ** >= its level, it redraws. + */ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + RedrawType display; // requested redraw level + bool process; // loop while true + + /* + ............................ Dialog variables ............................ + */ + KeyNumType input; // user input + bool cancel = false; // true = user cancels + + /* + ......................... Team display variables ......................... + */ + const TechnoTypeClass **teamclass; // array of team classes + int *teamcount; // array of class counts + int numcols; // # units displayed horizontally + int numrows; // # units displayed vertically +// int col; // horizontal picture index +// int row; // vertical picture index +// int x,y; + + /* + ** Dialog dimensions. + */ + int dlg_y; + int dlg_h; // dialog height + int dlg_picture_top; // coord of top of pictures + int msg_y; // y-coord for object names + + /* + ** Values for parsing the classes. + */ + InfantryType i_id; + AircraftType a_id; + UnitType u_id; + int curclass = -1; // current index into 'teamclass'; can be invalid! + // (is based on current mouse position) + int numclasses; // current # classes in the team (limited to <=5) + int maxclasses; // max # classes available + int i,j; + + /* + ** Values for timing when mouse held down. + */ + int lheld = 0; + int rheld = 0; + long tdelay[3] = {5, 20, 0}; + int tindex = 0; + long heldtime; + + /* + ** Buttons. + */ + ControlClass *commands; + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + /* + ** Set up the team data arrays (ObjectTypeClass pointers & count) + */ + teamclass = (const TechnoTypeClass **)(new TechnoTypeClass *[MAX_TEAM_CLASSES]); + teamcount = new int[MAX_TEAM_CLASSES]; + + /* + ** Fill in the ObjectTypeClass array with all available object type ptrs, + ** checking to be sure this house can own the object + */ + i = 0; + for (i_id = INFANTRY_FIRST; i_id < INFANTRY_COUNT; i_id++) { + if (Verify_House(house,&InfantryTypeClass::As_Reference(i_id))) { + teamclass[i] = &InfantryTypeClass::As_Reference(i_id); + i++; + } + } + + for (a_id = AIRCRAFT_FIRST; a_id < AIRCRAFT_COUNT; a_id++) { + if (Verify_House(house,&AircraftTypeClass::As_Reference(a_id))) { + teamclass[i] = &AircraftTypeClass::As_Reference(a_id); + i++; + } + } + + for (u_id = UNIT_FIRST; u_id < UNIT_COUNT; u_id++) { + if (Verify_House(house,&UnitTypeClass::As_Reference(u_id))) { + teamclass[i] = &UnitTypeClass::As_Reference(u_id); + i++; + } + } + + /* + ** Save max # classes. + */ + maxclasses = i; + + /* + ** Fill in the 'count' array with data from the current team: + ** - For every class in the current team, find that class type in the + ** 'teamclass' array & set its count value + */ + for (j = 0; j < maxclasses; j++) { + teamcount[j] = 0; + } + + /* + ** Loop through all classes in the team. + */ + for (i = 0; i < CurTeam->ClassCount; i++) { + + /* + ** Find this class in our array. + */ + for (j = 0; j < maxclasses; j++) { + + /* + ** Set the count; detect a match between the team's class & the + ** 'teamclass' array entry by comparing the actual pointers; typeid + ** won't work because E1 & E2 are the same type class. + */ + if (CurTeam->Class[i] == teamclass[j]) { + teamcount[j] = CurTeam->DesiredNum[i]; + break; + } + } + } + numclasses = CurTeam->ClassCount; + + /* + ** Set up the dialog dimensions based on number of classes we have to draw + ** + ** Compute picture rows & cols. + */ + numcols = (D_DIALOG_W - 16) / D_PICTURE_W; + numrows = (maxclasses + numcols - 1) / numcols; + + // + // Dialog's height = top margin + label + picture rows + + // margin + label + margin + btn + // + dlg_h = (D_MARGIN + D_TXT6_H + D_MARGIN + (numrows * D_ROW_H) + + D_MARGIN + D_TXT6_H + D_MARGIN + D_OK_H + D_MARGIN); + if (dlg_h > 400) { + dlg_h = 400; + } + dlg_y = (400 - dlg_h) / 2; + dlg_picture_top = dlg_y + D_MARGIN + D_TXT6_H + D_MARGIN; + msg_y = dlg_y + D_MARGIN + D_TXT6_H + D_MARGIN + (numrows * D_ROW_H) + D_MARGIN; + + okbtn.Y = dlg_y + dlg_h - D_MARGIN - D_OK_H; + cancelbtn.Y = dlg_y + dlg_h - D_MARGIN - D_CANCEL_H; + + /* + ** Draw to SeenBuff. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Make sure 'house' is valid. + */ + if (house!=HOUSE_GOOD && house!=HOUSE_BAD && house != HOUSE_MULTI1 && + house != HOUSE_MULTI2 && house != HOUSE_MULTI3 && house != HOUSE_MULTI4 ) { + if (ScenPlayer == SCEN_PLAYER_MPLAYER) { + house = HOUSE_MULTI1; + } else { + house = HOUSE_GOOD; + } + } + + /* + ** Create the list. + */ + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + + /* + ** Main Processing Loop. + */ + display = REDRAW_ALL; + process = true; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ** Invoke game callback. + */ + Call_Back(); + + /* + ** Refresh display if needed. + */ + if (display) { + + /* + ** Display the dialog box. + */ + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + + /* + ** Display the constant background of this dialog. + */ + Dialog_Box(D_DIALOG_X, dlg_y, D_DIALOG_W, dlg_h); + Draw_Caption(TXT_NONE, D_DIALOG_X, dlg_y, D_DIALOG_W); + Fancy_Text_Print("Team Members", D_DIALOG_CX, dlg_y + D_MARGIN, CC_GREEN, TBLACK, + TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + // + // Draw the objects. + // + for (i = 0; i < maxclasses; i++) { + // + // Display the object along with any count value for it. + // + Draw_Member(teamclass[i], i, teamcount[i], house, + D_DIALOG_X + 16, dlg_picture_top); + } + + if ((unsigned)curclass < maxclasses) { + Fancy_Text_Print(teamclass[curclass]->Full_Name(), + D_DIALOG_X + D_DIALOG_W / 2, msg_y, CC_TAN, TBLACK, + TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + } + } + + /* + ** Redraw the buttons. + */ + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ** Get user input. + */ + input = commands->Input(); + + /* + ** Process input. + */ + switch (input) { + + /* + ** Mouse buttons set or clear 'held' values + */ + case (KN_LMOUSE): + if (curclass >= 0 && curclass < maxclasses) { + lheld = 1; + tindex = 2; + heldtime = 0; + } + break; + + case (KN_RMOUSE): + if (curclass >= 0 && curclass < maxclasses) { + rheld = 1; + tindex = 2; + heldtime = 0; + } + break; + + case ((int)KN_LMOUSE | (int)KN_RLSE_BIT): + lheld = 0; + break; + + case ((int)KN_RMOUSE | (int)KN_RLSE_BIT): + rheld = 0; + break; + + /* + ** OK: save values & return. + */ + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + + /* + ** Cancel: abort & return. + */ + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + default: + /* + ** Compute new 'curclass' based on mouse position. + */ + i = (Get_Mouse_X() - 16 - D_DIALOG_X) / D_PICTURE_W + + ((Get_Mouse_Y() - dlg_picture_top) / D_ROW_H) * numcols; + + /* + ** If it's changed, update class label. + */ + if (i != curclass) { + + curclass = i; + + /* + ** Clear out the previously printed name of the item. + */ + Hide_Mouse(); + LogicPage->Fill_Rect(D_DIALOG_X + 8, msg_y, D_DIALOG_X + D_DIALOG_W - 9, msg_y + D_TXT6_H, BLACK); + + if ((unsigned)curclass < maxclasses) { + Fancy_Text_Print(teamclass[curclass]->Full_Name(), + D_DIALOG_X + D_DIALOG_W / 2, msg_y, + CC_GREEN, TBLACK, + TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + } + + /* + ** Force buttons to not be held. + */ + lheld = 0; + rheld = 0; + Show_Mouse(); + } + break; + } + + /* + ** Check for a 'held' mouse button; if it's down, and the correct + ** amount of time has gone by, increment/decrement the count for the + ** current class. + */ + if (lheld) { + + /* + ** The first time in, TickCount - heldtime will be larger than + ** tdelay[2], so we increment the count immediately; then, we decrement + ** tindex to go to the next time delay, which is longer; then, decr. + ** again to go to the 1st time delay which is the shortest. + */ + if (TickCount.Time() - heldtime > tdelay[tindex]) { + heldtime = TickCount.Time(); + if (tindex) { + tindex--; + } + + /* + ** Detect addition of a new class. + */ + if (teamcount[curclass]==0) { + + /* + ** Don't allow more classes than we can handle. + */ + if (numclasses == TeamTypeClass::MAX_TEAM_CLASSCOUNT) { + continue; + } + numclasses++; + } + teamcount[curclass]++; + + /* + ** Update number label. + */ + Draw_Member(teamclass[curclass], curclass, teamcount[curclass], + house, D_DIALOG_X + 16, dlg_picture_top); + + } + + } else { + + if (rheld) { + + /* + ** The first time in, TickCount - heldtime will be larger than + ** tdelay[2], so we increment the count immediately; then, we decrement + ** tindex to go to the next time delay, which is longer; then, decr. + ** again to go to the 1st time delay which is the shortest. + */ + if (TickCount.Time() - heldtime > tdelay[tindex]) { + if (tindex) { + tindex--; + } + heldtime = TickCount.Time(); + + if (teamcount[curclass] > 0) { + teamcount[curclass]--; + + /* + ** Detect removal of a class. + */ + if (teamcount[curclass] == 0) { + numclasses--; + } + } + + /* + ** Update number label. + */ + Draw_Member(teamclass[curclass], curclass, teamcount[curclass], + house, D_DIALOG_X + 16, dlg_picture_top); + + } + } + } + } + + /* + ** Copy data into team. + */ + if (!cancel) { + CurTeam->ClassCount = numclasses; + i = 0; // current team class index + for (j = 0; j < maxclasses; j++) { + if (teamcount[j] > 0) { + CurTeam->DesiredNum[i] = teamcount[j]; + CurTeam->Class[i] = teamclass[j]; + i++; + } + } + } + + /* + ** Redraw the display. + */ + HiddenPage.Clear(); + Flag_To_Redraw(true); + Render(); + + delete [] teamclass; + delete [] teamcount; + + if (cancel) return(-1); + return(0); +} + + +/*********************************************************************************************** + * MapEditClass::Draw_Member -- Draws a member of the team dialog box. * + * * + * This routine will display the cameo image of the potential team member. In the corner, * + * it will show the current quantity of this member for the current team being edited. * + * * + * INPUT: ptr -- Pointer to the member object type. * + * * + * index -- The index into the team dialog box array of selectable objects. This is * + * used to determine the correct X and Y offsets to draw. * + * * + * quant -- The quantity number to display in the corner of the image. * + * * + * house -- The owner of this object. * + * pic_x, pic_y -- x,y coords of upper-left corner to start drawing at + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1995 JLB : Created. * + *=============================================================================================*/ +void MapEditClass::Draw_Member(TechnoTypeClass const * ptr, int index, + int quant, HousesType house, int pic_x, int pic_y) +{ + int numcols = (D_DIALOG_W - 32) / D_PICTURE_W; + int col = index % numcols; + int row = index / numcols; + int x = pic_x + col * D_PICTURE_W; + int y = pic_y + row * D_ROW_H; + + WindowList[WINDOW_EDITOR][WINDOWX] = 0; + WindowList[WINDOW_EDITOR][WINDOWY] = 0; + WindowList[WINDOW_EDITOR][WINDOWWIDTH] = 640 / 8; + WindowList[WINDOW_EDITOR][WINDOWHEIGHT] = 400; + Change_Window((int)WINDOW_EDITOR); + + Hide_Mouse(); + Draw_Box(x, y, D_PICTURE_W, D_PICTURE_H, BOXSTYLE_GREEN_DOWN, true); + + ptr->Display(x + D_PICTURE_W / 2, y + D_PICTURE_H / 2, WINDOW_EDITOR, house); + + if (quant > 0) { + Fancy_Text_Print("%d", x + 1, y + D_PICTURE_H - 16, + CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_DROPSHADOW, quant); + } + + Show_Mouse(); + +#if 0 + int numcols = (D_DIALOG_W - 16) / D_PICTURE_W; + int col = index % numcols; + int row = index / numcols; + int dlg_y = 0; + int x = D_DIALOG_X + 8 + col * D_PICTURE_W; + int y = dlg_y + 8 + 13 + row * D_ROW_H; + + /* + ** Change the window to this box. + */ + WindowList[WINDOW_EDITOR][WINDOWX] = x >> 3; + WindowList[WINDOW_EDITOR][WINDOWY] = y; + WindowList[WINDOW_EDITOR][WINDOWWIDTH] = D_PICTURE_W >> 3; + WindowList[WINDOW_EDITOR][WINDOWHEIGHT] = D_PICTURE_H; + Change_Window((int)WINDOW_EDITOR); + + Hide_Mouse(); + Draw_Box(x, y, D_PICTURE_W, D_PICTURE_H, BOXSTYLE_GREEN_DOWN, true); + ptr->Display(WinW<<2, WinH>>1, WINDOW_EDITOR, house); + if (quant > 0) { + Fancy_Text_Print("%d", x+1, y+D_PICTURE_H-8, CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_DROPSHADOW, quant); + } + Show_Mouse(); +#endif +} + + +/*************************************************************************** + * MapEditClass::Build_Mission_list -- fills in mission list box * + * * + * INPUT: * + * missioncount # of missions to add to the list * + * missions array of TeamMissionStruct's * + * missionbuf character arrays to store strings in * + * list list box to add strings to * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Build_Mission_List(int missioncount, TeamMissionStruct *missions, + char missionbuf[TeamTypeClass::MAX_TEAM_MISSIONS][20], ListClass *list) +{ + /* + ** Start with an empty list + */ + while (list->Count()) { + list->Remove_Item(list->Get_Item(0)); + } + + for (int i = 0; i < missioncount; i++) { + /* + ** generate the string for a MOVE mission; the argument is the + ** letter-designation of the cell to move to. + */ + if (missions[i].Mission == TMISSION_MOVE || missions[i].Mission == TMISSION_UNLOAD) { + sprintf(missionbuf[i],"%s\t%c", + TeamTypeClass::Name_From_Mission(missions[i].Mission), + missions[i].Argument + 'A'); + } else { + + /* + ** All other missions take a numeric argument. + */ + sprintf(missionbuf[i],"%s\t%d", + TeamTypeClass::Name_From_Mission(missions[i].Mission), + missions[i].Argument); + } + + /* + ** Add the string to the list box + */ + list->Add_Item(missionbuf[i]); + } +} + +#endif diff --git a/MAPSEL.CPP b/MAPSEL.CPP new file mode 100644 index 0000000..9967606 --- /dev/null +++ b/MAPSEL.CPP @@ -0,0 +1,1276 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\mapsel.cpv 1.8 16 Oct 1995 16:49:48 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPSEL.CPP * + * * + * Programmer : Barry W. Green * + * * + * Start Date : April 17, 1995 * + * * + * Last Update : April 27, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Bit_It_In -- Pixel fade graphic copy. * + * Map_Selection -- Starts the whole process of selecting next map to go to * + * Print_Statistics -- Prints statistics on country selected * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "textblit.h" + +#ifndef DEMO + +void Map_Selection(void); +void Fading_Byte_Blit(int srcx, int srcy, int destx, int desty, int w, int h, GraphicBufferClass *src, GraphicBufferClass *dest); +void Print_Statistics(int country, int xpos, int ypos); +void Cycle_Call_Back_Delay(int time, unsigned char *pal); +int LowMedHiStr(int percentage); +extern int ControlQ; + +unsigned char CountryRemap[256]; +#ifdef OBSOLETE +unsigned char const High16Remap[256]={ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF +}; +#endif + +#define SDE SCEN_DIR_EAST +#define SDW SCEN_DIR_WEST +#define SDN SCEN_DIR_NONE +#define SVA SCEN_VAR_A +#define SVB SCEN_VAR_B +#define SVC SCEN_VAR_C +#define SVN SCEN_VAR_NONE +struct countrylist { + int Choices[2]; // # of map choices this time - 0 = no map selection screen + int Start[2]; + int ContAnim[2]; + int CountryColor[2][3]; + int CountryShape[2][3]; // shape in COUNTRYE.SHP + ScenarioDirType CountryDir[2][3]; + ScenarioVarType CountryVariant[2][3]; +} const CountryArray[27] = { +// GDI SCENARIO CHOICES +/* 0 */ {0}, /*E W*/ /* cont */ /* East colors */ /* West color*/ /* E frame W frame */ +/* 1 */ {{1,1}, { 0, 0}, { 3, 3},{{0x95, 0, 0},{0x95, 0, 0}}, {{17, 0, 0},{17, 0, 0}}, {{SDE,SDN,SDN},{SDN,SDN,SDN}},{{SVA,SVN,SVN},{SVN,SVN,SVN}}}, +/* 2 */ {{1,1}, {16,16}, { 19, 19},{{0x80, 0, 0},{0x80, 0, 0}}, {{ 0, 0, 0},{ 0, 0, 0}}, {{SDE,SDN,SDN},{SDN,SDN,SDN}},{{SVA,SVN,SVN},{SVN,SVN,SVN}}}, +/* 3 */ {{3,3}, {32,32}, { 35, 35},{{0x81,0x82,0x83},{0x81,0x82,0x83}}, {{ 3, 3, 1},{ 3, 3, 1}}, {{SDW,SDW,SDE},{SDN,SDN,SDN}},{{SVA,SVB,SVA},{SVN,SVN,SVN}}}, +/* 4 */ {{2,2}, {48,64}, { 51, 67},{{0x84,0x85, 0},{0x86,0x87, 0}}, {{ 4, 4, 0},{ 2, 2, 0}}, {{SDE,SDE,SDN},{SDW,SDW,SDN}},{{SVA,SVA,SVN},{SVA,SVB,SVN}}}, +/* 5 */ {{2,2}, {99,99}, {102,102},{{0x88,0x89, 0},{0x88,0x89, 0}}, {{ 7, 7, 0},{ 7, 7, 0}}, {{SDE,SDE,SDN},{SDE,SDE,SDN}},{{SVA,SVA,SVN},{SVA,SVA,SVN}}}, +/* 6 */ {{2,2}, {80,83}, { 86, 86},{{0x88,0x89, 0},{0x88,0x89, 0}}, {{ 7, 7, 0},{ 7, 7, 0}}, {{SDE,SDE,SDN},{SDE,SDE,SDN}},{{SVA,SVA,SVN},{SVA,SVA,SVN}}}, +/* 7 */ {{2,2}, {115,0}, {118, 0},{{0x8B,0x8A, 0},{0x8B,0x8A, 0}}, {{ 6, 8, 0},{ 6, 8, 0}}, {{SDE,SDE,SDN},{SDN,SDN,SDN}},{{SVA,SVB,SVN},{SVN,SVN,SVN}}}, +/* 8 */ {{1,1}, {131,0}, {134, 0},{{0x8C, 0, 0},{0x8C, 0, 0}}, {{ 9, 0, 0},{ 9, 0, 0}}, {{SDE,SDN,SDN},{SDN,SDN,SDN}},{{SVA,SVN,SVN},{SVN,SVN,SVN}}}, +/* 9 */ {{2,1}, {147,0}, {150, 0},{{0x8D,0x8E, 0},{ 0, 0, 0}}, {{10,13, 0},{ 0, 0, 0}}, {{SDE,SDE,SDN},{SDN,SDN,SDN}},{{SVA,SVB,SVN},{SVN,SVN,SVN}}}, +/* 10 */ {{1,1}, {163,0}, {166, 0},{{0x8F, 0, 0},{ 0, 0, 0}}, {{16, 0, 0},{ 0, 0, 0}}, {{SDE,SDN,SDN},{SDN,SDN,SDN}},{{SVA,SVN,SVN},{SVN,SVN,SVN}}}, +/* 11 */ {{2,1}, {179,0}, {182, 0},{{0x90,0x91, 0},{ 0, 0, 0}}, {{14,15, 0},{ 0, 0, 0}}, {{SDE,SDE,SDN},{SDN,SDN,SDN}},{{SVA,SVB,SVN},{SVN,SVN,SVN}}}, +/* 12 */ {{2,1}, {195,0}, {198, 0},{{0x92,0x93, 0},{ 0, 0, 0}}, {{12,12, 0},{ 0, 0, 0}}, {{SDE,SDE,SDN},{SDN,SDN,SDN}},{{SVA,SVB,SVN},{SVN,SVN,SVN}}}, +/* 13 */ {{1,1}, {211,0}, {214, 0},{{0x93, 0, 0},{ 0, 0, 0}}, {{12, 0, 0},{ 0, 0, 0}}, {{SDE,SDN,SDN},{SDN,SDN,SDN}},{{SVA,SVN,SVN},{SVN,SVN,SVN}}}, +/* 14 */ {{3,1}, { 0,0}, { 3, 0},{{0x81,0x82,0x83},{ 0, 0, 0}}, {{ 0, 0, 0},{ 0, 0, 0}}, {{SDE,SDE,SDE},{SDN,SDN,SDN}},{{SVA,SVB,SVC},{SVN,SVN,SVN}}}, + +// NOD SCENARIO CHOICES +// choices E/W start continue East colors West colors E shape W shape direction variant +/* 1 */ { {2,1}, { 0,0}, { 3, 0},{{0x80,0x81,0x00},{0,0,0}}, {{ 4, 4, 0},{0,0,0}}, {{SDE,SDE,SDN},{SDN,SDN,SDN}},{{SVA,SVB,SVN},{SVN,SVN,SVN}} }, +/* 2 */ { {2,1}, { 16,0}, { 19, 0},{{0x82,0x83,0x00},{0,0,0}}, {{ 6, 6, 0},{0,0,0}}, {{SDE,SDE,SDN},{SDN,SDN,SDN}},{{SVA,SVB,SVN},{SVN,SVN,SVN}} }, +/* 3 */ { {2,1}, { 32,0}, { 35, 0},{{0x84,0x85,0x00},{0,0,0}}, {{ 5, 5, 0},{0,0,0}}, {{SDE,SDE,SDN},{SDN,SDN,SDN}},{{SVA,SVB,SVN},{SVN,SVN,SVN}} }, +/* 4 */ { {1,1}, { 48,0}, { 51, 0},{{0x86,0x00,0x00},{0,0,0}}, {{ 0, 0, 0},{0,0,0}}, {{SDE,SDN,SDN},{SDN,SDN,SDN}},{{SVA,SVN,SVN},{SVN,SVN,SVN}} }, +/* 5 */ { {3,1}, { 64,0}, { 67, 0},{{0x87,0x88,0x89},{0,0,0}}, {{ 1, 2, 3},{0,0,0}}, {{SDE,SDE,SDE},{SDN,SDN,SDN}},{{SVA,SVB,SVC},{SVN,SVN,SVN}} }, +/* 6 */ { {3,1}, { 80,0}, { 83, 0},{{0x8A,0x8B,0x8C},{0,0,0}}, {{ 9, 7, 8},{0,0,0}}, {{SDE,SDE,SDE},{SDN,SDN,SDN}},{{SVA,SVB,SVC},{SVN,SVN,SVN}} }, +/* 7 */ { {2,1}, { 96,0}, { 99, 0},{{0x8D,0x8E,0x00},{0,0,0}}, {{10,10, 0},{0,0,0}}, {{SDE,SDE,SDN},{SDN,SDN,SDN}},{{SVA,SVB,SVN},{SVN,SVN,SVN}} }, +/* 8 */ { {1,1}, {112,0}, {115, 0},{{0xA0,0x00,0x00},{0,0,0}}, {{ 4, 4, 0},{0,0,0}}, {{SDE,SDN,SDN},{SDN,SDN,SDN}},{{SVA,SVN,SVN},{SVN,SVN,SVN}} }, +/* 9 */ { {2,1}, {128,0}, {131, 0},{{0x8F,0x90,0x00},{0,0,0}}, {{11,15, 0},{0,0,0}}, {{SDE,SDE,SDN},{SDN,SDN,SDN}},{{SVA,SVB,SVN},{SVN,SVN,SVN}} }, +/* 10 */ { {2,1}, {144,0}, {147, 0},{{0x91,0x92,0x00},{0,0,0}}, {{12,16, 0},{0,0,0}}, {{SDE,SDE,SDN},{SDN,SDN,SDN}},{{SVA,SVB,SVN},{SVN,SVN,SVN}} }, +/* 11 */ { {1,1}, {160,0}, {163, 0},{{0x93,0x00,0x00},{0,0,0}}, {{13, 0, 0},{0,0,0}}, {{SDE,SDN,SDN},{SDN,SDN,SDN}},{{SVA,SVN,SVN},{SVN,SVN,SVN}} }, +/* 12 */ { {3,1}, { 0,0}, { 3, 0},{{0x81,0x82,0x83},{0,0,0}}, {{14, 0, 0},{0,0,0}}, {{SDE,SDE,SDE},{SDN,SDN,SDN}},{{SVA,SVB,SVC},{SVN,SVN,SVN}} } +}; + +struct gdistats { + int nameindex; + int pop; + int area; + int capital; + int govt; + int gdp; + int conflict; + int military; +} const GDIStats[]={ +// Name Pop Area Capital Government GDP Conflict Military + { 0,TXT_MAP_P01, TXT_MAP_A00,TXT_MAP_C00, 0, TXT_MAP_GDP00, TXT_MAP_PC00, 0}, + { 1,TXT_MAP_P02, TXT_MAP_A01,TXT_MAP_C01, 1, TXT_MAP_GDP01, TXT_MAP_PC01, 3}, + { 1,TXT_MAP_P02, TXT_MAP_A01,TXT_MAP_C01, 1, TXT_MAP_GDP01, TXT_MAP_PC02, 3}, + { 2,TXT_MAP_P03, TXT_MAP_A02,TXT_MAP_C02, 0, TXT_MAP_GDP00, TXT_MAP_PC03, 1}, + { 3,TXT_MAP_P04, TXT_MAP_A03,TXT_MAP_C03, 3, TXT_MAP_GDP02, TXT_MAP_PC04, 1}, + { 3,TXT_MAP_P04, TXT_MAP_A03,TXT_MAP_C03, 3, TXT_MAP_GDP02, TXT_MAP_PC04, 1}, + { 4,TXT_MAP_P05, TXT_MAP_A04,TXT_MAP_C04, 2, TXT_MAP_GDP03, TXT_MAP_PC05, 5}, + { 4,TXT_MAP_P05, TXT_MAP_A04,TXT_MAP_C04, 2, TXT_MAP_GDP03, TXT_MAP_PC06, 5}, + { 5,TXT_MAP_P06, TXT_MAP_A05,TXT_MAP_C05, 0, TXT_MAP_GDP04, TXT_MAP_PC07, 2}, + { 5,TXT_MAP_P06, TXT_MAP_A05,TXT_MAP_C05, 0, TXT_MAP_GDP04, TXT_MAP_PC07, 2}, + { 6,TXT_MAP_P07, TXT_MAP_A06,TXT_MAP_C06, 0, TXT_MAP_GDP00, TXT_MAP_PC08, 0}, + { 7,TXT_MAP_P08, TXT_MAP_A07,TXT_MAP_C07, 4, TXT_MAP_GDP05, TXT_MAP_PC00, 2}, + { 8,TXT_MAP_P09, TXT_MAP_A08,TXT_MAP_C08, 4, TXT_MAP_GDP06, TXT_MAP_PC10, 2}, + { 9,TXT_MAP_P10, TXT_MAP_A09,TXT_MAP_C09, 0, TXT_MAP_GDP07, TXT_MAP_PC11, 1}, + {10,TXT_MAP_P11, TXT_MAP_A10,TXT_MAP_C10, 0, TXT_MAP_GDP08, TXT_MAP_PC12, 2}, + {11,TXT_MAP_P12, TXT_MAP_A11,TXT_MAP_C11, 5, TXT_MAP_GDP09, TXT_MAP_PC13, 3}, + {12,TXT_MAP_P13, TXT_MAP_A12,TXT_MAP_C12, 6, TXT_MAP_GDP10, TXT_MAP_PC14, 2}, + {13,TXT_MAP_P14, TXT_MAP_A13,TXT_MAP_C13, 0, TXT_MAP_GDP11, TXT_MAP_PC15, 2}, + {14,TXT_MAP_P15, TXT_MAP_A14,TXT_MAP_C14, 0, TXT_MAP_GDP12, TXT_MAP_PC16, 3}, + {14,TXT_MAP_P15, TXT_MAP_A14,TXT_MAP_C14, 0, TXT_MAP_GDP12, TXT_MAP_PC17, 3}, + {15,TXT_MAP_P16, TXT_MAP_A15,TXT_MAP_C15, 7, TXT_MAP_GDP13, TXT_MAP_PC18, 4}, +// Hack in a slot for Estonia + {34,TXT_MAP_P17, TXT_MAP_A16,TXT_MAP_C16, 0, TXT_MAP_GDP00, TXT_MAP_PC19, 0} +}; + +struct nodstats { + int nameindex; + int pop; + int expendable; + int capital; + int govt; + int corruptible; + int worth; + int conflict; + int military; + int probability; +} const NodStats[]={ +// Name Pop Expendable Capital Government Corruptible Worth Conflict Military Probability + {16, TXT_MAP_P18, 38, TXT_MAP_C17, 8, 86, TXT_MAP_GDP14, TXT_MAP_PC20, 0, 23}, + {17, TXT_MAP_P19, 75, TXT_MAP_C18, 0, 18, TXT_MAP_GDP15, TXT_MAP_PC21, 1, 82}, + {17, TXT_MAP_P19, 75, TXT_MAP_C18, 0, 18, TXT_MAP_GDP15, TXT_MAP_PC22, 1, 82}, + {18, TXT_MAP_P20, 50, TXT_MAP_C19, 9, 52, TXT_MAP_GDP16, TXT_MAP_PC23, 0, 72}, + {18, TXT_MAP_P20, 50, TXT_MAP_C19, 9, 52, TXT_MAP_GDP16, TXT_MAP_PC24, 0, 72}, + {19, TXT_MAP_P21, 80, TXT_MAP_C20, 0, 85, TXT_MAP_GDP17, TXT_MAP_PC25, 2, 35}, + {19, TXT_MAP_P21, 80, TXT_MAP_C20, 0, 85, TXT_MAP_GDP17, TXT_MAP_PC26, 2, 35}, + {20, TXT_MAP_P22, 50, TXT_MAP_C21, 10, 48, TXT_MAP_GDP17, TXT_MAP_PC27, 2, 24}, + {21, TXT_MAP_P23, 33, TXT_MAP_C22, 0, 28, TXT_MAP_GDP18, TXT_MAP_PC28, 3, 67}, + {22, TXT_MAP_P24, 75, TXT_MAP_C23, 6, 17, TXT_MAP_GDP19, TXT_MAP_PC29, 2, 80}, + {23, TXT_MAP_P25, 60, TXT_MAP_C24, 7, 93, TXT_MAP_GDP20, TXT_MAP_PC30, 3, 50}, + {24, TXT_MAP_P26, 5, TXT_MAP_C25, 0, 84, TXT_MAP_GDP21, TXT_MAP_PC31, 2, 22}, + {25, TXT_MAP_P27, 55, TXT_MAP_C26, 0, 48, TXT_MAP_GDP22, TXT_MAP_PC32, 3, 62}, + {26, TXT_MAP_P28, 65, TXT_MAP_C27, 0, 41, TXT_MAP_GDP23, TXT_MAP_PC33, 2, 49}, + {27, TXT_MAP_P29, 72, TXT_MAP_C28, 0, 74, TXT_MAP_GDP24, TXT_MAP_PC34, 3, 54}, + {27, TXT_MAP_P29, 72, TXT_MAP_C28, 0, 74, TXT_MAP_GDP24, TXT_MAP_PC35, 3, 54}, + {17, TXT_MAP_P30, 45, TXT_MAP_C29, 6, 3, TXT_MAP_GDP15, TXT_MAP_PC36, 3, 100}, + {28, TXT_MAP_P31, 45, TXT_MAP_C30, 0, 63, TXT_MAP_GDP25, TXT_MAP_PC37, 2, 66}, + {29, TXT_MAP_P32, 55, TXT_MAP_C31, 0, 27, TXT_MAP_GDP26, TXT_MAP_PC38, 2, 68}, + {30, TXT_MAP_P33, 5, TXT_MAP_C32, 0, 65, TXT_MAP_GDP27, TXT_MAP_PC39, 4, 74}, + {31, TXT_MAP_P34, 65, TXT_MAP_C33, 0, 52, TXT_MAP_GDP19, TXT_MAP_PC40, 2, 84}, + {32, TXT_MAP_P35, 2, TXT_MAP_C34, 11, 12, TXT_MAP_GDP28, TXT_MAP_PC41, 2, 92}, + {33, TXT_MAP_P36, 10, TXT_MAP_C35, 0, 8, TXT_MAP_GDP29, TXT_MAP_PC42, 1, 100} +}; + + + +/*********************************************************************************************** + * Map_Selection -- Starts the whole process of selecting next map to go to * + * * + * * + * INPUT: * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/17/1995 BWG : Created. * + *=============================================================================================*/ +void Map_Selection(void) +{ + void *anim, *progress, *oldfont, *greyearth, *greyearth2; + unsigned char localpalette[768]; + int scenario, lastscenario; + int house = PlayerPtr->Class->House; + int attackxcoord = 0; + + static int const _countryx[]={195,217,115,167, + 244, 97,130,142, + 171,170,139,158, + 180,207,177,213, + 201,198, + /* Nod countries */ + 69, 82,105,119, + 184,149,187,130, + 153,124,162,144, + 145,164,166,200, + 201}; + static int const _countryy[]={ 35, 57, 82, 75, + 93,111,108, 91, + 100,111,120,136, + 136,117,158,143, + 167,21, + /* Nod countries */ + 45, 80, 75, 76, + 31, 64, 69, 89, + 88,106,115,139, + 168,164,183,123, + 154}; + //static char const _greenpal[]={0,1,0x42,3,0x43,5,0x44,7,0x44,9,10,1,12,13,0x41,15}; + static char const _greenpal[]={0,0x41,0x42,0x43,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44}; + static char const _othergreenpal[]={0,0x21,0x22,0x23,0x24,0x25,0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26,0x26}; + static char const _regpal[]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + GraphicBufferClass backpage(20*6,8); + + unsigned char *grey2palette = new unsigned char[768]; + unsigned char *progresspalette = new unsigned char[768]; + + + Keyboard::Clear(); + oldfont = Set_Font(ScoreFontPtr); + Set_Font_Palette(_regpal); + Set_Palette(BlackPalette); + + scenario = Scenario + ((house == HOUSE_GOOD) ? 0 : 14); + if (house == HOUSE_GOOD) { + lastscenario = (Scenario == 14); + if (Scenario == 15) return; + } else { + lastscenario = (Scenario == 12); + if (Scenario == 13) return; + } + + // Check if they're even entitled to map selection this time + if (CountryArray[scenario].Choices[ScenDir]==0) return; + + Theme.Queue_Song(THEME_MAP1); + PseudoSeenBuff = new GraphicBufferClass(320,200,(void*)NULL); + + /* + ** Extra graphic buffer to draw text into + */ + TextPrintBuffer = new GraphicBufferClass(SeenBuff.Get_Width(), SeenBuff.Get_Height(), (void*)NULL); + TextPrintBuffer->Clear(); + BlitList.Clear(); + + /* + ** Now start the process where we fade the gray earth in. + */ + greyearth = Open_Animation("GREYERTH.WSA", NULL, 0, (WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE), localpalette); + greyearth2 = Open_Animation("E-BWTOCL.WSA", NULL, 0, (WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE), grey2palette); + + /* + ** Load the spinning-globe anim + */ + if (house == HOUSE_GOOD) { + //anim = Open_Animation("EARTH_E.WSA", NULL,0,(WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE),Palette); + anim = Open_Animation("HEARTH_E.WSA", NULL,0,(WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE),Palette); + //progress = Open_Animation(lastscenario ? "BOSNIA.WSA" : "EUROPE.WSA",NULL,0,(WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE),progresspalette); + progress = Open_Animation(lastscenario ? "HBOSNIA.WSA" : "EUROPE.WSA",NULL,0,(WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE),progresspalette); + } else { + //anim = Open_Animation("EARTH_A.WSA", NULL,0,(WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE),Palette); + anim = Open_Animation("HEARTH_A.WSA", NULL,0,(WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE),Palette); + //progress = Open_Animation(lastscenario ? "S_AFRICA.WSA" : "AFRICA.WSA",NULL,0,(WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE),progresspalette); + progress = Open_Animation(lastscenario ? "HSAFRICA.WSA" : "AFRICA.WSA",NULL,0,(WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE),progresspalette); + } + + void const * appear1 = MixFileClass::Retrieve("APPEAR1.AUD"); + void const * sfx4 = MixFileClass::Retrieve("SFX4.AUD"); + void const * text2 = MixFileClass::Retrieve("TEXT2.AUD"); + void const * target1 = MixFileClass::Retrieve("TARGET1.AUD"); + void const * target2 = MixFileClass::Retrieve("TARGET2.AUD"); +// void const * target3 = MixFileClass::Retrieve("TARGET3.AUD"); + void const * newtarg1 = MixFileClass::Retrieve("NEWTARG1.AUD"); + void const * beepy2 = MixFileClass::Retrieve("BEEPY2.AUD"); + void const * beepy3 = MixFileClass::Retrieve("BEEPY3.AUD"); + void const * beepy6 = MixFileClass::Retrieve("BEEPY6.AUD"); + void const * world2 = MixFileClass::Retrieve("WORLD2.AUD"); + void const * country1 = MixFileClass::Retrieve("COUNTRY1.AUD"); + void const * scold1 = MixFileClass::Retrieve("SCOLD1.AUD"); + + SysMemPage.Clear(); + PseudoSeenBuff->Clear(); + WWMouse->Erase_Mouse(&HidPage, TRUE); + HiddenPage.Clear(); + InterpolationPaletteChanged = TRUE; + InterpolationPalette = Palette; + Increase_Palette_Luminance(InterpolationPalette , 30,30,30,63); + Read_Interpolation_Palette("MAP1.PAL"); +// SeenBuff.Blit(HidPage); + Animate_Frame(greyearth, SysMemPage, 0); + + Bit_It_In_Scale(0, 0, 320, 200, &SysMemPage, PseudoSeenBuff , &SeenBuff); + PseudoSeenBuff->Put_Pixel(237,92,TBLACK); + PseudoSeenBuff->Put_Pixel(237,93,TBLACK); + Interpolate_2X_Scale(PseudoSeenBuff, &SeenBuff,"MAP1.PAL"); + + InterpolationPaletteChanged = TRUE; + InterpolationPalette = localpalette; + Increase_Palette_Luminance(InterpolationPalette , 30,30,30,63); + Read_Interpolation_Palette("MAP_LOCL.PAL"); + Play_Sample(appear1, 255, Options.Normalize_Sound(110)); + Fade_Palette_To(localpalette, FADE_PALETTE_MEDIUM, Call_Back); + for (int i = 1; i < Get_Animation_Frame_Count(greyearth); i++) { + Call_Back_Delay(4); + Animate_Frame(greyearth, *PseudoSeenBuff, i); + } + Close_Animation(greyearth); + Write_Interpolation_Palette("MAP_LOCL.PAL"); + + Call_Back_Delay(4); + SysMemPage.Clear(); + Animate_Frame(greyearth2, SysMemPage, 0); + InterpolationPaletteChanged = TRUE; + InterpolationPalette = grey2palette; + Increase_Palette_Luminance(InterpolationPalette , 30,30,30,63); + Read_Interpolation_Palette("MAP_GRY2.PAL"); + Wait_Vert_Blank(); + Set_Palette(grey2palette); + SysMemPage.Blit(*PseudoSeenBuff); + Call_Back_Delay(4); + for (i = 1; i < Get_Animation_Frame_Count(greyearth2); i++) { + Animate_Frame(greyearth2,*PseudoSeenBuff,i); + Call_Back_Delay(4); + } + Close_Animation(greyearth2); + Write_Interpolation_Palette("MAP_GRY2.PAL"); + + /* + ** Copy the first frame up to the seenpage (while screen is black) + */ + SysMemPage.Clear(); + Animate_Frame(anim, SysMemPage, 1);//, 0,0, (WSAType)0,0,0); + SysMemPage.Blit(*PseudoSeenBuff); + Interpolate_2X_Scale(PseudoSeenBuff, &SeenBuff ,NULL); + + Stop_Speaking(); + + while (CountDownTimer.Time() || Is_Speaking()) { + Call_Back(); +// if (Keyboard::Check()) CountDownTimer.Set(0); + } + +// Keyboard::Clear(); + + /* + ** now make the grid appear + */ + SysMemPage.Blit(*PseudoSeenBuff); + Interpolate_2X_Scale(PseudoSeenBuff, &SeenBuff, NULL); + Play_Sample(sfx4, 255, Options.Normalize_Sound(130)); + Play_Sample(text2, 255, Options.Normalize_Sound(90)); + + + int frame = 1; + + while (frame < Get_Animation_Frame_Count(anim)) { + if (frame == 16 || frame == 33 || frame == 44 || frame == 70 || frame == 73) Play_Sample(text2, 255, Options.Normalize_Sound(90)); + if (frame == 21 || frame == 27) Play_Sample(target1, 255, Options.Normalize_Sound(90)); + if (frame == 45 || frame == 47 || frame == 49) Play_Sample(beepy6, 255, Options.Normalize_Sound(90)); + if (frame == 51) Play_Sample(world2, 255, Options.Normalize_Sound(90)); + if (frame == 70 || frame == 72) Play_Sample(beepy2, 255, Options.Normalize_Sound(90)); + if (frame == 74) Play_Sample(target2, 255, Options.Normalize_Sound(110)); + + switch (frame){ + case 1: + Alloc_Object(new MultiStagePrintClass(Text_String (TXT_READING_IMAGE_DATA), 0,10,_othergreenpal)); + break; + + case 16: + TextPrintBuffer->Fill_Rect (0, 2*10, 2*String_Pixel_Width (Text_String (TXT_READING_IMAGE_DATA)), 2*(10 + 12), BLACK); + break; + + case 17: + TextPrintBuffer->Fill_Rect (0, 2*10, 2*String_Pixel_Width (Text_String (TXT_READING_IMAGE_DATA)), 2*(10 + 12), TBLACK); + Alloc_Object(new MultiStagePrintClass("ANALYZING", 0,10,_othergreenpal)); + break; + + case 33: + TextPrintBuffer->Fill_Rect (0, 2*10, 2*String_Pixel_Width (Text_String (TXT_ANALYZING)), 2*(10 + 12), BLACK); + break; + + case 34: + TextPrintBuffer->Fill_Rect (0, 2*10, 2*String_Pixel_Width (Text_String (TXT_ANALYZING)), 2*(10 + 12), TBLACK); + Alloc_Object(new MultiStagePrintClass(Text_String (TXT_ENHANCING_IMAGE_DATA), 0,10,_othergreenpal)); + break; + + case 44: + TextPrintBuffer->Fill_Rect (0, 2*10, 2*String_Pixel_Width (Text_String (TXT_ENHANCING_IMAGE_DATA)), 2*(10 + 12), BLACK); + break; + + case 45: + TextPrintBuffer->Fill_Rect (0, 2*10, 2*String_Pixel_Width (Text_String (TXT_ENHANCING_IMAGE_DATA)), 2*(10 + 12), TBLACK); + Alloc_Object(new MultiStagePrintClass(Text_String(TXT_ISOLATING_OPERATIONAL_THEATER), 0,10,_othergreenpal)); + break; + + case 70: + TextPrintBuffer->Fill_Rect (0, 2*10, 2*String_Pixel_Width (Text_String(TXT_ISOLATING_OPERATIONAL_THEATER)), 2*(10 + 12), BLACK); + break; + + case 71: + TextPrintBuffer->Fill_Rect (0, 2*10, 2*String_Pixel_Width (Text_String(TXT_ISOLATING_OPERATIONAL_THEATER)), 2*(10 + 12), TBLACK); + Alloc_Object(new MultiStagePrintClass(Text_String (TXT_ESTABLISHING_TRADITIONAL_BOUNDARIES), 0,10,_othergreenpal)); + break; + + case 74: + Alloc_Object(new MultiStagePrintClass(Text_String (TXT_FOR_VISUAL_REFERENCE), 0,22,_othergreenpal)); + break; + } + + + Animate_Frame(anim, *PseudoSeenBuff, frame++); + Call_Back_Delay(/*Keyboard::Check() ? 0 :*/ 3); + } + + TextPrintBuffer->Fill_Rect (0, 2*10, 2*String_Pixel_Width (Text_String (TXT_ESTABLISHING_TRADITIONAL_BOUNDARIES)), 2*(10 + 24), BLACK); + Call_Back_Delay (1); + TextPrintBuffer->Fill_Rect (0, 2*10, 2*String_Pixel_Width (Text_String (TXT_ESTABLISHING_TRADITIONAL_BOUNDARIES)), 2*(10 + 24), TBLACK); + Call_Back_Delay (1); + + Close_Animation(anim); + + Keyboard::Clear(); + BlitList.Clear(); + + /* + ** Freeze on the map of Europe or Africa + */ + + SysMemPage.Clear(); + Animate_Frame(progress,SysMemPage,0); + SysMemPage.Blit(*PseudoSeenBuff); + InterpolationPaletteChanged = TRUE; + InterpolationPalette = progresspalette; + Increase_Palette_Luminance(InterpolationPalette , 30,30,30,63); + Read_Interpolation_Palette("MAP_PROG.PAL"); + + GraphicBufferClass *europe = new GraphicBufferClass(SysMemPage.Get_Width(),SysMemPage.Get_Height(),(GBC_Enum)0); + SysMemPage.Blit(*europe); + + /* + ** Now show territories as they existed last scenario + */ + int startframe = CountryArray[scenario].Start[ScenDir]; + if (startframe) { + Animate_Frame(progress,SysMemPage,startframe); + SysMemPage.Blit(*PseudoSeenBuff); + } + Set_Palette(progresspalette); + Call_Back_Delay(45); + //Write_Interpolation_Palette("MAP_PROG.PAL"); + + /* + ** Now dissolve in first advance of territories + */ + int xcoord = (house == HOUSE_GOOD ? 0 : 204); + SysMemPage.Blit(backpage, xcoord,1, 0,0, 20*6,8); + Play_Sample(text2, 255, Options.Normalize_Sound(90)); + if (house == HOUSE_GOOD) { + Alloc_Object(new ScorePrintClass(TXT_MAP_GDI,0,2,_greenpal)); + } else { + Alloc_Object(new ScorePrintClass(TXT_MAP_NOD,xcoord,2,_greenpal)); + } + Call_Back_Delay(60); + + Play_Sample(country1, 255, Options.Normalize_Sound(90)); + Animate_Frame(progress,SysMemPage,startframe+1); + Bit_It_In_Scale(0, 0, 320, 200, &SysMemPage, PseudoSeenBuff,&SeenBuff,1,1); + backpage.Blit(SysMemPage, 0,0, xcoord,1, 20*6,8); + Call_Back_Delay(85); + + /* + ** Now dissolve in second advance of territories + */ +#ifdef FRENCH + PseudoSeenBuff->Fill_Rect(xcoord,0,xcoord + 6*16 + 10,8,BLACK); + TextPrintBuffer->Fill_Rect(xcoord*2,0,2*(xcoord + 6*16 + 10),2*8,BLACK); +#else + PseudoSeenBuff->Fill_Rect(xcoord,0,xcoord + 6*16,8,BLACK); + TextPrintBuffer->Fill_Rect(2*xcoord,0,2*(xcoord + 6*16),2*8,BLACK); +#endif + Interpolate_2X_Scale(PseudoSeenBuff, &SeenBuff,NULL); + SysMemPage.Blit(backpage, xcoord,1, 0,0, 20*6,8); + if (!lastscenario) { + Play_Sample(text2, 255, Options.Normalize_Sound(90)); + if (house == HOUSE_GOOD) { + Alloc_Object(new ScorePrintClass(TXT_MAP_NOD,0,12,_greenpal)); + } else { + Alloc_Object(new ScorePrintClass(TXT_MAP_GDI,xcoord,12,_greenpal)); + } + Call_Back_Delay(65); + } + + Play_Sample(country1, 255, Options.Normalize_Sound(90)); + Animate_Frame(progress,SysMemPage,startframe+2); + Bit_It_In_Scale(0, 0, 320, 200, &SysMemPage, PseudoSeenBuff , &SeenBuff,1,1); + backpage.Blit(SysMemPage, 0,0, xcoord,11, 20*6,8); + if (!lastscenario) Call_Back_Delay(85); +// Set_Font(oldfont); +#ifdef FRENCH + PseudoSeenBuff->Fill_Rect(xcoord,12,xcoord+6*16+10,20,BLACK); + TextPrintBuffer->Fill_Rect(2*xcoord,2*12,2*(xcoord+6*16+10),2*20,BLACK); +#else + PseudoSeenBuff->Fill_Rect(xcoord,12,xcoord+6*16,20,BLACK); + TextPrintBuffer->Fill_Rect(2*xcoord,2*12,2*(xcoord+6*16),2*20,BLACK); +#endif + Interpolate_2X_Scale(PseudoSeenBuff, &SeenBuff,NULL); + + startframe = CountryArray[scenario].ContAnim[ScenDir]; + + /* + ** Now print the text over the page + */ + Play_Sample(text2, 255, Options.Normalize_Sound(90)); + Alloc_Object(new ScorePrintClass(TXT_MAP_LOCATE,0,160,_greenpal)); + Call_Back_Delay(20); + Alloc_Object(new ScorePrintClass(TXT_MAP_NEXT_MISSION, 0,170,_greenpal)); +#if (GERMAN | FRENCH) + Call_Back_Delay(20); + Alloc_Object(new ScorePrintClass(TXT_MAP_NEXT_MISS2, 0,180,_greenpal)); +#endif + Call_Back_Delay(50); + + /* + ** If we're on the last scenario, erase that text before doing the crosshairs + */ + if (lastscenario) { +#if (GERMAN | FRENCH) + SysMemPage.Fill_Rect(0,160, 20*6,186, TBLACK); + PseudoSeenBuff->Fill_Rect(0,160, 20*6,186, TBLACK); + TextPrintBuffer->Fill_Rect(0,2*160, 2*20*6,2*186, BLACK); + SeenBuff.Fill_Rect(0,2*160, 2*20*6,2*186, TBLACK); + HidPage.Fill_Rect(0,2*160, 2*20*6,2*186, TBLACK); +#else + SysMemPage.Fill_Rect(0,160, 20*6,176, TBLACK); + PseudoSeenBuff->Fill_Rect(0,160, 20*6,176, TBLACK); + TextPrintBuffer->Fill_Rect(0,2*160, 2*20*6,2*176, BLACK); + SeenBuff.Fill_Rect(0,2*160, 2*20*6,2*176, TBLACK); + HidPage.Fill_Rect(0,2*160, 2*20*6,2*176, TBLACK); +#endif + BlitList.Clear(); + Bit_It_In_Scale(0, 0, 320, 200, &SysMemPage, PseudoSeenBuff , &SeenBuff); + } + + /* + ** Fix up the palette that seems different for the last scenario + */ + if (lastscenario){ + InterpolationPaletteChanged = TRUE; + InterpolationPalette = CurrentPalette; + if (house == HOUSE_GOOD){ + Read_Interpolation_Palette("LASTSCNG.PAL"); + Interpolate_2X_Scale(PseudoSeenBuff, &SeenBuff, "LASTSCNG.PAL"); + }else{ + Read_Interpolation_Palette("LASTSCNB.PAL"); + Interpolate_2X_Scale(PseudoSeenBuff, &SeenBuff, "LASTSCNB.PAL"); + } + } + + + int q = 0; + for (frame = 0; frame < ((lastscenario) ? Get_Animation_Frame_Count(progress)-4 : 13); frame++) { + if (!frame) Play_Sample(beepy3, 255, Options.Normalize_Sound(90)); + if (frame == 2) Play_Sample(beepy3, 255, Options.Normalize_Sound(90)); + if (frame == 6) Play_Sample(newtarg1, 255, Options.Normalize_Sound(90)); + + + if (lastscenario){ + switch ( frame ){ + + case 23: + if (house == HOUSE_GOOD){ + Alloc_Object(new MultiStagePrintClass (Text_String (TXT_ENHANCING_IMAGE), 0, 10, _othergreenpal)); + }else{ +#if (FRENCH) + Alloc_Object(new MultiStagePrintClass (Text_String (TXT_ENHANCING_IMAGE), 180, 10, _othergreenpal)); +#else + Alloc_Object(new MultiStagePrintClass (Text_String (TXT_ENHANCING_IMAGE), 210, 10, _othergreenpal)); +#endif //(FRENCH) + } + + + case 35: + if (house == HOUSE_GOOD){ + TextPrintBuffer->Fill_Rect (0, 2*10, 2*String_Pixel_Width (Text_String (TXT_ENHANCING_IMAGE)), 2*(10 + 12), BLACK); + }else{ +#if (FRENCH) + TextPrintBuffer->Fill_Rect (2*180, 2*10, 2*(180+String_Pixel_Width (Text_String (TXT_ENHANCING_IMAGE))), 2*(10 + 12), BLACK); +#else + TextPrintBuffer->Fill_Rect (2*210, 2*10, 2*(210+String_Pixel_Width (Text_String (TXT_ENHANCING_IMAGE))), 2*(10 + 12), BLACK); +#endif //(FRENCH) + } + break; + + case 36: + if (house == HOUSE_GOOD){ + TextPrintBuffer->Fill_Rect (0, 2*10, 2*String_Pixel_Width (Text_String (TXT_ENHANCING_IMAGE)), 2*(10 + 12), TBLACK); + }else{ +#if (FRENCH) + TextPrintBuffer->Fill_Rect (2*180, 2*10, 2*(180+String_Pixel_Width (Text_String (TXT_ENHANCING_IMAGE))), 2*(10 + 12), TBLACK); +#else + TextPrintBuffer->Fill_Rect (2*210, 2*10, 2*(210+String_Pixel_Width (Text_String (TXT_ENHANCING_IMAGE))), 2*(10 + 12), TBLACK); +#endif //(FRENCH) + } + break; + } + } + + Animate_Frame(progress,*PseudoSeenBuff,startframe + frame); + Call_Back_Delay(6); + /* Cause it to cycle on the flashing on the country for a little while */ + if (!lastscenario && frame == 4 && q < 4) { + frame = 2; + q++; + } + } + + int selection = 0, color = 0; + // erase the "Locating Coordinates" message... + Play_Sample(beepy6, 255, Options.Normalize_Sound(90)); + if (!lastscenario) { +#if (GERMAN | FRENCH) + SysMemPage.Fill_Rect( 0,160, 20*6,186, TBLACK); + PseudoSeenBuff->Fill_Rect(0,160, 20*6,186, TBLACK); + TextPrintBuffer->Fill_Rect(0,2*160, 2*20*6,2*186, BLACK); +#else + SysMemPage.Fill_Rect( 0,160, 20*6,176, TBLACK); + PseudoSeenBuff->Fill_Rect(0,160, 20*6,176, TBLACK); + TextPrintBuffer->Fill_Rect(0,2*160, 2*20*6,2*176, BLACK); +#endif + } + Interpolate_2X_Scale(PseudoSeenBuff, &SeenBuff, NULL); + + /* + ** Now the crosshairs are over the target countries - loop until a + ** selection is made? + */ + int done = 0; + int framecounter = 0; + + if (house == HOUSE_GOOD) { + Load_Uncompress(CCFileClass(lastscenario ? "CLICK_EB.CPS" : "CLICK_E.CPS"), SysMemPage, SysMemPage); + } else { + Load_Uncompress(CCFileClass(lastscenario ? "CLICK_SA.CPS" : "CLICK_A.CPS"), SysMemPage, SysMemPage); + if (lastscenario) attackxcoord = 200; + } + +// Set_Font(ScoreFontPtr); + Play_Sample(text2, 255, Options.Normalize_Sound(90)); + Alloc_Object(new ScorePrintClass(TXT_MAP_SELECT,attackxcoord,160,_greenpal)); + Cycle_Call_Back_Delay(16,progresspalette); + Alloc_Object(new ScorePrintClass(TXT_MAP_TO_ATTACK,attackxcoord,170,_greenpal)); + Cycle_Call_Back_Delay(24,progresspalette); + while (Get_Mouse_State() > 0) Show_Mouse(); + + Keyboard::Clear(); + while (!done) { + Cycle_Call_Back_Delay(1,progresspalette); + + // Check for the mouse button + if (Keyboard::Check()) { + if ((Keyboard::Get() & 0x10FF) == KN_LMOUSE) { + for (selection = 0; selection < CountryArray[scenario].Choices[ScenDir]; selection++) { + color = SysMemPage.Get_Pixel(Get_Mouse_X()/2,Get_Mouse_Y()/2); + + /* + ** Special hack for Egypt the second time through + */ + if (CountryArray[scenario].CountryColor[ScenDir][selection] == 0xA0) { + if ((color == 0x80) || (color == 0x81)) color = 0xA0; + } + + if (CountryArray[scenario].CountryColor[ScenDir][selection] == color) { + Play_Sample(world2, 255, Options.Normalize_Sound(90)); + done = 1; + break; + } else { + Play_Sample(scold1, 255, Options.Normalize_Sound(90)); + } + } + } + } + } + ScenVar = CountryArray[scenario].CountryVariant[ScenDir][selection]; + ScenDir = CountryArray[scenario].CountryDir[ScenDir][selection]; + + if (!lastscenario) { + Close_Animation(progress); + + /* + ** Now it's time to highlight the country we're going to. + */ + void const * countryshape = MixFileClass::Retrieve(house == HOUSE_GOOD ? "COUNTRYE.SHP" : "COUNTRYA.SHP"); + + Hide_Mouse(); + // erase "Select country to attack" + PseudoSeenBuff->Fill_Rect(attackxcoord,160, attackxcoord+(17*6),178,BLACK); + TextPrintBuffer->Fill_Rect(2*attackxcoord,2*160, 2*(attackxcoord+(17*6)),2*178,BLACK); +#if (GERMAN | FRENCH) + PseudoSeenBuff->Fill_Rect(attackxcoord+(17*6),160, attackxcoord+(21*6),178,BLACK); + TextPrintBuffer->Fill_Rect(2*attackxcoord+(17*6*2),2*160, 2*(attackxcoord+(21*6)),2*178,BLACK); +#endif //GERMAN + Interpolate_2X_Scale(PseudoSeenBuff, &SeenBuff ,NULL); + + /* + ** Draw the country's shape in non-fading colors + */ + Set_Logic_Page(SysMemPage); + europe->Blit(SysMemPage); + + int shape = CountryArray[scenario].CountryShape[ScenDir][selection]; + int xyindex = shape + (house == HOUSE_GOOD ? 0 : 18); + CC_Draw_Shape(countryshape, shape, + _countryx[xyindex],_countryy[xyindex], + WINDOW_MAIN, SHAPE_WIN_REL | SHAPE_CENTER, 0,0); + SysMemPage.Blit(*PseudoSeenBuff); + Interpolate_2X_Scale(PseudoSeenBuff, &SeenBuff ,NULL); + + /* + ** Now clear the palette of all but the country's colors, and fade + ** the palette down + */ + CCFileClass("DARK_E.PAL").Read(localpalette, 768); +// Load_Data("DARK_E.PAL", localpalette, 768); + InterpolationPaletteChanged = TRUE; + InterpolationPalette = localpalette; + Increase_Palette_Luminance(localpalette , 30,30,30,63); + Read_Interpolation_Palette("MAP_LOC2.PAL"); + Interpolate_2X_Scale(PseudoSeenBuff , &SeenBuff , "MAP_LOC2.PAL"); + Fade_Palette_To(localpalette, FADE_PALETTE_MEDIUM, Call_Back); + + countryshape = 0; + + Print_Statistics(color & 0x7F, _countryx[xyindex], _countryy[xyindex]); + } else { + CCFileClass(house == HOUSE_GOOD ? "DARK_B.PAL" : "DARK_SA.PAL").Read(localpalette, 768); + InterpolationPaletteChanged = TRUE; + InterpolationPalette = localpalette; + Increase_Palette_Luminance(localpalette , 30,30,30,63); + Read_Interpolation_Palette("MAP_LOC3.PAL"); + Interpolate_2X_Scale(PseudoSeenBuff , &SeenBuff , "MAP_LOC3.PAL"); + Set_Palette(localpalette); +// Load_Data(house == HOUSE_GOOD ? "DARK_B.PAL" : "DARK_SA.PAL", localpalette, 768); + + Hide_Mouse(); +#if (GERMAN | FRENCH) + PseudoSeenBuff->Fill_Rect(attackxcoord, 160, 319, 178, BLACK); // erase "Select country to attack" + TextPrintBuffer->Fill_Rect(2*attackxcoord, 2*160, 639, 2*178, BLACK); // erase "Select country to attack" +#else + PseudoSeenBuff->Fill_Rect(attackxcoord, 160, attackxcoord + (17*6), 199, BLACK); // erase "Select country to attack" + TextPrintBuffer->Fill_Rect(2*attackxcoord, 2*160, 2*(attackxcoord + (17*6)), 2*199, BLACK); // erase "Select country to attack" +#endif + Interpolate_2X_Scale(PseudoSeenBuff, &SeenBuff, NULL); + Animate_Frame(progress, *PseudoSeenBuff, Get_Animation_Frame_Count(progress)-1); + Set_Palette(localpalette); + Close_Animation(progress); + PseudoSeenBuff->Blit(SysMemPage); + Print_Statistics(20, 160, house == HOUSE_GOOD ? 0 : 160); + } + + Theme.Queue_Song(THEME_NONE); + Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, NULL); + delete europe; + delete progresspalette; + delete grey2palette; + delete TextPrintBuffer; + TextPrintBuffer = NULL; + BlitList.Clear(); +} + + +/*************************************************************************** + * Print_Statistics -- Prints statistics on country selected * + * * + * * + * * + * INPUT: shape = country #, x & y = country's on-screen coords * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/27/1995 BWG : Created. * + *=========================================================================*/ +void Print_Statistics(int country, int xpos, int ypos) +{ + int index,newx; + void *oldfont; + static int const _gdistatnames[]={ + TXT_MAP_GDISTAT0, + TXT_MAP_GDISTAT1, + TXT_MAP_GDISTAT2, + TXT_MAP_GDISTAT3, + TXT_MAP_GDISTAT4, + TXT_MAP_GDISTAT5, + TXT_MAP_GDISTAT6 + }; + static int const _nodstatnames[]={ + TXT_MAP_GDISTAT0, + TXT_MAP_NODSTAT0, + TXT_MAP_GDISTAT2, + TXT_MAP_GDISTAT3, + TXT_MAP_NODSTAT1, + TXT_MAP_NODSTAT2, + TXT_MAP_GDISTAT5, + TXT_MAP_NODSTAT3, + TXT_MAP_NODSTAT4 + }; + static int const _countryname[]={ + TXT_MAP_COUNTRYNAME0, + TXT_MAP_COUNTRYNAME1, + TXT_MAP_COUNTRYNAME2, + TXT_MAP_COUNTRYNAME3, + TXT_MAP_COUNTRYNAME4, + TXT_MAP_COUNTRYNAME5, + TXT_MAP_COUNTRYNAME6, + TXT_MAP_COUNTRYNAME7, + TXT_MAP_COUNTRYNAME8, + TXT_MAP_COUNTRYNAME9, + TXT_MAP_COUNTRYNAME10, + TXT_MAP_COUNTRYNAME11, + TXT_MAP_COUNTRYNAME12, + TXT_MAP_COUNTRYNAME13, + TXT_MAP_COUNTRYNAME14, + TXT_MAP_COUNTRYNAME15, + TXT_MAP_COUNTRYNAME16, + TXT_MAP_COUNTRYNAME17, + TXT_MAP_COUNTRYNAME18, + TXT_MAP_COUNTRYNAME19, + TXT_MAP_COUNTRYNAME20, + TXT_MAP_COUNTRYNAME21, + TXT_MAP_COUNTRYNAME22, + TXT_MAP_COUNTRYNAME23, + TXT_MAP_COUNTRYNAME24, + TXT_MAP_COUNTRYNAME25, + TXT_MAP_COUNTRYNAME26, + TXT_MAP_COUNTRYNAME27, + TXT_MAP_COUNTRYNAME28, + TXT_MAP_COUNTRYNAME29, + TXT_MAP_COUNTRYNAME30, + TXT_MAP_COUNTRYNAME31, + TXT_MAP_COUNTRYNAME32, + TXT_MAP_COUNTRYNAME33, + TXT_MAP_COUNTRYNAME34 + }; + + static int const _govtnames[]={ + TXT_MAP_GOVT0, + TXT_MAP_GOVT1, + TXT_MAP_GOVT2, + TXT_MAP_GOVT3, + TXT_MAP_GOVT4, + TXT_MAP_GOVT5, + TXT_MAP_GOVT6, + TXT_MAP_GOVT7, + TXT_MAP_GOVT8, + TXT_MAP_GOVT9, + TXT_MAP_GOVT10, + TXT_MAP_GOVT11 + }; + static int const _armynames[]={ + TXT_MAP_ARMY0, + TXT_MAP_ARMY1, + TXT_MAP_ARMY2, + TXT_MAP_ARMY3, + TXT_MAP_ARMY4, + TXT_MAP_ARMY5 + }; + static int const _military[]={ + TXT_MAP_MILITARY0, + TXT_MAP_MILITARY1, + TXT_MAP_MILITARY2, + TXT_MAP_MILITARY3, + TXT_MAP_MILITARY4 + }; + + static char const _greenpal[]={0,0x41,0x42,0x43,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44}; + //static char const _greenpal[]={0,1,0x42,3,0x43,5,0x44,7,0x44,9,10,1,12,13,0x41,15}; + static char _deststr[16]; + +/* Change to the six-point font for Text_Print */ + oldfont = Set_Font(ScoreFontPtr); + +#ifdef GERMAN + xpos = 8; +#else + xpos = (xpos > 128) ? 8 : 136; +#endif + ypos = (ypos > 100) ? 8 : 104-6; + if (PlayerPtr->Class->House == HOUSE_GOOD) { + Alloc_Object(new ScorePrintClass(_countryname[GDIStats[country].nameindex],xpos,ypos,_greenpal)); + Call_Back_Delay(strlen(Text_String(_countryname[GDIStats[country].nameindex]))*3); + ypos += 16; + for (index = 0; index < 7; index ++) { + Alloc_Object(new ScorePrintClass(_gdistatnames[index],xpos,ypos,_greenpal)); + Call_Back_Delay(strlen(Text_String(_gdistatnames[index]+3))); + newx = xpos + 6*strlen(Text_String(_gdistatnames[index])); + switch (index) { + case 0: + Alloc_Object(new ScorePrintClass(GDIStats[country].pop,newx,ypos,_greenpal)); + break; + case 1: + Alloc_Object(new ScorePrintClass(GDIStats[country].area,newx,ypos,_greenpal)); + break; + case 2: + Alloc_Object(new ScorePrintClass(GDIStats[country].capital,newx,ypos,_greenpal)); + break; + case 3: + Alloc_Object(new ScorePrintClass(_govtnames[GDIStats[country].govt],newx,ypos,_greenpal)); + break; + case 4: + Alloc_Object(new ScorePrintClass(GDIStats[country].gdp,newx,ypos,_greenpal)); + break; + case 5: + Alloc_Object(new ScorePrintClass(GDIStats[country].conflict,newx,ypos,_greenpal)); + break; + case 6: + Alloc_Object(new ScorePrintClass(_armynames[GDIStats[country].military],newx,ypos,_greenpal)); + break; + } + ypos += 8; + } + } else { // Nod statistics + if (country > 30) { + country = 15; // hack for 2nd time in Egypt + } else { + if (country >=15) country++; // hack to account for Egypt + } + country++; + + Alloc_Object(new ScorePrintClass(_countryname[NodStats[country].nameindex],xpos,ypos,_greenpal)); + Call_Back_Delay(strlen(Text_String(_countryname[NodStats[country].nameindex]))*3); + ypos += 16; + for (index = 0; index < 9; index ++) { + Alloc_Object(new ScorePrintClass(_nodstatnames[index],xpos,ypos,_greenpal)); + Call_Back_Delay(strlen(Text_String(_nodstatnames[index]+3))); + newx = xpos + 6*strlen(Text_String(_nodstatnames[index])); + switch (index) { + case 0: + Alloc_Object(new ScorePrintClass(NodStats[country].pop,newx,ypos,_greenpal)); + break; + case 1: + sprintf(_deststr,"%d%%",NodStats[country].expendable); + Alloc_Object(new ScorePrintClass(_deststr,newx,ypos,_greenpal)); + break; + case 2: + Alloc_Object(new ScorePrintClass(NodStats[country].capital,newx,ypos,_greenpal)); + break; + case 3: + Alloc_Object(new ScorePrintClass(_govtnames[NodStats[country].govt],newx,ypos,_greenpal)); + break; + case 4: +#ifdef FIX_ME_LATER + sprintf(_deststr,"%s %d%%",LowMedHiStr(NodStats[country].corruptible),NodStats[country].corruptible); +#endif //FIX_ME_LATER + sprintf(_deststr,"%d%%",NodStats[country].corruptible); + Alloc_Object(new ScorePrintClass(_deststr,newx,ypos,_greenpal)); + break; + case 5: + Alloc_Object(new ScorePrintClass(NodStats[country].worth,newx,ypos,_greenpal)); + break; + case 6: + Alloc_Object(new ScorePrintClass(NodStats[country].conflict,newx,ypos,_greenpal)); + break; + case 7: + Alloc_Object(new ScorePrintClass(_military[NodStats[country].military],newx,ypos,_greenpal)); + break; + case 8: + sprintf(_deststr,"%d%%",NodStats[country].probability); + Alloc_Object(new ScorePrintClass(_deststr,newx,ypos,_greenpal)); + break; + } + ypos += 8; + } + } + +#ifdef FRENCH + Alloc_Object(new ScorePrintClass(TXT_MAP_CLICK2,94,193-6,_greenpal)); +#else + Alloc_Object(new ScorePrintClass(TXT_MAP_CLICK2,160-(17*3),193-6,_greenpal)); +#endif + + int done = 0; + while (!done) { + done = 1; + for (int x = 0; x < MAXSCOREOBJS; x++) if (ScoreObjs[x]) { + done = 0; + Call_Back_Delay(1); + } + } + Keyboard::Clear(); + while (Keyboard::Check()){ + Keyboard::Clear(); + } + while (!Keyboard::Check() && !ControlQ) { + Call_Back_Delay(1); + } + Keyboard::Clear(); + Set_Font(oldfont); +} + +#ifdef NEVER +/*************************************************************************** + * FADING_BYTE_BLIT -- 'Pixelized' incremental byte blit. * + * * + * This routine will perform the same function as Byte_Blit, but will * + * do so in an incremental (one piece at a time) method. This is * + * usefull for graphic 'fades' from one display to another. * + * * + * INPUT: srcx - Source page X byte position of upper left corner. * + * * + * srcy - Source page Y pixel position of upper left corner. * + * * + * destx - Dest page X byte position of upper left corner. * + * * + * desty - Dest page Y pixel position of upper left corner. * + * * + * w - Width of the blit in bytes. * + * * + * h - Height of the blit in pixels. * + * * + * src - PageType of the source page. * + * * + * dest - Page type of the destination. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine, although functionally equivalent to the * + * normal Byte_Blit, is very slow. Only use this when * + * the 'fading' graphic effect is desired. This means the * + * destination page should probably be the SEENPAGE. * + * * + * HISTORY: * + * 07/17/1991 JLB : Created from Bit_It_In() (LJC code). * + * 04/17/1995 BWG : Adapted to the C++ system. * + *=========================================================================*/ +void Fading_Byte_Blit(int srcx, int srcy, int destx, int desty, int w, int h, GraphicBufferClass *src, GraphicBufferClass *dest) +{ + unsigned int xindex, // Working array index var. + yindex; // Working y index var. + unsigned int x,y; // Extraction position indexes. + unsigned int tempy; // Temporary working Y index var. + int _xindex[40]; // X position array. + int _yindex[200]; // Y position array. + + // Anticipate two pixel rows per blit. + h >>= 1; + + // This routine is byte-aligned + srcx >>=3; + destx >>=3; + w >>=3; + + for (xindex = 0; xindex < w; xindex++) _xindex[xindex] = xindex; /* init the index array */ + for (yindex = 0; yindex < h; yindex++) _yindex[yindex] = yindex; /* init the index array */ + + /* + ** Shuffle the X indexes around a bit. This gives it + ** that 'random' feel while remaining precise. + */ + for (xindex = 0; xindex < w; xindex++) { + int temp; + + x = IRandom(0,w-1); + temp = _xindex[x]; + _xindex[x] = _xindex[xindex]; + _xindex[xindex] = temp; + } + + /* + ** Shuffle the Y indexes around a bit for the same reason that + ** the x indexes were shuffled. + */ + for (yindex = 0; yindex < h; yindex++) { + int temp; + + y = IRandom(0,h-1); + temp = _yindex[y]; + _yindex[y] = _yindex[yindex]; + _yindex[yindex] = temp; + } + + /* + ** Sweep through the indexes and 'construct' the destination display + ** from a series of miniature Byte_Blits. + */ + for (yindex = 0; yindex < h; yindex++) { + tempy = yindex; + Call_Back(); + for (xindex = 0; xindex < w; xindex++) { + x = _xindex[xindex]; + y = _yindex[tempy]; + tempy++; + if (tempy >= h) tempy = 0; + src->Blit(*dest, (srcx+x)<<3, srcy+(y<<1), (destx+x)<<3, desty+(y<<1), 1<<3, 2); + } + } +} +#endif + + + + + +void Cycle_Call_Back_Delay(int time, unsigned char *pal) +{ + static int _counter; + unsigned char r,g,b; + int i; + + while (time--) { + _counter = (++_counter & 3); + + if (!(_counter & 3) ) { + r = pal[249*3+0]; + g = pal[249*3+1]; + b = pal[249*3+2]; + + for (i = 249; i < 254; i++) { + pal[i*3+0] = pal[(i+1)*3+0]; + pal[i*3+1] = pal[(i+1)*3+1]; + pal[i*3+2] = pal[(i+1)*3+2]; + } + pal[254*3+0] = r; + pal[254*3+1] = g; + pal[254*3+2] = b; + + Set_Palette(pal); + } + Call_Back_Delay(1); + } +} + +int LowMedHiStr(int percentage) +{ + if (percentage < 30) return(TXT_MAP_LMH0); + if (percentage < 70) return(TXT_MAP_LMH1); + return(TXT_MAP_LMH2); +} + + + +#endif //DEMO + + +/*************************************************************************** + * Bit_It_In -- Pixel fade graphic copy. * + * * + * Copies a block of graphic memory using a 'random' pixel algorithm. * + * Typcial use would be to 'fade' some graphic display into another. * + * * + * INPUT: x,y - Pixel position of upper left corner of block. * + * * + * w,y - Pixel width and height of block to pixel blit. * + * * + * src - Page number of the source page. * + * * + * dest - Page number of the destination page. * + * * + * delay - # of frames to wait after each line fades in * + * * + * OUTPUT: none * + * * + * WARNINGS: This function uses PIXEL coordinates for the X and width * + * parameters. This is unlike the Byte_Blit() routine that * + * it most closely resembles. * + * * + * HISTORY: * + * 04/16/1991 JLB : Created. * + * 04/17/1995 BWG : Adapted to C++ library. * + *=========================================================================*/ + +void Bit_It_In_Scale(int x, int y, int w, int h, GraphicBufferClass *src, GraphicBufferClass *dest, GraphicViewPortClass * /*seen*/ , int delay, int dagger) +{ + short int *xindex,*yindex; + int n; + unsigned int i,j,k,m,j1; + short ScaleBuffer[320+200]; + + xindex = (short int *) ScaleBuffer; + yindex = xindex + 320; + + for (i=0; iLock() && dest->Lock()){ + for (i=0; i=h) j1=0; + + Buffer_Put_Pixel (dest,k,m,Buffer_Get_Pixel(src,k,m)); + //n=src->Get_Pixel(k,m); + //dest->Put_Pixel(k,m,n); + } + if (dagger) for (int q=j; q>=0; q--) { + Buffer_Put_Pixel(dest,160-(j-q),q,Buffer_Get_Pixel(src,160-(j-q),q)); + Buffer_Put_Pixel(dest,160+(j-q),q,Buffer_Get_Pixel(src,160+(j-q),q)); + //dest->Put_Pixel(160-(j-q),q,src->Get_Pixel(160-(j-q),q)); + //dest->Put_Pixel(160+(j-q),q,src->Get_Pixel(160+(j-q),q)); + } + } + src->Unlock(); + dest->Unlock(); + //if (seen){ + //Interpolate_2X_Scale(dest , seen ,NULL); + //} + } +} + +void Bit_It_In(int x, int y, int w, int h, GraphicBufferClass *src, GraphicBufferClass *dest, int delay, int dagger) +{ + Bit_It_In_Scale (x, y, w, h, src, dest, NULL , delay, dagger); +} + diff --git a/MEMCHECK.H b/MEMCHECK.H new file mode 100644 index 0000000..788d1ce --- /dev/null +++ b/MEMCHECK.H @@ -0,0 +1,2605 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + + +/* #*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*# + + MemCheck 3.0 Professional for DOS + + Copyright (c) 1990-1994, StratosWare Corporation. + All rights reserved. + + 1-800-WE-DEBUG + + Note to Developers: + -------------------- + This file should be #included AFTER any other #includes in + each source file which is to be memory checked, and BEFORE + any code that performs any operations on allocated pointers. + If it isn't, MemCheck will not pick up source file and line + information for intercepted functions. + + The MCCONFIG.EXE utility distributed with MemCheck 3.0 + will do this safely and quickly for you. + + Most specifically, this header file MUST NOT come before + any prototypes of routines that MemCheck intercepts, like + malloc(), free(), strcpy(), and so on. + + The Final Cut: + --------------- + To ENTIRELY remove MemCheck from your code, just #define + the constant "NOMEMCHECK", or equivalently, "NOMC". + + This header file will then automatically 'evaporate' all + MemCheck 3.0 calls. This is MUCH PREFERABLE to placing + #if-#endif's around the header file's inclusion, as in + + #ifdef DEBUG // DON'T DO THIS! + #include + #endif + + Using the "#ifdef DEBUG" as above doesn't allow the + MemCheck header file to evaporate the MemCheck 3.0 API + calls you may have placed in your code, like mc_startcheck() + and mc_endcheck(). You would then have to surround + each MemCheck API call with additional #if-#endif's. + + Modification History + + WHO WHEN WHAT + KWB 07/28/93 Gussy for beta + KWB 09/11/93 Add new placement overload, NEW() C++ stuff + KWB 11/08/93 Final QA/QC for initial release + KWB 12/02/93 LINT -save added + KWB 02/19/94 Fixed function inlining holes, added macros + for underscore func variants under MSC 7+ + KWB 07/08/94 Added defines for BC extender (_CC_POWERPACK_) + KWB 07/09/94 Added special case for STACKTOP, END under PowerPack + KWB 08/04/94 Added cdecl modifier to stklen, atopsp etc. decls + KWB 08/11/94 Associated _CC32_ with _PROTECTED_ in ccdefs section; + Changed method of determining compiler model, + e.g. _CC_MSC6_ from if == to if >= + Associated DOSX286 with _PROTECTED_ for Phar Lap + Added MC_SET_EXCEPTF, mc_set/get_exceptf() + KWB 08/17/94 Added new[] support filtering (_CPP_ANSI20_) + KWB 08/18/94 Changed _MCFARCALL to _MCFARGLUE + KWB 09/13/94 Added erf_printf as alias for erf_stdout + KWB 09/14/94 Added endf_summary + KWB 09/21/94 Added MCCRITF and mc_set_critf() & related + KWB 10/10/94 Added #if !defined(setmem) etc. for BC DPMI32 + KWB 10/11/94 Added _CC_BORLANDx_ comp def, 'x' = major ver + +*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*# */ + + +/* Avoid multiple inclusions */ +#ifndef _MEMCHECK_H_ +#define _MEMCHECK_H_ + +/* Prevent inclusion in Windows(tm) compilations */ +#if defined(_Windows) || defined(WINDOWS) || defined(_WINDOWS) +# if !defined (__DPMI16__) && !defined (__DPMI32__) && !defined (DOSX286) +# error DOS version of MemCheck header file #included! +# endif +#endif + +/* Shorthand equivalence, V2.0 backwards compatibility... */ +#if defined (NOMC) || defined (NOMEMCHK) +# define NOMEMCHECK +#endif + +/* C++ new interception -- see note later and + read the MemCheck 3.0 User's Manual, section + "Integration With C++." Uncommenting the next line + and following simple instructions allows seamless + transmission of the exact file and line location + of new's in your source code. +*/ +/* #define NEW_OVERLOADED */ + + +/* *** Backwards compatibility with V2.0 *** */ + +#define mc_isactive mc_is_active /* standardize naming... */ +#define mc_getmode mc_get_mode +#define mc_errorstatus mc_error_flags +#define mc_verify_memory mc_check_buffers + +#define MC_USEDISK MC_USING_DISK +#define MC_USEMEM MC_USING_MEMORY + + +/* *** Backwards compatibility with V2.1 *** */ + +#define MCLINENO MCSL /* "MemCheck Source Line" */ +#define MFUNC ERF /* error reporting function */ + +#define mc_set_msgfunc mc_set_erf /* "Message funcs" are now */ +#define mc_get_msgfunc mc_get_erf /* universally referred to as */ +#define mc_error_status mc_error_flags /* "error reporting functions"*/ + +/* Maintain source code compatibility with version 2.1. + Buffer registration is simplified to + just calling "mc_register", regardless of model. + Same with buffer checking, e.g. "mc_check_far" + and "mc_check_near" are rolled into "mc_check". +*/ +#define mc_register_near(p,s) mc_register((void _MCFAR *)(p),s) +#define mc_register_far(p,s) mc_register((void _MCFAR *)(p),s) +#define mc_unregister_near(p) mc_unregister((void _MCFAR *)(p)) +#define mc_unregister_far(p) mc_unregister((void _MCFAR *)(p)) +#define mc_check_near(p) mc_check((void _MCFAR *)(p)) +#define mc_check_far(p) mc_check((void _MCFAR *)(p)) + +/* Error Number Definitions + Returned by mc_endcheck() and mc_error_flags(). + In MemCheck 3.0, there's now a global error number much + like the "errno" variable in standard C. +*/ +#define MCE_NO_ERROR 0 /* it's debugging time & all well */ +#define MCE_NULL_SOURCE 1 /* null source ptr on copy */ +#define MCE_NULL_DEST 2 /* null dest ptr on copy */ +#define MCE_UNDERWRITE 3 /* allocated buf underwritten (front) */ +#define MCE_OVERWRITE 4 /* allocated buf overwritten (end) */ +#define MCE_LEAK 5 /* forgot to free alloc'd memory */ +#define MCE_LEAKAGE MCE_LEAK +#define MCE_UNFREED_MEMORY MCE_LEAK +#define MCE_NULL_PTR_ASSIGN 6 /* assigned data through null ptr */ +#define MCE_BAD_STACK_PTR 7 /* bad stack pointer */ +#define MCE_STACK_OVERWRITE 8 /* copy trashes stack frame */ +#define MCE_INTERNAL 9 /* internal error msg */ +#define MCE_OVERLAPPING_COPY 10 /* source overlaps dest on copy */ +#define MCE_INVALID_PTR 11 /* bad ptr on free, realloc */ +#define MCE_DEST_OVERWRITE 12 /* copy too big for dest buffer */ +#define MCE_OUT_OF_MEMORY 13 /* out of memory */ +#define MCE_OOM MCE_OUT_OF_MEMORY +#define MCE_GPF_PTR 14 /* ptr caused GPF */ + + +/* MemCheck Error Flags + + The MemCheck error flag is just an unsigned long + (specifically, a MCEFLAGS typedef) with "sticky" bits + corresponding to the above MCE_... error numbers. + You can use the error flags macro MC_EFLAG(e) to + test the global MC_Errno double flag word. +*/ +/* Returns a long word with the e-th bit set */ +#define MC_EFLAG(e) ( (e) ? ((MCEFLAGS)1 << (e-1)) : (MCEFLAGS)0) + +#define EFLAG_NULL_PTR MC_EFLAG(MCE_NULL_DEST) +#define EFLAG_BAD_PTR MC_EFLAG(MCE_UNALLOCED_PTR) +#define EFLAG_FRONT_MAGIC MC_EFLAG(MCE_UNDERWRITE) +#define EFLAG_BACK_MAGIC MC_EFLAG(MCE_OVERWRITE) +#define EFLAG_PTRS_NOT_FREED MC_EFLAG(MCE_LEAK) +#define EFLAG_TRACK_FILE 0 /*obsolete in 3.0+*/ +#define EFLAG_NULL_ASSIGN MCE_FLAG(MCE_NULL_PTR_ASSIGN) + +/* *** End Compatibility Section *** */ + + +/* *** MemCheck Compiler Constant Definitions *** */ + +/* + Compiler Defines + -------- ------- + Microsoft _CC_MSC_, _CC_MSC_COMPATIBLE_ + V8.x _CC_MSC8_ + V7.x _CC_MSC7_ + V6.x _CC_MSC6_ + V5.x _CC_MSC5_ + Borland* _CC_BORLAND_, _CC_BCC_ + V3.x _CC_BORLAND3_ + V4.x _CC_BORLAND4_ + PowerPack/16 _CC_POWERPACK_, _CC_POWERPACK16_ + PowerPack/32 _CC_POWERPACK_, _CC_POWERPACK32_, _CC32_ + WATCOM _CC_WATCOM_, _CC_MSC_COMPATIBLE_ + WAT/386 _CC_WATCOM32_, _CC32_ + + * Borland has no good way of determining compiler + version. __BORLANDC__ returns some truly funky + hex constant that "will increase in future versions." + + Define Meaning + ------ -------- + _CC32_ * 32-bit compile + _PROTECTED_ 16- or 32-bit protected mode compile + LCODE Defined if large code model + LDATA Defined if large data model + STACKTOP Highest stack address (top) + STACKEND Lowest stack address + STACKLEN Stack length (top - end) + _CPP_ANSI20_ Compiler supports C++ 2.0, e.g. new[] + + * If _CC32_ is defined, _PROTECTED_ is also defined +*/ + + +#ifndef _CCDEFS_H_ +#define _CCDEFS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* NOTE: Microsoft C 5.x users have to "#define _MSC_VER 500" + at the top of this file. +*/ +#if defined(_MSC_VER) +/* + _MSC_VER Microsoft C version; currently defined as 700. + M_I86 _M_I86 Member of the I86 processor family. + M_I86mM _M_I86mM Memory model type: + = T Tiny + S Small (default) + C Compact model + M Medium model + L Large model + H Huge model + Identifiers defined by /AT, /AS, /AC, /AM, + /AL, and /AH, respectively. + _MSDOS MS-DOS operating system. + _QC Microsoft QuickC Compiler. + _WINDLL Windows protected-mode dynamic-link library + is selected with /GD. + _WINDOWS Windows protected-mode is selected with /GA, + /Gn, /GW, /Mq, or /GD. +*/ +# define _CC_MSC_ +# define _CC_MSC_COMPATIBLE_ + +# if (_MSC_VER >= 800) +# define _CC_MSC8_ +# elif (_MSC_VER >= 700) +# define _CC_MSC7_ +# elif (_MSC_VER >= 600) +# define _CC_MSC6_ +# elif (_MSC_VER >= 500) /* see note above */ +# define _CC_MSC5_ +# else +# error MemCheck.h: unrecognized version of Microsoft compiler! +# endif + +#elif defined(__BORLANDC__) +# define _CC_BORLAND_ /* Borland product */ +# define _CC_BCC_ /* Borland C compiler */ + + /* Major compiler rev */ +# if (__BORLANDC__ >= 0x0450) +# define _CC_BORLAND4_ +# elif (__BORLANDC__ >= 0x400) +# define _CC_BORLAND3_ +# else + /* not needed */ +# endif + + /* Borland 4.0 PowerPack BCC.EXE defines (-WX): + __DPMI16__ _Windows + + With -WXD, -WXDE: + __DLL__ __DPMI16__ _Windows + + Borland 4.0 PowerPack BCC21.EXE defines (-WX): + __CONSOLE__ __DPMI32__ __FLAT__ __WIN32__ _Windows + + With -WXD: + __DLL__ __CONSOLE__ __DPMI32__ __FLAT__ __WIN32__ _Windows + */ +# if defined(__DPMI16__) +# define _CC_POWERPACK_ +# define _CC_POWERPACK16_ +# define _PROTECTED_ +# endif +# if defined(__DPMI32__) +# define _CC_POWERPACK_ +# define _CC_POWERPACK32_ +# define _CC32_ /* flat model */ +# endif + +/* Turbo C++ */ +#elif defined(MCTCP) /* homebrew */ +# define _CC_BORLAND_ /* Borland product */ +# define _CC_TCP_ /* Turbo C++ */ + +#elif defined(__TURBOC__) +/* + __TURBOC__ Gives the current Turbo C version + number, a hexadecimal number. Version + 1.0 id 0x1000; version 1.2 is 0x0102, etc. + __TINY__, __SMALL__, __MEDIUM__, + __COMPACT__, __LARGE__, __HUGE__ + Model defintions + __MSDOS__ Signals that we're not yet in the + twenty-first century +*/ +# define _CC_BORLAND_ /* Borland C product */ +# define _CC_TCC_ /* Turbo C/C++ compiler */ + + +#elif defined(_INTELC32_) +/* + _INTELC32_ has the value 1. + _ARCHITECTURE_ is 386 if the nomod486 pragma is ineffect, + 486 otherwise. +*/ +# define _CC_INTEL_ /* Intel Code Builder */ +# define _CC_MSC_COMPATIBLE_ +# define _CC32_ /* flat model */ + +#elif defined(__WATCOMC__) +/* + __WATCOMC__ Used to determine if the WATCOM C + or C/386 compiler is compiling + __386__ identifies the target machine as an + Intel 80386 under the WATCOM C/386 compiler + MSDOS Signals that we're not yet in the + twenty-first century + __FLAT__, __SMALL__, __MEDIUM__, + __COMPACT__, __LARGE__ + Model defintions (flat is default) + Also id's MSC-compatible model defines + + 8.5 and later: + __WINDOWS__ Identifies 16-bit Windows ("zw" or "zW" opts) + __WINDOWS_386__ 32-bit Microsoft Windows "zW" opt + __NETWARE_386__ Novell Netware 386, defined by the + Novell/Watcom C + __OS2__ IBM OS/2-hosted version of Watcom +*/ +# define _CC_WATCOM_ /* Watcom C product */ +# define _CC_MSC_COMPATIBLE_ +# ifdef __386__ +# define _CC32_ /* flat model */ +# define _CC_WATCOM32_ +# endif + + +#elif defined(__STDC__) /* Standard ANSI C */ +# define _CC_ANSI_ +# define _CC32_ /* no segmentation via far/near */ +# define far +# define near +# define huge +# define cdecl + +/* Avoids parameter mismatches from _far, etc. */ +# define _far +# define _near +# define _huge +# define _cdecl + +#else /* UNSUPPORTED / UNRECOGNIZED COMPILER */ + +#error MemCheck 3.0: unrecognized compiler +/* + If you're using Microsoft C5.1, you must + define the constant _MSC_VER, e.g. + + #define _MSC_VER 500 + + Place this statement at the top of this + header file or in your project header file + BEFORE the MemCheck header file is included. + + Microsoft C 5.0 is NOT supported (it's non-ANSI). +*/ + +#endif /* compiler constant definitions */ + +/* END of _CC..._ name setups; ripple & alias */ + +/* Sheer utility & avoidance of !_CC32_ */ +#ifndef _CC32_ +# define _CC16_ +#endif + +/* 32-bit compilers are always protected mode */ +#ifdef _CC32_ +#ifndef _PROTECTED_ +# define _PROTECTED_ +#endif +#endif /* CC32 */ + +/* Phar Lap support */ +#ifdef DOSX286 +#ifndef _PROTECTED_ +# define _PROTECTED_ +#endif +#endif /* DOSX286 */ + +/* C++ 2.0 compliance: _CPP_ANSI20_ */ +#if defined(_CC_BORLAND_) + /* Only BC++ 4.x and later, but Borland has + seriously mangled version info (__BORLANDC__) */ +# if defined (__BCPLUSPLUS__) +# if (__BCPLUSPLUS__ > 0x0310) /* after 3.1 */ +# define _CPP_ANSI20_ +# endif +# elif defined(_CC_POWERPACK_) +# define _CPP_ANSI20_ +# endif +#elif defined (_CC_MSC_) + /* Current release through 8.x doesn't support new[] */ +#elif defined (_CC_WATCOM_) +# if (__WATCOMC__ >= 1000) /* 10.x */ +# define _CPP_ANSI20_ +# endif +#endif + + +/*******************************************************************/ +/*******************************************************************/ + +/* *** Code and Data Size Constants *** + LCODE not used by this header file. + LDATA *is* used, however. + + May be commented out if these constants are already defined. +*/ + +/* #ifndef LCODE */ +#if defined(M_I86MM) || defined(M_I86LM) || defined(M_I86HM) +# define LCODE 1 +#elif defined(__MEDIUM__) || defined(__LARGE__) || defined(__HUGE__) +# define LCODE 1 +#endif +/* #endif */ + +#if defined(__COMPACT__) || defined(__LARGE__) || defined(__HUGE__) +# define LDATA 1 +#elif defined(M_I86CM) || defined(M_I86LM) || defined(M_I86HM) +# define LDATA 1 +#endif + +/* Macro "aliases" */ + +#ifndef _LCODE +# ifdef LCODE +# define _LCODE LCODE +# endif +#endif + +#ifndef _LDATA +# ifdef LDATA +# define _LDATA LDATA +# endif +#endif + +/*******************************************************************/ +/*******************************************************************/ + +/* *** Physical Stack Address *** */ + +#if defined(_CC_BORLAND_) /* -------------------------- */ + + /* + BORLAND RTL Notes: + ;---------------------------------------------------------------------- + ; In large data models, the stack is in its own segment. The stack + ; starts at SS:__stklen and grows down to SS:0. + ; + ; In small data models, the stack is in the DGROUP, and grows down + ; to meet the heap. The end of the heap is marked by ___brklvl. + (KWB: Note that the brklvl is adjusted upwards until it meets + _stklen...) + ;---------------------------------------------------------------------- + */ + +# define STACKSLOP 256 + extern unsigned cdecl _stklen; + +# if defined(_CC_POWERPACK_) +# define STACKTOP (mc_stacktop()) +# define STACKEND (mc_stackend()) +# else /* not P-Pack */ +# ifdef LDATA + /* Compact, Large, Huge Models ... + + The stack starts at SS:_stklen and + grows downward to SS:0 + */ +# define STACKTOP ((unsigned) _stklen) +# define STACKEND ((unsigned) 0 + STACKSLOP) + +# else + /* Small, Medium Models ... + + The stack starts at SS:0xFFFE and grows + downwards _stklen bytes. + */ +# define STACKTOP ((unsigned) 0xFFFE) +# define STACKEND (STACKTOP - _stklen + STACKSLOP) +# endif +# endif /* not PowerPack */ + +#elif defined (_CC_MSC_) /* ------------------------------- */ + + extern char cdecl end; /* end of stack */ + extern unsigned cdecl _atopsp; /* top of stack */ + +# define STACKTOP _atopsp +# define STACKSLOP 256 + +# ifdef LDATA + /* If in far data, stack could be in its own + seg. tho not usually. see /FARSTACK */ +# define STACKEND ((unsigned) ((unsigned long)&end) + STACKSLOP) +# else + /* If near data, must be in DS; use near ptr */ +# define STACKEND ((unsigned)&end + STACKSLOP) +# endif + +#elif defined (_CC_WATCOM_) /* ------------------------------- */ + + extern unsigned _STACKLOW; /* end of stack */ + extern unsigned _STACKTOP; /* top of stack */ + +# define STACKTOP (_STACKTOP) +# define STACKSLOP 256 + +# ifdef LDATA +# define STACKEND ((unsigned) (_STACKLOW + STACKSLOP)) +# else +# define STACKEND ((unsigned) (_STACKLOW + STACKSLOP)) +# endif + +#else /* Unknown compiler ---------------- */ + +#error Unknown compiler at __FILE__(__LINE__) + +#endif /* defining stack top, end */ + +/*******************************************************************/ +/*******************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* _ccdefs already #included */ + +/* End CCDEFS */ + + +#if !defined (NULL) /* pull in stdio.h if not already */ +# include +#endif + +/* Backup... sometimes NULL defined in other headers */ +#if !defined (_IOFBF) /* pull in stdio.h if not already */ +# include +#endif + + +/* *** MemCheck Constants *** */ + +/* Standard from MemCheck 3.0 onwards. + Access major version and revision + via mc_version() and mc_revision() macros. +*/ +#define _MC_VERSION 0x0300 /* welcome 3.0 the powerful */ + +#define mc_version() ((int)((_MC_VERSION & 0xFF00) >> 8)) +#define mc_revision() ((int)(_MC_VERSION & 0x00FF)) + +#if defined (_CC32_) /* 32-bit Intel target */ +#define PRT_FP "0x%p" +#else +#define PRT_FP "%Fp" +#endif + +/* *** MCID Macro *** */ + +/* Allows later flexibility in assigning mapping... + Also makes MCIDs formulaic +*/ +#define _MCID(f) (MCID)(__paste(MCID_,f)) + +/* + MemCheck Function ID's (MCID's) + + These are the indices used to retrieve information + about specific runtime library calls. +*/ +/* --- MEMCHECK INTERNAL FUNCTIONS --- */ + +#define _MCID_FIRST_INTERNAL 0 /* index of first internal func */ + +#define MCID_mc_startcheck 0 +#define MCID_mc_endcheck 1 +#define MCID_mc_check_buffers 2 +#define MCID_mc_check 3 +#define MCID_mc_register 4 +#define MCID_mc_unregister 5 +#define MCID_mc_set_alignsize 6 +#define MCID_mc_set_checkbytes 7 +#define MCID_mc_nullcheck 8 +#define MCID_mc_breakpoint 9 +#define MCID_mc_debug 10 +#define MCID_mc_set_location 11 +#define MCID_mc_stack_trace 12 +#define MCID_mc_report 13 + +#define _MCID_LAST_INTERNAL 14 /* Set = to last internal ID */ + + +/* *************** STANDARD C ROUTINES ******************* */ + +#define _MCID_FIRST_ANSI (_MCID_LAST_INTERNAL+1) + +#define MCID_calloc (_MCID_FIRST_ANSI + 0) +#define MCID_free (_MCID_FIRST_ANSI + 1) +#define MCID_malloc (_MCID_FIRST_ANSI + 2) +#define MCID_memcpy (_MCID_FIRST_ANSI + 3) +#define MCID_memmove (_MCID_FIRST_ANSI + 4) +#define MCID_memset (_MCID_FIRST_ANSI + 5) +#define MCID_realloc (_MCID_FIRST_ANSI + 6) +#define MCID_sprintf (_MCID_FIRST_ANSI + 7) +#define MCID_strcat (_MCID_FIRST_ANSI + 8) +#define MCID_strcpy (_MCID_FIRST_ANSI + 9) +#define MCID_strncat (_MCID_FIRST_ANSI + 10) +#define MCID_strncpy (_MCID_FIRST_ANSI + 11) +#define MCID_vsprintf (_MCID_FIRST_ANSI + 12) + +#define _MCID_LAST_ANSI MCID_vsprintf /* define to last ANSI */ + + +/* *************** MICROSOFT C ******************* */ + +#if defined(_CC_MSC_COMPATIBLE_) + +#define _MCID_FIRST_MSC (_MCID_LAST_ANSI + 1) + +# define MCID__expand (_MCID_FIRST_MSC + 0) +# define MCID__ffree (_MCID_FIRST_MSC + 1) +# define MCID__fcalloc (_MCID_FIRST_MSC + 2) +# define MCID__fmalloc (_MCID_FIRST_MSC + 3) +# define MCID__fmsize (_MCID_FIRST_MSC + 4) +# define MCID__frealloc (_MCID_FIRST_MSC + 5) +# define MCID__fexpand (_MCID_FIRST_MSC + 6) +# define MCID__msize (_MCID_FIRST_MSC + 7) + +# define MCID__fmemmove (_MCID_FIRST_MSC + 8) +# define MCID__fmemcpy (_MCID_FIRST_MSC + 9) +# define MCID__fmemset (_MCID_FIRST_MSC + 10) +# define MCID__fmemccpy (_MCID_FIRST_MSC + 11) +# define MCID__fstrcat (_MCID_FIRST_MSC + 12) +# define MCID__fstrncat (_MCID_FIRST_MSC + 13) +# define MCID__fstrcpy (_MCID_FIRST_MSC + 14) +# define MCID__fstrncpy (_MCID_FIRST_MSC + 15) +# define MCID__fstrdup (_MCID_FIRST_MSC + 16) +# define MCID__fstrset (_MCID_FIRST_MSC + 17) +# define MCID__fstrnset (_MCID_FIRST_MSC + 18) + +# define MCID__nfree (_MCID_FIRST_MSC + 19) +# define MCID__nmalloc (_MCID_FIRST_MSC + 20) +# define MCID__ncalloc (_MCID_FIRST_MSC + 21) +# define MCID__nrealloc (_MCID_FIRST_MSC + 22) +# define MCID__nexpand (_MCID_FIRST_MSC + 23) +# define MCID__nmsize (_MCID_FIRST_MSC + 24) +# define MCID__nstrdup (_MCID_FIRST_MSC + 25) + +# define MCID__dos_setvect (_MCID_FIRST_MSC + 26) +# define MCID__getdcwd (_MCID_FIRST_MSC + 27) + +/* Here starts the Great ANSI Divide. + MSC6 and earlier have no underscores; + MSC7 and later *have* underscores to emphasize + departure from ANSI... +*/ +#if defined(_CC_MSC_) && !defined (MSC6) /* not MSC6-compatible */ + +# define MCID__getcwd (_MCID_FIRST_MSC + 28) +# define MCID__cgets (_MCID_FIRST_MSC + 29) +# define MCID__halloc (_MCID_FIRST_MSC + 30) +# define MCID__hfree (_MCID_FIRST_MSC + 31) +# define MCID__memccpy (_MCID_FIRST_MSC + 32) +# define MCID__strdup (_MCID_FIRST_MSC + 33) +# define MCID__strnset (_MCID_FIRST_MSC + 34) +# define MCID__strset (_MCID_FIRST_MSC + 35) +# define MCID__swab (_MCID_FIRST_MSC + 36) +# define MCID__tempnam (_MCID_FIRST_MSC + 37) + +#else /*** MSC6 and before; WATCOM ***/ + +/* No leading underscores */ + +# define MCID_getcwd (_MCID_FIRST_MSC + 28) +# define MCID_cgets (_MCID_FIRST_MSC + 29) +# define MCID_halloc (_MCID_FIRST_MSC + 30) +# define MCID_hfree (_MCID_FIRST_MSC + 31) +# define MCID_memccpy (_MCID_FIRST_MSC + 32) +# define MCID_strdup (_MCID_FIRST_MSC + 33) +# define MCID_strnset (_MCID_FIRST_MSC + 34) +# define MCID_strset (_MCID_FIRST_MSC + 35) +# define MCID_swab (_MCID_FIRST_MSC + 36) +# define MCID_tempnam (_MCID_FIRST_MSC + 37) + +#endif /* MSC6 ANSI calls */ + +# define MCID_new (_MCID_FIRST_MSC + 38) +# define MCID_delete (_MCID_FIRST_MSC + 39) +# define MCID__fullpath (_MCID_FIRST_MSC + 40) + +#endif /* Microsoft C-compatible calls */ + + +/* *************** BORLAND C ******************* */ + +#if defined (_CC_BORLAND_) + +#define _MCID_FIRST_BC (_MCID_LAST_ANSI + 1) + +# define MCID__fmemmove (_MCID_FIRST_BC + 0) +# define MCID__fmemcpy (_MCID_FIRST_BC + 1) +# define MCID__fmemset (_MCID_FIRST_BC + 2) +# define MCID__fmemccpy (_MCID_FIRST_BC + 3) +# define MCID__fstrcat (_MCID_FIRST_BC + 4) +# define MCID__fstrncat (_MCID_FIRST_BC + 5) +# define MCID__fstrcpy (_MCID_FIRST_BC + 6) +# define MCID__fstrncpy (_MCID_FIRST_BC + 7) +# define MCID__fstrdup (_MCID_FIRST_BC + 8) +# define MCID__fstrset (_MCID_FIRST_BC + 9) +# define MCID__fstrnset (_MCID_FIRST_BC + 10) + +# define MCID__dos_setvect (_MCID_FIRST_BC + 11) +# define MCID__getdcwd (_MCID_FIRST_BC + 12) + +# define MCID_getcwd (_MCID_FIRST_BC + 13) +# define MCID_cgets (_MCID_FIRST_BC + 14) +# define MCID_memccpy (_MCID_FIRST_BC + 15) +# define MCID_strdup (_MCID_FIRST_BC + 16) +# define MCID_strnset (_MCID_FIRST_BC + 17) +# define MCID_strset (_MCID_FIRST_BC + 18) +# define MCID_swab (_MCID_FIRST_BC + 19) +# define MCID_tempnam (_MCID_FIRST_BC + 20) + +# define MCID_farmalloc (_MCID_FIRST_BC + 21) +# define MCID_farrealloc (_MCID_FIRST_BC + 22) +# define MCID_farfree (_MCID_FIRST_BC + 23) +# define MCID_farcalloc (_MCID_FIRST_BC + 24) +# define MCID_movmem (_MCID_FIRST_BC + 25) +# define MCID_setmem (_MCID_FIRST_BC + 26) +# define MCID_setvect (_MCID_FIRST_BC + 27) +# define MCID_stpcpy (_MCID_FIRST_BC + 28) +# define MCID__fmovmem (_MCID_FIRST_BC + 29) +# define MCID__fsetmem (_MCID_FIRST_BC + 30) +# define MCID_new (_MCID_FIRST_BC + 31) +# define MCID_delete (_MCID_FIRST_BC + 32) +# define MCID__fullpath (_MCID_FIRST_BC + 33) + +#endif + + +/* + 'TOUCH' macro so high warning levels don't generate + 'unreferenced variable' warnings, especially when + making Production libraries... All MemCheck code + compiles without a whymper. +*/ +#if defined (_CC_WATCOM_) +# define TOUCH(var) var = var +#elif defined (_CC_BORLAND4_) +# define TOUCH(var) var = var +#else +# define TOUCH(var) if (var) +#endif + + +/* Default log name used by stock erf_logfile() and variants... */ +#define MEMCHECK_LOG "MEMCHECK.LOG" + +#define MAX_MEMORY 1000 /* 1000K is more than ever possible */ + +/* User-Modifiable Defaults */ + +#define D_CheckByteCt sizeof(int) /* word size is default */ +#define D_AlignSize sizeof(int) /* align returned memory ptrs */ + +/* Number of bytes to copy from null segment (to determine null + pointer assignments) +*/ +#define D_NULLCHECK_BYTES_FAR 16 /* at 0000:0000 (far NULL) */ +#define D_NULLCHECK_BYTES_NEAR 16 /* at DS:0000 (near NULL) */ +#define MAX_NULLCHECK_BYTES_FAR 1024 /* extent of irupt vect tbl */ +#define MAX_NULLCHECK_BYTES_NEAR 66 /* reserved in DS */ + +/* Unroll the double-negative */ +/* + Debugging code specific to MemCheck can be + conditionally compiled by placing it within + #if-#endif sections: (NOTE that this is NOT + required when just using API functions) + + #ifdef MEMCHECK + + void _MCCALLBACK trackf_special (int op, MEMRECP memrecp) + { + (... your custom callback code ...) + } + + #endif + + instead of the more arcane + + #ifndef NOMEMCHECK + : + #endif + + (Both approaches work equally well, however...) +*/ +#ifndef NOMEMCHECK /* MemCheck active */ +#define MEMCHECK +#endif + + +/* *** Calling Conventions *** */ + +#if !defined (_CC_ANSI_) +#define _MCAPI pascal /* MemCheck API functions */ +#define _FASTAPI pascal /* speed-critical functions */ +#define _MCCDECL cdecl /* MemCheck varargs API */ +#define _MCCALLBACK cdecl /* callback functions */ +#define _MCVAR cdecl /* MemCheck global variable */ +#else +#define _MCAPI /* MemCheck API functions */ +#define _FASTAPI /* speed-critical functions */ +#define _MCCDECL /* MemCheck varargs API */ +#define _MCCALLBACK /* callback functions */ +#define _MCVAR /* MemCheck global variable */ +#endif + +#if !defined(_CC_WATCOM_) +# define _RTL _MCCDECL /* RTL calling conv */ +#else +# define _RTL /* RTL calling conv */ + +/* WATCOM C++ does not currently (2/17/94) + accept "cdecl" as a modifier on variables... +*/ +# undef _MCVAR +# define _MCVAR +#endif /* _CC_WATCOM_ */ + +/* 32-bit compiler-independent stuff */ +#if !defined(_CC32_) +#define _MCFAR far +#define _MCFARCALL far +#define _MCNEAR near +#define _MCNEARCALL near +#define _MCHUGE huge +#else +#define _MCFAR +#define _MCFARCALL +#define _MCNEAR +#define _MCNEARCALL +#define _MCHUGE +#endif /* _CC32_ */ + +/* + MSC declares the following routines as "far"... + So does Borland. WATCOM does not; define glue. + + _fstrset _fstrnset _fstrcpy + _fstrncpy _fstrcat _fstrncat + _fmemset _fmemmove _fmemccpy +*/ +#if !defined(_CC_WATCOM_) +# define _MCFARGLUE far +#else +# define _MCFARGLUE +#endif + + +/* Microsoft C7 and later will not have + have a malloc_mc, only _fmalloc_mc; likewise + with free_mc. + The RTLMALLOC and RTLFREE macros are used + to refer to a generically present back-end malloc + and free. +*/ +#if defined (_CC_MSC_) +# if defined (LDATA) +# define RTLMALLOC RTL(_fmalloc) +# define RTLFREE RTL(_ffree) +# else +# define RTLMALLOC RTL(_nmalloc) +# define RTLFREE RTL(_nfree) +# endif +#else /* non-MSC */ +# define RTLMALLOC RTL(malloc) +# define RTLFREE RTL(free) +#endif + + +/* WATCOM defines its atexit funcs as a "register", + which causes a param type mismatch. + _ATEXITFUNC calling convention smooths this out. +*/ +#if defined (_CC_WATCOM_) +# define _ATEXITFUNC +#else +# define _ATEXITFUNC _MCCDECL +#endif + + +/* MemCheck Tracking Mode + + Returned by mc_get_mode(). + Indicates whether information on each allocation + is being stored in memory or on disk. +*/ +#define MC_USING_MEMORY 1 +#define MC_USING_DISK 2 + + +/* Min, max orders for each node in the B-tree */ + +#define BT_ORDER_MIN 5 +#define BT_ORDER_MAX 255 /* maximum tree order */ +#define BT_ORDER_DEFAULT 19 /* default tree order */ + +/* + Returned by mc_get_speed(). + Pass as params to mc_set_speed(). +*/ +#define MC_RUN_NORMAL 1 +#define MC_RUN_FAST 2 + +/* For mc_report(): + "Flags" field of the MEMREC structure + is set to REPORT_START or REPORT_END + to indicate begin and end of report. + + NOTE: If REPORT_START or REPORT_END conflicts + with defines in your project, just comment + them out and use the MC_... variants instead. +*/ +#define REPORT_START (MRFLAGS)0xFE +#define REPORT_END (MRFLAGS)0xFD + +#define MC_REPORT_START (MRFLAGS)0xFE /* alternates in case of conflict */ +#define MC_REPORT_END (MRFLAGS)0xFD + + +/* + Maximum number of breakpoints that + can be set via mc_breakpoint(). +*/ +#define MC_MAX_BREAKS 50 + + +/* "Optype" parameter on Tracking function callback. */ +#define TRACKF_ADD 1 /* record being added to tree */ +#define TRACKF_DEL 2 /* record being deleted from tree */ + +/* Used for the mcflags field of MEMREC to indicate + whether file & line are exact or approximate +*/ +#define MRFLAG_EXACT_LOC ( (MRFLAGS) 0x01) + +/* + Set if the values for a MEMREC are already converted + to "user" values. +*/ +#define MRFLAG_USER_SPECS ( (MRFLAGS) 0x02) +#define MRFLAG_NO_CHECKBYTES ( (MRFLAGS) 0x04) + +/* Alternate name */ +#define mc_message mc_debug + +/* + Parameter to mc_check_transfer() that + specifies that the given data transfer function cannot + have overlapping source & destination. + (MCID's are unsigned bytes.) +*/ +#define MCF_NO_OVERLAP ((unsigned)0x8000) +#define NO_OVERLAP(mcid) ((mcid) | MCF_NO_OVERLAP) + +/* Parameter to mc_check_transfer indicating that + the found memory record is not needed */ +#define NO_MEMREC ((MEMRECP)NULL) +#define NOMEMREC NO_MEMREC + +/* Parameter to mc_check_transfer indicating that + there is no source pointer operand associated + with the data transfer being checked: e.g. memset. */ +#define NO_SOURCE ((void _MCFAR *)0xFFFFFFFA) + + +/* *** TYPEDEFS *** */ + +typedef char * MCSF; /* MemCheck source file */ +typedef unsigned int MCSL; /* MemCheck source line */ +typedef unsigned char MCID; /* MemCheck function ID */ + +typedef unsigned long MCEFLAGS; /* MemCheck error flags */ +typedef void _MCFAR * MCPTR; /* type of ptr stored in tree */ +typedef unsigned char MRFLAGS; /* flags in MEMRECORD */ +typedef unsigned long MCFLAGS; /* MemCheck settings flags */ + +/* MemCheck Rocket allocator prototypes */ +typedef void _MCFAR * (_MCFAR *ROCKETALLOCF) (size_t); +typedef void (_MCFAR *ROCKETFREEF) (void _MCFAR *); + +#pragma pack(1) +/* + Memory Tracking Structure (MEMREC) + + This is the data structure for buffers being + tracked by MemCheck. +*/ +typedef struct MemRecord + { + MCPTR ptr; /* heap/registered ptr */ + MCID mcid; /* MemCheck function ID */ + MRFLAGS flags; /* internal MC flags */ + unsigned long allocno; /* cardinality of allocation */ + unsigned long size; /* size of block */ + MCSF file; /* source file */ + MCSL line; /* source line */ + + } MEMREC, _MCFAR *MEMRECP; + + +/* *** SETTINGS *** */ +/* These are values that describe the life of a MemCheck run. */ + +typedef struct MCSETTINGS { + /* + Bit Flag What + --- --------------- ----------------------------------- + 0 MCF_ACTIVE MemCheck active or off + 1 MCF_FAST_MODE Fast mode or normal + 2 MCF_PROTECTED_MODE Protected mode or real + 3 MCF_FAR_NULL_CHECK Check for far NULL ptr assigns * + 4 MCF_NEAR_NULL_CHECK Check for far NULL ptr assigns * + 5 MCF_STANDARD_STACK Standard stack frame * + 6 MCF_AUTOINIT Start up automatically * + 7 MCF_CLEAR_ON_FREE Clear buffers when freed + 8 MCF_DISK_ROCKET Use DiskRocket options + 9 MCF_IDX_IN_MEMORY Use memory only for Rocket indexes * + (only if DiskRocket linked) + 10 MCF_SOURCE_ONLY Intercept in source code only + + 11 - 31 Reserved + */ + MCFLAGS Flags; /* Main settings flags */ + + unsigned short MaxMem; /* Max mem for tree usage, in K */ + unsigned short NearNullBytes; /* bytes to check in near null */ + unsigned short FarNullBytes; /* " " " " far " */ + unsigned char CheckByteCt; /* check byte count */ + unsigned char AlignSize; /* alignment boundary size */ + char TrackingDir[36]; /* Rocket stores temp files here */ + + } MCSETTINGS, *MCSETTINGSP; + + +/* Random 32-bit .CFG file sentinel */ +#define MC_CFG_FILE_SENTINEL ( (unsigned long) 0x10F23BC4 ) + +typedef struct MCCfgInfo { + + unsigned long sentinel; /* always MC_CFG_FILE_SENTINEL */ + MCSETTINGS MemCheckSettings; /* saved by user */ + + } MCCFGINFO, *MCCFGINFOP; + + +#ifndef _CC32_ + +/* 16-bit exception stack */ +typedef struct { + + unsigned xRetIP; + unsigned xRetCS; + unsigned xErr; + unsigned xIP; + unsigned xCS; + unsigned xFlags; + unsigned xSP; + unsigned xSS; + + } MCEXCEPTINFO; + +#else + +/* 32-bit exception stack */ +typedef struct { + + unsigned long xRetEIP; + unsigned short xRsvd1; + unsigned short xRetCS; + unsigned long xErr; + unsigned long xEIP; + unsigned short xRsvd2; + unsigned short xCS; + unsigned long xFlags; + unsigned long xESP; + unsigned short xRsvd3; + unsigned short xSS; + + } MCEXCEPTINFO; + +#endif /* _CC32_ */ + +/* Values for MCCRITSECT.action */ +#define MCCS_ENTER_SECTION 0 +#define MCCS_LEAVE_SECTION 1 + +#define MCCS_ACTION(pMCCS) (pMCCS->nAction) +#define MCCS_ENTER(pMCCS) ((*(pMCCS->pLocked))++) /* inc flag */ +#define MCCS_LEAVE(pMCCS) ((*(pMCCS->pLocked))--) /* dec flag */ + +/* + Critical section object - ptr to this passed to crit sect callback + WARNING: access these fields ONLY via the MCCS_...() macros. + To do otherwise subjects you to changes in implementation + of the underlying coordination of critical section locking. +*/ +typedef struct { + int nAction; /* MCCS_ENTER/LEAVE_SECTION */ + int * pLocked; /* # times entered */ + unsigned long ulRsvd; /* internal use */ + } MCCRITSECT; + +#pragma pack() + + +#define MC_FEXIT ( (MCID) 0xFF ) + + +/* Error Reporting Function typedef */ +#ifndef _ERF_DEFINED +#define _ERF_DEFINED +typedef void (_MCCALLBACK *ERF) (char *); +#endif + + +/* *** Callback Functions *** */ + +/* Interception callback (on every interception) */ +typedef void (_MCCALLBACK * GLOBALF) (void); + +/* Called whenever nodes added to or deleted from MC database */ +typedef void (_MCCALLBACK *TRACKF) (int, MEMRECP); + +/* User-definable check function to add to transfer checking */ +typedef void (_MCCALLBACK * CHECKF) ( + int , /* 0 or MCE_... error val for this xfer op */ + void _MCFAR *, /* user ptr dest */ + long /* bytes to copy to dest */ + ); + +/* Funcs called at startup or shutdown */ +typedef void (_MCCALLBACK *STARTF) (void); +typedef void (_MCCALLBACK *ENDF) (void); + +/* Report function type passed to mc_report() */ +typedef void (_MCCALLBACK *REPORTF) (MEMRECP); + +/* Additional heap pointer verification (troubleshoot only) */ +typedef int (_MCCALLBACK *VERIFYF) (void _MCFAR *); + +typedef void (*MCVOIDFP) (void); + +/* Exception handler */ +typedef void (_MCCALLBACK _MCFAR *MCEXCEPTF) (void); + +/* Multitasking enter/exit critical section callback. + Specify as param to mc_set_critf() at beginning of program; + callback function will be called with MCCS_ENTER_SECTION + on entry to MemCheck, or MCCS_LEAVE_SECTION on exit. + + NOT TO BE CONFUSED WITH A MEMCHECK "GLOBAL" FUNCTION. + Global functions (GLOBALF's) are used to perform any + actions on the interception of a runtime function; + CRITF's must be used ONLY to serialize access to MemCheck. +*/ +typedef void (_MCCALLBACK * MCCRITF) (MCCRITSECT *); + + +/* Stack Frame Handler + + You can define your own function to + record, analyze, or inspect each stack frame + when mc_stack_trace() is called. + + You *cannot* modify ANY of the values passed + in, as the "const" typing indicates. If you need to + modify a value, make a copy. See the MemCheck 3.0 + documentation for more details on stack frame handlers. +*/ + +typedef void (_MCFAR _MCCDECL *_SSFRAMEHANDLER ) ( + short const , /* AX: near/far/error flag */ + unsigned short const , /* CX: near (default) rtn CS */ + unsigned short const , /* ES: far rtn CS */ + unsigned const , /* DI: rtn offset from stack */ + short const /* DX: frame count */ + ); + +/* Values for "flag" constant parameter to a + stack frame handler. +*/ +#define TRACE_BAD_FRAME 0x00 /* couldn't recognize frame */ +#define TRACE_FAR_CALL 0x01 /* frame represents a far call */ +#define TRACE_NEAR_CALL 0x02 /* " " " near " */ +#define TRACE_BAD_CHAIN 0x03 /* frame BP chewed up */ +#define TRACE_BEGIN 0x80 /* signals begin walk */ +#define TRACE_END 0x81 /* signals end of walk */ + + +/* MC Settings Structure, "flags" member: */ +#define MCF_ACTIVE (MCFLAGS)(0x01) +#define MCF_FAST_MODE (MCFLAGS)(0x02) +#define MCF_PROTECTED_MODE (MCFLAGS)(0x04) +#define MCF_FAR_NULL_CHECK (MCFLAGS)(0x08) +#define MCF_NEAR_NULL_CHECK (MCFLAGS)(0x10) +#define MCF_STANDARD_STACK (MCFLAGS)(0x20) +#define MCF_AUTOINIT (MCFLAGS)(0x40) +#define MCF_CLEAR_ON_FREE (MCFLAGS)(0x80) +#define MCF_DISK_ROCKET (MCFLAGS)(0x100) +#define MCF_IDX_IN_MEMORY (MCFLAGS)(0x200) +#define MCF_SOURCE_ONLY (MCFLAGS)(0x400) + + +/* *** Conditional Compilation *** */ + +/* -------------------------------------------------------------- + If MEMCHECK is not being `compiled out' (via definition + of the constant NOMEMCHECK), include this section... +-------------------------------------------------------------- */ + +#if !defined(MEMCHECK) + +/* Include Section for `MemCheck Not Active' */ + +/* ***************************** + MemCheck Not Active Section + ***************************** + + This section completely removes or + "evaporates" all MemCheck function references + from your projects when you compile with + NOMEMCHECK #defined. + + There's no need to remove any MemCheck + headers or statements from your code + to produce a full production version + of your application. + + o + ooo + ooooooo + ooooooooo + ooooooooooo + ooo + ooo + ooo + ooo + */ + +#ifndef MEMCHECK_MODULE + +/* Evaporate all MemCheck 3.0 API + statements to do nothing, safely... */ + +# define mc_alloc_count() 0L +# define mc_blocks_allocated() 0L +# define mc_blocks_freed() 0L +# define mc_breakpoint(fi) 0 +# define mc_bytes_allocated() 0L +# define mc_bytes_freed() 0L +# define mc_check(p) 0 +# define mc_check_buffers() 0 +# define mc_cur_file() "No file" +# define mc_cur_line() 0 +# define mc_debug(s) +# define mc_debugf(_args) +# define mc_debug_on() +# define mc_debug_off() +# define mc_endcheck() (MCEFLAGS)0 +# define mc_errno() MCE_NO_ERROR +# define mc_error_flags() (MCEFLAGS)0 +# define mc_error_text(e) "MemCheck not active" +# define mc_except_text(e) "MemCheck not active" +# define mc_file() "No file" +# define mc_find_buffer(p,mr) 0 +# define mc_func() (MCID)0 +# define mc_func_name(mcid) ("") +# define mc_get_erf() (ERF)NULL +# define mc_get_mode() 0 +# define mc_get_speed() 0 +# define mc_in_source() 0 +# define mc_is_active() 0 +# define mc_line() 0 +# define mc_load_debugger() +# define mc_location_text() "MemCheck not active" +# define mc_memory_leaked() 0L +# define mc_memrec() (MEMRECP)NULL +# define mc_memrec_text() "MemCheck not active" +# define mc_msg_continued() 0 +# define mc_nullcheck() 0 +# define mc_null_snapshot() +# define mc_register(p,s) +# define mc_report(_f) +# define mc_set_alignsize(_s) +# define mc_set_breakfile(_f) +# define mc_set_checkbytes(_cb) +# define mc_set_checkf(_f) (CHECKF)NULL +# define mc_set_critf(_f) +# define mc_set_endf(erf) (ENDF)NULL +# define mc_set_erf(erf) (ERF)NULL +# define mc_set_globalf(_f) (GLOBALF)NULL +# define mc_set_globalexitf(_f) (GLOBALF)NULL +# define mc_set_speed(_s) +# define mc_set_location() +# define mc_set_trackf(_f) (TRACKF)NULL +# define mc_set_tracefile(_f) +# define mc_set_tree_order(_q) +# define mc_set_track_dir(_dir) +# define mc_source_ptr() (MCPTR)NULL +# define mc_stack_trace(_memo) 0 +# define mc_startcheck(_erf) +# define mc_unregister(p) +# define mc_set_exceptf(f) +# define mc_get_exceptf() ((MCEXCEPTF)NULL) + +/* *** Stock error reporting functions *** */ +# define erf_default(_msg) +# define erf_standard(_msg) +# define erf_logfile(_msg) +# define erf_log_only(_msg) +# define erf_trace(_msg) + +/* Internal Helpers */ +# define _direct_output(_s) +# define _mcsl(_f,_l) +# define _mcsl_delete(_f,_l) +# define _mcsl_new(_f,_l) +# define _mcslx(_f,_l,_s) +# define _mc_set_delflag() +# define _mc_set_location(_f,_l) +# define _mc_set_newflag() + +/* Link-time compileouts */ +# define MC_SET_AUTOINIT(_toggle) +# define MC_SET_CHECK_FREQ(_freq) +# define MC_SET_CHECKF(_f) +# define MC_SET_CRITF(_f) +# define MC_SET_ENDF(_f) +# define MC_SET_ENV_VAR(_envvarname) +# define MC_SET_ERF(_f) +# define MC_SET_GLOBALF(_f) +# define MC_SET_GLOBALEXITF(_f) +# define MC_SET_LOGFILE(_logfilename) +# define MC_SET_PANIC_BUFFERS(_q) +# define MC_SET_SSFH(_f) +# define MC_SET_STARTF(_f) +# define MC_SET_TRACKF(_f) +# define MC_SET_VERIFYF(_f) + +/* Back-end direct */ +#define RTL(_f) _f + +/* *** C++ *** */ +#ifdef __cplusplus + +#define NEW(_object) new _object +#define DELETE(_object) delete _object +#define DELETE_ARR(_arr) delete[] _arr + +#define cpp_malloc(_s) malloc(_s) +#define cpp_calloc(_n,_s) calloc(_n,_s) +#define cpp_free(_p) free(_p) + +/* Borland C++ */ +#define cpp_farmalloc(_s) farmalloc(_s) +#define cpp_farfree(_fp) farfree(_fp) + +/* Microsoft C++-compatibles */ +#define cpp__fmalloc(_s) _fmalloc(_s) +#define cpp__ffree(_fp) _ffree(_fp) + +#endif /* C++ */ + +#endif /* not MEMCHECK_MODULE */ + + +/* #*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*# */ + +#else /* MEMCHECK is defined */ + +/* #*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*# */ + +#pragma message ("MemCheck V3.0") + +/* + ************************* + MemCheck Active Section + ************************* + + The rest of this header file deals with + MemCheck's being compiled into an application. + + */ + +/* Specify that vars and funcs are straight C.. */ +#ifdef __cplusplus +extern "C" { +#endif + + +/* *** ANSI Location Defines *** */ + +#define _MC_NO_FILE ((MCSF) 0) /* just in case... */ +#define _MC_NO_LINE ((MCSL) 0) + + +/* Allow for the possibility of _MCSF_ being + defined to reference a single, static module + filename. This prevents a multiplicity of + static filenames getting added to the DGROUP, e.g. + + static char *_thisfile = __FILE__; + #define _MCSF_ ((MCSF)thisfile) + #include + + This is only needed under MSC pre-VC++. + Borland has "-d" Merge duplicate strings. + VC++ has "/Gf" Elim duplicate strings. +*/ +#if !defined (_MCSF_) +# ifdef __FILE__ +# define _MCSF_ (MCSF)__FILE__ +# else +# define _MCSF_ _MC_NO_FILE +# endif +#endif + +#ifdef __LINE__ +# define _MCSL_ (MCSL)__LINE__ +#else +# define _MCSL_ _MC_NO_LINE +#endif + + +/* *** Standard ANSI C Includes *** + + For va_list function call args + Inclusion of this header won't change + the behavior of host code. +*/ +#if !defined (va_start) /* avoid multiple inclusion... */ +# include +#endif + + +/* *** Compiler-specific includes *** */ + +/*lint -save -e537 Repeated include files (if necessary) */ +#if defined(_CC_MSC_COMPATIBLE_) + +# if !defined (_INC_MALLOC) /* C7.x and later optimization */ +# include +# endif + +# if !defined (_INC_STRING) /* C7.x and later optimization */ +# include +# endif + +#elif defined(_CC_BORLAND_) + +# if !defined (__ALLOC_H) +# include +# endif + +/* String functions must be proto'd before pragmas */ +# if !defined (__STRING_H) +# include +# endif + +#endif /* Compiler-specific includes */ +/*lint -restore */ + +#if defined (_CC_POWERPACK32_) +extern void cdecl mcinitfp_startcheck (void); +extern void cdecl mcexitfp_endcheck (void); +#pragma startup mcinitfp_startcheck 16 +#pragma exit mcexitfp_endcheck 16 +#endif + +/***************************************/ +/* *** MemCheck 3.0 API Prototypes *** */ +/***************************************/ + +/* Internal helper macro - proto shorthand */ +#define _LOCP MCSF,MCSL + +extern unsigned long _MCAPI mc_alloc_count (void); +extern unsigned long _MCAPI mc_blocks_allocated (void); +extern unsigned long _MCAPI mc_blocks_freed (void); +extern unsigned long _MCAPI mc_bytes_allocated (void); +extern unsigned long _MCAPI mc_bytes_freed (void); +extern int _MCAPI mc_check (void _MCFAR *); +extern int _MCAPI mc_check_buffers (void); +extern MCSF _MCAPI mc_cur_file (void); +extern MCSL _MCAPI mc_cur_line (void); +extern void _MCCDECL mc_debugv (const char *, ...); +extern void _MCAPI mc_debug (const char *); +extern MCEFLAGS _MCAPI mc_endcheck (void); +extern MCEFLAGS _MCAPI mc_error_flags (void); +extern char * _MCAPI mc_error_text (int); +extern int _MCAPI mc_errno (void); +extern char * _MCAPI mc_except_text (unsigned); +extern MCSF _MCAPI mc_file (void); +extern int _MCAPI mc_find_buffer(void _MCFAR *realptr,MEMRECP memrecp); +extern MCID _MCAPI mc_func (void); +extern char * _MCAPI mc_func_name(MCID); +extern ERF _MCAPI mc_get_erf (void); +extern MCEXCEPTF _MCAPI mc_get_exceptf (void); +extern int _MCAPI mc_get_mode (void); +extern int _MCAPI mc_get_speed (void); +extern char * _MCAPI mc_get_tracefile (void); +extern int _MCAPI mc_in_source (void); +extern int _MCAPI mc_is_active (void); +extern MCSL _MCAPI mc_line (void); +extern char * _MCAPI mc_location_text (void); +#define mc_load_debugger() _asm int 3 +extern unsigned long _MCAPI mc_memory_leaked (void); +extern char * _MCAPI mc_memrec_text (MEMRECP); +extern MEMRECP _MCAPI mc_memrec (void); +extern int _MCAPI mc_msg_continued (void); +extern int _MCAPI mc_nullcheck (void); +extern void _MCAPI mc_null_snapshot (void); +extern void _MCAPI mc_register (void _MCFAR *, unsigned long); +extern void _MCAPI mc_report (REPORTF); +extern void _MCAPI mc_set_alignsize (unsigned int); +extern void _MCAPI mc_set_breakfile (char *); +extern void _MCAPI mc_set_checkbytes (unsigned int); +extern CHECKF _MCAPI mc_set_checkf (CHECKF); +extern void _MCAPI mc_set_critf (MCCRITF); +extern ENDF _MCAPI mc_set_endf (ENDF); +extern ERF _MCAPI mc_set_erf (ERF); +extern MCEXCEPTF _MCAPI mc_set_exceptf (MCEXCEPTF); +extern GLOBALF _MCAPI mc_set_globalf (GLOBALF); +extern GLOBALF _MCAPI mc_set_globalexitf (GLOBALF); +#define mc_set_location() _mc_set_location(_MCSF_,_MCSL_) +extern MCPTR _MCAPI mc_source_ptr (void); +extern void _MCAPI mc_set_speed (int); +extern void _MCAPI mc_set_tracefile (char *); +extern void _MCAPI mc_set_track_dir (char *); +extern TRACKF _MCAPI mc_set_trackf (TRACKF); +extern void _MCAPI mc_set_tree_order (int); +extern int _MCAPI mc_stack_trace (char *); +extern void _MCAPI mc_startcheck (_LOCP, ERF); +extern void _ATEXITFUNC mc_endcheck_at_exit (void); +extern void _MCAPI mc_unregister (void _MCFAR *); + +/* Debugging versions of the MemCheck library only */ +extern void _MCAPI mc_debug_on (void); +extern void _MCAPI mc_debug_off (void); +extern int _MCCALLBACK mc_search_heap (void _MCFAR *); + +/* *** INTERNAL API HELPERS *** */ +extern void _MCAPI _mc_set_location (_LOCP); +extern void _FASTAPI _mcsl (MCSF,MCSL); /* location run-ahead */ +extern void _FASTAPI _mcslx (MCSF,MCSL,size_t); /* location run-ahead */ +extern void _FASTAPI _mcsl_new (MCSF,MCSL); /* location run-ahead */ +extern void _FASTAPI _mcsl_delete (MCSF,MCSL); /* location run-ahead */ +extern void _FASTAPI _mc_set_newflag (void); /* new's a'comin' */ +extern void _FASTAPI _mc_set_delflag (void); /* delete's a'comin' */ + +/* Misc - uses INT 9 to output directly to screen */ +#if !defined (_CC_WATCOM32_) +extern void _MCCDECL _MCFAR _direct_output (char _MCFAR *); +#else +#define _direct_output(s) printf ("%s\n", s) /* ALERT */ +#endif + +/* + mc_breakpoint() is now a MemCheck Tracking function (TRACKF). + Tracking functions get called every time + MemCheck adds or deletes from its database. +*/ +#define mc_breakpoint(_f) \ + mc_set_trackf( (mc_set_breakfile (_f), trackf_breakpoint) ) +#define mc_breakpoint_trace(_f) \ + mc_set_trackf( (mc_set_tracefile (_f), trackf_breakpoint_trace) ) + + +/* *** Advanced-user API extenders *** */ + +/* extern int _MCAPI mc_find_buffer(void _MCFAR *, MEMRECP); */ +extern int _MCAPI mc_check_transfer( + void _MCFAR *, + void _MCFAR *, + unsigned long, + unsigned, + unsigned, + MEMRECP); + +/* mc_get_settings + + Write your own "get settings" routine + to override the one shipped with MemCheck. + You can hard-wire any settings you like, e.g. + always ON for versions of your app shipped to + testers/QA stations, etc. +*/ +extern void _MCCALLBACK mc_get_settings (MCSETTINGS *); + + +/* *** Callbacks / Functionality Extenders *** + + Function Type Called... + -------------- ------------------------------ + Error reporting To handle each MemCheck error message + Global Interception On each MemCheck interception + Checking On every data transfer check + Tracking On every allocation/deallocation + Start On mc_startcheck or AutoInit + End At mc_endcheck or MemCheck shutdown + + Refer to your MemCheck 3.0 manual for further details. + + *** STOCK FUNCTIONS *** + These functions are available in the MemCheck + libraries as "ready-made" for your programming + pleasure in the categories above. +*/ + +/* *** Stock error reporting functions *** */ + +extern void _MCCALLBACK erf_default (char *); +extern void _MCCALLBACK erf_standard (char *); +extern void _MCCALLBACK erf_logfile (char *); +extern void _MCCALLBACK erf_log_only (char *); +extern void _MCCALLBACK erf_trace (char *); +extern void _MCCALLBACK erf_trace_all (char *); +extern void _MCCALLBACK erf_trace_obj (char *); +extern void _MCCALLBACK erf_stdout (char *); +extern void _MCCALLBACK erf_stderr (char *); +extern void _MCCALLBACK erf_find_leaks (char *); + +#define erf_printf erf_stdout /* alias*/ + +/* *** Stock Tracking Functions *** */ + +extern void _MCCALLBACK trackf_default (int, MEMRECP); +extern void _MCCALLBACK trackf_all (int, MEMRECP); +extern void _MCCALLBACK trackf_all_2 (int, MEMRECP); +extern void _MCCALLBACK trackf_breakpoint (int, MEMRECP); +extern void _MCCALLBACK trackf_breakpoint_trace (int, MEMRECP); +extern void _MCCALLBACK trackf_big_alloc (int, MEMRECP); + +/* *** Stock End Functions *** */ + +extern void _MCCALLBACK endf_default (void); /* does nothing */ +extern void _MCCALLBACK endf_info (void); /* write run info to log */ +extern void _MCCALLBACK endf_alert (void); /* warn if run errs */ +extern void _MCCALLBACK endf_summary (void); /* warn if run errs */ + +/* *** Stock Start functions *** */ + +extern void _MCCALLBACK startf_default (void); /* does nothing */ +extern void _MCCALLBACK startf_info (void); /* write options to log */ + +/* *** Stock Check Functions *** */ + +extern void _MCCALLBACK checkf_default (int,void _MCFAR *,long); +extern void _MCCALLBACK checkf_dataseg ( + int, /* 0 or MCE_... error val for this xfer op */ + void _MCFAR *, /* user ptr dest */ + long /* bytes to copy to dest */ + ); +extern void _MCCALLBACK checkf_verify_heap (int,void _MCFAR *,long); + +/* *** Stock Global Interception Functions *** */ + +extern void _MCCALLBACK globalf_default (void); /* does nothing */ +extern void _MCCALLBACK globalf_supercheck (void); +extern void _MCCALLBACK globalf_check_buffers (void); +extern void _MCCALLBACK globalf_heapcheck (void); + +/* *** Stock Report Functions *** */ +extern void _MCCALLBACK reportf_default (MEMRECP); + +/* *** Stock Exception Handlers *** */ +extern void _MCCALLBACK _MCFAR exceptf_default (void); + +/* *** Stock Stack Frame Handlers *** */ +extern void _MCFAR _MCCDECL ssfh_info ( + short const, unsigned short const, unsigned short const, unsigned const, short const); + +extern void _MCFAR _MCCDECL ssfh_fast ( + short const, unsigned short const, unsigned short const, unsigned const, short const); + /* int const _flag, */ + +extern void _MCFAR _MCCDECL ssfh_standard ( + short const, unsigned short const, unsigned short const, unsigned const, short const); + +extern void _MCFAR _MCCDECL ssfh_debug ( + short const, unsigned short const, unsigned short const, unsigned const, short const); + +/* */ +extern unsigned int _MCAPI mc_stacktop (void); /* high address */ +extern unsigned int _MCAPI mc_stackend (void); /* low address */ + + +/* Function external variables. + + These are used effectively with MemCheck 3.0's AutoInit + setting. Under AutoInit, MemCheck fires itself up automatically + on its first interception. Under these circumstances, + there's no chance to have changed any defaults (like the + ERF or error reporting function). These variables provide + a link-level method of setting these functions: + + #include + : + // Sets custom erf at link-time + MC_SET_ERF (erf_custom_startup); + : +*/ +/* *** GLOBALS *** */ + +extern ERF _MCVAR MC_ERF; /* error reporting func ptr */ +extern CHECKF _MCVAR MC_CheckF; /* transfer check func */ +extern MCCRITF _MCVAR MC_CritF; /* crit section enter/exit */ +extern GLOBALF _MCVAR MC_GlobalF; /* global interception callback */ +extern GLOBALF _MCVAR MC_GlobalExitF; /* called on exit interception */ +extern TRACKF _MCVAR MC_TrackF; /* alloc/dealloc callback */ +extern STARTF _MCVAR MC_StartF; /* startup callback */ +extern ENDF _MCVAR MC_EndF; /* shutdown callback */ + +extern VERIFYF _MCVAR MC_VerifyF; /* troubleshooting */ + +extern char * _MCVAR MC_LogFile; /* log file name used */ +extern char _MCVAR MC_UserAutoInit; +extern int _MCVAR MC_CheckFreq; /* for globalf_supercheck() et al */ +extern char * _MCVAR MC_EnvVar; /* Env var to detect 'active' */ +extern unsigned short _MCVAR MC_DataSeg; /* DS value */ + +extern int _MCVAR MC_MaxTraceDepth; +extern char * _MCVAR MCST_Desc; /* trace descrip to mc_..trc() */ + +extern MCSETTINGS _MCVAR MC_DefaultSettings; /* default settings */ +extern MCSETTINGS _MCVAR MC_Settings; /* real settings-- + USE WITH CARE!!! */ + +extern MCVOIDFP _MCVAR MC_PMMap1; /* p-mode func in map seg 1 */ + +/* Protected mode exception handling */ +extern unsigned char _MCVAR MC_ExceptList[]; /* exceptions to handle */ +extern MCEXCEPTINFO _MCVAR MC_ExceptInfo; /* in exception */ +extern MCEXCEPTF _MCVAR MC_ExceptF; /* installed hdler */ + +/* Rocket Guidance Systems */ +extern ROCKETALLOCF _MCVAR MC_RocketAllocF; +extern ROCKETFREEF _MCVAR MC_RocketFreeF; +extern unsigned char _MCVAR MC_PanicBufCount; /* anti-tree failure */ + +/* This char is used to fill freed buffers + if the "ClearOnFree" option in effect. + Default buffer clear char is 0. +*/ +extern unsigned char _MCVAR MC_FreedBufferFillChar; + +/* Link-time defaults. + + These macros are "covers" to insulate you, the developer, + from the underlying implementation, as well as to provide + such bennies as compiling clean out of your code when + NOMEMCHECK or NOMC is defined. + + Use instead of accessing vars directly! + + To use, place the following in ONE MODULE e.g. your main module + (any *one* module will work fine) after the MemCheck + header file has been included: + + #include + MC_SET_...(params); + + For example, to change the default log file that MemCheck + uses at runtime to C:\MYDEV\MYPROG.LOG: + + #include + MC_SET_LOGFILE ("C:\\MYDEV\\MPROG.LOG"); + + Most of these macros have runtime function equivalents, + such as mc_set_erf() for MC_SET_ERF(), etc. Notable + exceptions to this are the following values that + must generally have link-time initializations: + + MC_SET_LOGFILE() + MC_SET_AUTOINIT() + MC_SET_STARTF() +*/ +#define MC_SET_AUTOINIT(_toggle) \ + char _MCVAR MC_UserAutoInit = (_toggle); +#define MC_SET_CHECKF(_f) \ + CHECKF _MCVAR MC_CheckF = (_f) +#define MC_SET_CHECK_FREQ(_freq) \ + int _MCVAR MC_CheckFreq = (_freq) +#define MC_SET_CRITF(_f) \ + MCCRITF _MCVAR MC_CritF = (_f) +#define MC_SET_ENDF(_f) \ + ENDF _MCVAR MC_EndF = (_f) +#define MC_SET_ENV_VAR(_envvarname) \ + char * _MCVAR MC_EnvVar = (_envvarname) +#define MC_SET_ERF(_f) \ + ERF _MCVAR MC_ERF = (_f) +#define MC_SET_EXCEPTF(_f) \ + MCEXCEPTF _MCVAR MC_ExceptF = (_f) +#define MC_SET_GLOBALF(_f) \ + GLOBALF _MCVAR MC_GlobalF = (_f) +#define MC_SET_GLOBALEXITF(_f) \ + GLOBALF _MCVAR MC_GlobalExitF = (_f) +#define MC_SET_LOGFILE(_f) \ + char * _MCVAR MC_LogFile = (_f) +#define MC_SET_PANIC_BUFFERS(_q) \ + unsigned char _MCVAR MC_PanicBufCount = (_q) +#define MC_SET_SSFH(_f) \ + _SSFRAMEHANDLER _MCVAR near MC_SFrameHandler = (_f) +#define MC_SET_STARTF(_f) \ + STARTF _MCVAR MC_StartF = (_f) +#define MC_SET_TRACKF(_f) \ + TRACKF _MCVAR MC_TrackF = (_f) +#define MC_SET_VERIFYF(_f) \ + VERIFYF _MCVAR MC_VerifyF = (_f) + +/* Use the MC_BEGIN_EXCEPTLIST, MC_HANDLE_EXCEPTION, + and MC_END_EXCEPTLIST macros to change the exceptions + MemCheck handles in protected mode by default. + + Usage (exactly as typed): + #include + : + MC_BEGIN_EXCEPTLIST + MC_HANDLE_EXCEPTION (0x0) + MC_HANDLE_EXCEPTION (0xD) + MC_END_EXCEPTLIST + + NOTE: + To turn off MemCheck's exception handling completely, use + + MC_SET_EXCEPTF(NULL); + + instead of trying to define an empty EXCEPTLIST... +*/ +#define MC_BEGIN_EXCEPTLIST \ + unsigned char _MCVAR MC_ExceptList[] = { +#define MC_HANDLE_EXCEPTION(e) \ + (unsigned char)(e), +#define MC_END_EXCEPTLIST \ + (unsigned char)0xFF }; /* 0xFF MUST end list */ + +/* ------------- End MemCheck 3.0 Library Calls --------------- */ + +/* Formulaic rogue varargs interceptions; + most host-code-compatible method... + "Are you experienced?" + + "It is better to be mugged than + to live in fear." - Anon. +*/ +#define _VA_DEF(f,r,p) \ + typedef r (_RTL *p_##f) p; \ + extern p_##f _MCAPI _loc_##f (_LOCP); + +/* Declare sprintf helper function */ +_VA_DEF(sprintf,int,(char *, const char *, ...)) + + /* * * * * * * * * * * * * * * * * * * * * * * + ************************* + Back-End RTL + ************************* +*/ + +/* *** Back-end functions *** */ + +/* Macro to access true back-end RTL. + Used internally by the MemCheck API functions. +*/ +#define __paste(x,y) x ## y +#define RTL(func) __paste(func,_mc) + +/* Macro to encapsulate the declaration of + the renamed (zapped) back-end rtl +*/ +#define _RTLDECL(f,rctype,params) \ + extern rctype _RTL RTL(f) params + + +/* For the conversion that MSC underwent + from C 6 to 7, where non-ANSI calls + have underbars +*/ +#if defined (_CC_MSC_) && !defined (MSC6) +#if (_MSC_VER >= 700) +# define _C7A +#endif +#endif + +#ifdef _C7A +#define C7ANSI(func) _##func +#else +#define C7ANSI(func) func +#endif + +#undef _C7A + + +/* ---------------------------------------------- */ +/* These are the renamed ("zapped") RTL functions */ +/* ---------------------------------------------- */ + +/* *** ANSI *** */ + +_RTLDECL(malloc, void *, (size_t)); +_RTLDECL(calloc, void *, (size_t, size_t)); +_RTLDECL(realloc, void *, (void *, size_t)); +_RTLDECL(free, void, (void *)); +_RTLDECL(memcpy, void *, (void *,const void *,size_t)); +_RTLDECL(memmove, void *, (void *,const void *,size_t)); +_RTLDECL(memset, void *, (void *,int,size_t)); +_RTLDECL(strcpy, char *, (char *,const char *)); +_RTLDECL(strncpy, char *, (char *,const char *,size_t)); +_RTLDECL(strcat, char *, (char *,const char *)); +_RTLDECL(strncat, char *, (char *,const char *,size_t)); +_RTLDECL(vsprintf, int, (char *,const char *,va_list)); +_RTLDECL(sprintf, int, (char *,const char *,...)); + +#if !defined (_CC_ANSI_) +/* *** MSC *** */ + +/* WATCOM doesn't support these... */ +#if !defined(_CC32_) +_RTLDECL(_fmalloc, void far *, (size_t)); +_RTLDECL(_fcalloc, void far *, (size_t, size_t)); +_RTLDECL(_ffree, void, (void far *)); +_RTLDECL(_fmsize, size_t, (void far *)); +#endif + +_RTLDECL(_nmalloc, void _MCNEAR *,(size_t)); +_RTLDECL(_nfree, void, (void _MCNEAR *)); + +/* *** Borland *** */ + +#if !defined(_CC_POWERPACK32_) +_RTLDECL(farmalloc, void _MCFAR *, (unsigned long)); +_RTLDECL(farcalloc, void _MCFAR *, (unsigned long, unsigned long)); +_RTLDECL(farfree, void, (void _MCFAR *)); + +/* *** General Porpoise *** */ + +_RTLDECL(_fmemset, void far * _MCFARGLUE, (void far *,int,size_t)); +_RTLDECL(_fmemcpy, void far * _MCFARGLUE, (void far *,const void far *,size_t )); +_RTLDECL(_fstrcpy, char far * _MCFARGLUE, (char far *,const void far *)); +#endif /* not _CC_POWERPACK32_ */ + +#endif /* not STDC/ANSI */ + +/***************************************************************** + * -------- Function Call Interception Definitions --------- * + *****************************************************************/ + +#ifndef MEMCHECK_MODULE + +/* + This section targets user's code only +*/ + +/* Func interceptors... */ +#define _INTERCEPT(_f) (_mcsl(_MCSF_,_MCSL_),_f) +#define _VA_INTERCEPT(_f) (*_loc_##_f(_MCSF_,_MCSL_)) +#define _SETLOC(_f) (mc_set_location(),_f) + +/* NOTE near _mcsl with #if (_MCC_NEAR_INTERCEPT == 0) */ + +/* + MC_NO_TRANSFER_SIZE is used to eliminate errors or warnings + like "sizeof returns 0" or "Not allowed type in sizeof ". + These occur for unsized variables declared like + + extern unsigned char gHelpString[]; + + The optimal solution is to "size" the extern, e.g. + + extern unsigned char gHelpString[80]; + + but where this may not be practical, MC_NO_TRANSFER_SIZE may + be defined on a module-by-module OR project-wide basis. +*/ +#ifdef MC_NO_XFER_SIZE /* beta compat */ +# define MC_NO_TRANSFER_SIZE +#endif +#ifdef NO_TRANSFER_SIZE /* alternate */ +# define MC_NO_TRANSFER_SIZE +#endif + +#if defined (MC_NO_TRANSFER_SIZE) +# define _INTERCEPTX(_f,_d) _INTERCEPT(_f) +#else /* standard; transmit sizeof dest */ +# define _INTERCEPTX(_f,_d) (_mcslx(_MCSF_,_MCSL_,sizeof(_d)),_f) +#endif + + +/* Intrinsic Function Disabling + + It's important to disable function inlining for + all intercepted functions. +*/ + +#if defined(_CC_MSC_) + +/* Intrinsics (== in-line functions) not permissible + since they're implemented as macros... +*/ +#pragma function(strcat) +#pragma function(strcpy) +#pragma function(memcpy) +#pragma function(memset) + +#pragma function(strset) + +#if defined(_MSC_VER) +#if (_MSC_VER >= 700) +#pragma function(_fmemcpy) +#pragma function(_fmemset) +#pragma function(_fstrcat) +#pragma function(_fstrcpy) +#pragma function(_fstrset) +#pragma function(_strset) +#endif +#endif /* defined _MSC_VER */ + +#elif defined(_CC_BORLAND_) + +/* Turbo C not like pragmae */ +#if !defined (_CC_TCC_) + +/* Eliminate duplicate strings. + This can save a bit of space in large + programs particularly, since each call to + MemCheck references an otherwise separate + copy of the current filename. +*/ +#pragma option -d + +/* Intrinsics (== in-line functions) not permissible + since they're implemented as macros, for one... +*/ +#pragma intrinsic -strcat +#pragma intrinsic -strncat +#pragma intrinsic -strcpy +#pragma intrinsic -strncpy +#pragma intrinsic -stpcpy +#pragma intrinsic -strset +#pragma intrinsic -strnset +#pragma intrinsic -memcpy +#pragma intrinsic -memset + +#endif /* not Turbo C */ + +/* end Borland compiler intrinsics */ + +#elif defined (_CC_WATCOM_) + +/* NOTE: unfortunately, WATCOM C/C++ compilers + force inlining of the strcpy() function regardless + of whether you want it inlined or not, all the time. + So this pragma, while it should ensure that + strcpy() is a function call, does not... :{ + + So we take other measures below: see _mcwatcom_strcpy() +*/ +#pragma function(strcpy) + +#pragma function(strcat) +#pragma function(memcpy) +#pragma function(memset) + +#pragma function(_fmemcpy) +#pragma function(_fmemset) +#pragma function(_fstrcat) +#pragma function(_fstrcpy) + +#endif + +/* End disable function inlining */ + + +/*lint -save -e652 Define of symbol declared previously */ +#if defined (MC_NO_INTERCEPT) +#define NO_INTERCEPT +#endif + +#if !defined (NO_INTERCEPT) + +/* *** ANSI Standard C *** */ + +#define calloc(n,_sz) _INTERCEPT(calloc(n,_sz)) +#define malloc(_sz) _INTERCEPT(malloc(_sz)) +#define realloc(p,s) _INTERCEPT(realloc(p,s)) +#define free(p) _INTERCEPT(free(p)) + +#define memcpy(d,s,n) _INTERCEPTX(memcpy(d,s,n),d) +#define memmove(d,s,n) _INTERCEPTX(memmove(d,s,n),d) +#define memset(p,c,n) _INTERCEPTX(memset(p,c,n),p) +#define strcat(s1,s2) _INTERCEPTX(strcat(s1,s2),s1) +#if defined(_CC_WATCOM_) + /* WATCOM forces inlining of strcpy()... see note above */ +# define strcpy(d,s) _INTERCEPTX(_mcwatcom_strcpy(d,s),d) + extern char * _RTL _mcwatcom_strcpy (char *, const char *); +#else +# define strcpy(d,s) _INTERCEPTX(strcpy(d,s),d) +#endif +#define strncat(s1,s2,n) _INTERCEPTX(strncat(s1,s2,n),s1) +#define strncpy(d,s,n) _INTERCEPTX(strncpy(d,s,n),d) +#define vsprintf(s,f,a) _INTERCEPTX(vsprintf(s,f,a),s) + +/* #define sprintf _VA_INTERCEPT(sprintf) */ +#ifndef _lint +#define sprintf _INTERCEPT(sprintf) +#endif + +#if defined(_CC_MSC_COMPATIBLE_) /* *** Microsoft C *** */ + +#define _expand(_p,_s) _INTERCEPT(_expand(_p,_s)) +#define _fcalloc(n,_sz) _INTERCEPT(_fcalloc(n,_sz)) +#define _fexpand(_p,_s) _INTERCEPT(_fexpand(_p,_s)) +#define _ffree(p) _INTERCEPT(_ffree(p)) +#define _fmalloc(_sz) _INTERCEPT(_fmalloc(_sz)) +#define _frealloc(p,s) _INTERCEPT(_frealloc(p,s)) +#define _fmsize(p) _INTERCEPT(_fmsize(p)) +#define _msize(p) _INTERCEPT(_msize(p)) +#define _nfree(p) _INTERCEPT(_nfree(p)) +#define _nmalloc(_sz) _INTERCEPT(_nmalloc(_sz)) +#define _nrealloc(p,s) _INTERCEPT(_nrealloc(p,s)) +#define _ncalloc(n,_sz) _INTERCEPT(_ncalloc(n,_sz)) +#define _nexpand(_p,_s) _INTERCEPT(_nexpand(_p,_s)) +#define _nmsize(p) _INTERCEPT(_nmsize(p)) +#define _nstrdup(s) _INTERCEPT(_nstrdup(s)) +/* #define halloc(n,_sz) _INTERCEPT(halloc(n,_sz)) */ +/* #define _halloc(n,_sz) _INTERCEPT(halloc(n,_sz)) */ +/* #define hfree(p) _INTERCEPT(hfree(p)) */ +/* #define _hfree(p) _INTERCEPT(hfree(p)) */ + +#define cgets(s) _INTERCEPTX(cgets(s),s) +#define _cgets(s) _INTERCEPTX(_cgets(s),s) +#define memccpy(d,s,c,n) _INTERCEPTX(memccpy(d,s,c,n),d) +#define _memccpy(d,s,c,n) _INTERCEPTX(_memccpy(d,s,c,n),d) +#define strdup(s) _INTERCEPT(strdup(s)) +#define _strdup(s) _INTERCEPT(_strdup(s)) +#define _strnset(s,c,n) _INTERCEPTX(_strnset(s,c,n),s) +#define strnset(s,c,n) _INTERCEPTX(strnset(s,c,n),s) +#define strset(s,c) _INTERCEPTX(strset(s,c),s) +#define _strset(s,c) _INTERCEPTX(_strset(s,c),s) +#define swab(s,d,n) _INTERCEPTX(swab(s,d,n),d) +#define _swab(s,d,n) _INTERCEPTX(_swab(s,d,n),d) +#define tempnam(d,pfx) _INTERCEPT(tempnam(d,pfx)) +#define _tempnam(d,pfx) _INTERCEPT(_tempnam(d,pfx)) + +#define _fmemcpy(d,s,n) _INTERCEPTX(_fmemcpy(d,s,n),d) +#define _fmemmove(d,s,n) _INTERCEPTX(_fmemmove(d,s,n),d) +#define _fmemset(d,c,n) _INTERCEPTX(_fmemset(d,c,n),d) +#define _fmemccpy(d,s,c,n) _INTERCEPTX(_fmemccpy(d,s,c,n),d) +#define _fstrcat(s1,s2) _INTERCEPTX(_fstrcat(s1,s2),s1) +#define _fstrcpy(d,s) _INTERCEPTX(_fstrcpy(d,s),d) +#define _fstrncat(s1,s2,n) _INTERCEPTX(_fstrncat(s1,s2,n),s1) +#define _fstrncpy(d,s,n) _INTERCEPTX(_fstrncpy(d,s,n),d) +#define _fstrdup(s) _INTERCEPT(_fstrdup(s)) +#define _fstrnset(d,c,n) _INTERCEPTX(_fstrnset(d,c,n),d) +#define _fstrset(d,c) _INTERCEPTX(_fstrset(d,c),d) + +#define getcwd(d,n) _INTERCEPTX(getcwd(d,n),d) +#define _getcwd(d,n) _INTERCEPTX(getcwd(d,n),d) +#define _getdcwd(r,d,n) _INTERCEPTX(_getdcwd(r,d,n),d) +#define _dos_setvect(_i,_h) _INTERCEPT(_dos_setvect(_i,_h)) +#define _fullpath(b,p,n) _INTERCEPTX(_fullpath(b,p,n),b) + +/* ----- END Microsoft C/C++ interceptions ----- */ + +#elif defined (_CC_BORLAND_) /* *** Borland C/C++ *** */ + +#ifndef _CC_POWERPACK32_ +#define farfree(p) _INTERCEPT(farfree(p)) +#define farmalloc(s) _INTERCEPT(farmalloc(s)) +#define farcalloc(n,s) _INTERCEPT(farcalloc(n,s)) +#define farrealloc(p,s) _INTERCEPT(farrealloc(p,s)) +#endif /* not _CC_POWERPACK32_ */ + +#define cgets(s) _INTERCEPTX(cgets(s),s) +#define memccpy(d,s,c,n) _INTERCEPTX(memccpy(d,s,c,n),d) +#if !defined(movmem) +#define movmem(s,d,l) _INTERCEPTX(movmem(s,d,l),d) +#endif +#if !defined(setmem) +#define setmem(d,c,v) _INTERCEPTX(setmem(d,c,v),d) +#endif +#define setvect(i,v) _INTERCEPT(setvect(i,v)) +#define stpcpy(d,s) _INTERCEPTX(stpcpy(d,s),d) +#define _stpcpy(d,s) _INTERCEPTX(_stpcpy(d,s),d) +#define strdup(s) _INTERCEPT(strdup(s)) +#define strnset(s,c,n) _INTERCEPTX(strnset(s,c,n),s) +#define strset(s,c) _INTERCEPTX(strset(s,c),s) +#define swab(s,d,n) _INTERCEPTX(swab(s,d,n),d) +#define tempnam(d,pfx) _INTERCEPT(tempnam(d,pfx)) + +#define getcwd(d,n) _INTERCEPTX(getcwd(d,n),d) +#define _getdcwd(r,d,n) _INTERCEPTX(_getdcwd(r,d,n),d) +#define _dos_setvect(_i,_h) _INTERCEPT(_dos_setvect(_i,_h)) + +#ifndef _CC_POWERPACK32_ +#define _fmemcpy(d,s,n) _INTERCEPTX(_fmemcpy(d,s,n),d) +#define _fmemmove(d,s,n) _INTERCEPTX(_fmemmove(d,s,n),d) +#define _fmemset(d,c,n) _INTERCEPTX(_fmemset(d,c,n),d) +#define _fmemccpy(d,s,c,n) _INTERCEPTX(_fmemccpy(d,s,c,n),d) +#define _fmovmem(s,d,l) _INTERCEPTX(_fmovmem(s,d,l),s) +#define _fsetmem(d,c,v) _INTERCEPTX(_fsetmem(d,c,v),d) +#define _fstrcat(s1,s2) _INTERCEPTX(_fstrcat(s1,s2),s1) +#define _fstrcpy(d,s) _INTERCEPTX(_fstrcpy(d,s),d) +#define _fstrncat(s1,s2,n) _INTERCEPTX(_fstrncat(s1,s2,n),s1) +#define _fstrncpy(d,s,n) _INTERCEPTX(_fstrncpy(d,s,n),d) +#define _fstrdup(s) _INTERCEPT(_fstrdup(s)) +#define _fstrnset(d,c,n) _INTERCEPTX(_fstrnset(d,c,n),d) +#define _fstrset(d,c) _INTERCEPTX(_fstrset(d,c),d) +#endif /* not _CC_POWERPACK32_ */ + +/* +#define freemem(g) _INTERCEPT(freemem(g)) +#define vsscanf(d,f,a) _INTERCEPTX(vsscanf(d,f,a),d) +*/ + +/* ----- END Borland C/C++ interceptions ----- */ + +#else + +#error Unknown compiler in MemCheck.h + +#endif /* Compiler-specific Function Mapping Section */ + +/* Location Transmitters + + You can add any non-intercepted functions to + this bunch... Just updates MemCheck's file and line + information via mc_set_location(), which is thousands + of times faster than anything that does I/O. + The only time this section could be a problem is + if the header file is included before any other header + files which prototype these routines. + + Borland's TD (Turbo Debugger) also has problems here (see note). +*/ +#ifndef _lint /* LINT not like */ + +/* Borland's Turbo Debugger gets confoosed and executes + a `Run' instead of a `Step' when _SETLOC macro is used... +*/ +#if !defined (_CC_BORLAND_) +#if 1 /* Change this to '0' to omit this section */ + +#define printf _SETLOC(printf) + +#define fopen _SETLOC(fopen) +#define fprintf _SETLOC(fprintf) +#define fread _SETLOC(fread) +#define fwrite _SETLOC(fwrite) +#define fclose _SETLOC(fclose) + +#define system _SETLOC(system) +#define exec _SETLOC(exec) +#define spawnl _SETLOC(spawnl) +#define spawnlp _SETLOC(spawnlp) +#define spawnle _SETLOC(spawnle) +#define spawnlpe _SETLOC(spawnlpe) +#define spawnv _SETLOC(spawnv) +#define spawnvp _SETLOC(spawnvp) +#define spawnve _SETLOC(spawnve) +#define spawnvpe _SETLOC(spawnvpe) + +#endif /* end location transmission section */ +#endif /* not Borland C++ */ +#endif /* not def _lint */ + + +/* **** THIRD-PARTY MAPPINGS **** */ + +/* Vermont Views V3.xx + + The following code will transmit the exact file + and line of any mem_get() and mem_free() calls to + MemCheck, so that it can report on the location where + these functions are called, instead of the location of + the calloc() or free(). + + If you've used MCCONFIG to configure the Vermont Views source + code, you *must* either NOT include the MemCheck header file + in the MEM_GET.C and MEM_FREE.C modules, or, if you do, then + #define NO_INTERCEPT beforehand, e.g. + + Module MEM_GET.C ... + : + #define NO_INTERCEPT + #include + : + + MCCONFIG may be used to configure even the shrouded + Vermont Views source code. + + See also: TechNote "Using MemCheck 3.0 Professional + With Vermont Views", available on the StratosWare + BBS (313) 996-2993 as VIEWS.TXT, or by fax. +*/ +#if defined (VV_SYS) /* should do the trick */ +# define mem_get(s) _INTERCEPT(mem_get(s)) +# define mem_free(p) _INTERCEPT(mem_free(p)) +#endif + + +/* **** APPLICATION-SPECIFIC MAPPINGS **** */ + +/* + If your application uses allocation "cover" routines, + MemCheck will by default report errors and leaks by + the file and line of the malloc or free within the + cover module. To get MemCheck to report by file and + line where the cover function is actually called, follow + the instructtions in MemCheck TechNote "Transmitting File + and Line to MemCheck 3.0 Professional Through Cover Functions." + + This is where you can place the cover function macros. +*/ + + +/* end APPLICATION-SPECIFIC MAPPINGS */ + +#endif /* not NO_INTERCEPT */ + +/* Calls that xmit source file, line number if called in source */ +/* *** MemCheck API file & line transmittal *** */ +#define mc_startcheck(erf) mc_startcheck(_MCSF_,_MCSL_,erf) +#define mc_stack_trace(_memo) _INTERCEPT(mc_stack_trace(_memo)) +#define mc_debug(s) _INTERCEPT(mc_debug(s)) +#define mc_debugf(arglist) mc_debugv arglist +#define mc_debugv _mcsl(_MCSF_,_MCSL_),mc_debugv +#define mc_endcheck() _INTERCEPT(mc_endcheck()) +#define mc_check_buffers() _INTERCEPT(mc_check_buffers()) +#define mc_check(p) _INTERCEPT(mc_check(p)) +#define mc_register(p,s) _INTERCEPT(mc_register(p,s)) +#define mc_unregister(p) _INTERCEPT(mc_unregister(p)) +#define mc_nullcheck() _INTERCEPT(mc_nullcheck()) +#define mc_report(f) _INTERCEPT(mc_report(f)) + +/*lint -restore 652 Define of symbol declared prev */ + +#endif /* not MEMCHECK_MODULE, function interceptions */ + + +/* End "C" call wrapper */ +#ifdef __cplusplus +} + + +/* C++ MemCheck Class + + This class can be used as an alternative to + AutoInit, or to placing the mc_startcheck() and + mc_endcheck() calls in your main() program. + Just declaring an object of class 'MemCheck' + will start MemCheck up; usually you will place + this 'above' any other global or statically declared + C++ objects in your main module. + + Here are some examples of starting MemCheck up + via object mechanics: + + MemCheck On; + MemCheck Active; + MemCheck Rules; + + Use your imagination! Note that if AutoInit is ON, + any calls to mc_startcheck() and mc_endcheck() are + ignored. +*/ +#if !defined (NO_INTERCEPT) /* must not have this def'd */ + +/* This class def causes a warning under MSC if not used */ +#if !defined (_CC_MSC_) + +class MemCheck { +public: + MemCheck () { mc_startcheck (NULL); } + ~MemCheck () { mc_endcheck (); } +}; + +#endif + +#endif /* NO_INTERCEPT */ + + +/* *** For use in new and delete modules only *** */ +/* + Replace 'mallocs' with 'cpp_mallocs', etc. + In new and delete modules, NO_INTERCEPT should be #defined, e.g. + + #define NO_INTERCEPT + #include + : + void * operator new ( size_t size ) + { + if (!size) size = 1; + return (cpp_malloc (size)); + } + etc. +*/ +#define cpp_malloc(_s) (_mc_set_newflag(), malloc(_s)) +#define cpp_calloc(_n,_s) (_mc_set_newflag(), calloc(_n,_s)) +#define cpp_free(_p) (_mc_set_delflag(), free(_p)) + +/* Borland C++ */ +#define cpp_farmalloc(_s) (_mc_set_newflag(), farmalloc(_s)) +#define cpp_farfree(_fp) (_mc_set_delflag(), farfree(_fp)) + +/* Microsoft C++-compatibles */ +#define cpp__fmalloc(_s) (_mc_set_newflag(), _fmalloc(_s)) +#define cpp__ffree(_fp) (_mc_set_delflag(), _ffree(_fp)) + + +/* C++ */ +#if !defined (NO_INTERCEPT) +#if !defined (NO_CPP) + +/* + This method is off by default, because it + requires definition of a new operator like: + + void * new (size_t size, char *file, int lineno); + + Such a new operator is included in your SOURCE\CPP + directory. To have this method used for all modules, + #define NEW_OVERLOADED at the top of this header file + or in your project #include file, BEFORE the MemCheck + header file is #included. + + The substitutions for the new operator + may not work in all situations. To disable + MemCheck's interception of new on a module-by- + module basis by undefining NEW_OVERLOADED. +*/ +#if defined (NEW_OVERLOADED) + +/* Method 1: Placement Operators + + Use placement operators to trap file and line location transparently + on calls to new. + + Thanks for this tip to Dan Saks, + C and C++ writer, author, teacher, and columnist--buy his books. + He came through when no one else had a clue! + + Please consult your manual, MemCheck technotes, + or StratosWare Technical Support (1-800-WE-DEBUG) + for details on how to configure your project for + use with an overloaded new placement operator. +*/ + +/* Declare overloaded new with placement operators */ +void *operator new (size_t _sz, char *file, int lineno); +#if defined (_CPP_ANSI20_) +/* Array version; only under supporting compilers + COMMENT LINE OUT if it causes a compile error. +*/ +void *operator new[] (size_t _sz, char *file, int lineno); +#endif + +#if !defined (_CC_MSC_) +#define new new((char *)__FILE__,(int)__LINE__) +#else +#define new new(__FILE__,__LINE__) +#endif + +/* NOTE: + This placement operator interception syntax has been + known to cause syntax errors (in VC++ 1.0) for no apparent reason + on statements like + + Domain *d = new Domain (); + + Workaround is to change line (sorry!) to equivalent + + Domain *d = new Domain; +*/ + +/* Backwards compatibility with the V2.1 C++ macros */ +#ifndef NEW +#define NEW(_object) new _object +#endif +#ifdef DELETE +#define DELETE(_object) delete _object +#endif +#define DELETE_ARR(_arr) delete[] _arr + +#else /* !NEW_OVERLOADED - end of Placement Operator intercept */ + +/* New and Delete Interception, Method 2: NEW() and DELETE() + + The NEW() and DELETE() macros may be used to transmit file + and line of new and delete. These macros, which require + modification of source code, i.e. "NEW(object)" for "new object", + should probably be used only if the above overloaded new does + not work for your code base. + + Please consult your manual, MemCheck technotes, + or StratosWare Technical Support (1-800-WE-DEBUG) + for details on how to configure your project for + use with NEW() and DELETE(). + + If calling, please have your MemCheck serial number handy. +*/ +#ifndef NEW +#define NEW(_object) (_mcsl_new(_MCSF_,_MCSL_), new _object) +#endif +#ifndef DELETE /* WINNT.H under BC DPMI32 defines DELETE */ +#define DELETE(_object) (_mcsl_delete(_MCSF_,_MCSL_), delete _object) +#endif +#define DELETE_ARR(_arr) (_mcsl_delete(_MCSF_,_MCSL_), delete[] _arr) + +#endif /* !NEW_OVERLOADED */ + +#define delete _mcsl_delete(_MCSF_,_MCSL_), delete + + +/* *** FAILURES *** */ + +/* These macros failed in the purpose of + intercepting new transparently in some + situation or other. +*/ + +/* Failed on " * new expr " (TV) */ +/* #define new (mc_set_location(),0) ? NULL : new */ + +/* Failed on " x = new Object " (TV) */ +/* #define new ((mc_set_location(),0) ? NULL : new) */ +/* #define new new (mc_set_location(),0) ? NULL : */ + +#endif /* !NO_CPP */ +#endif /* NO_INTERCEPT */ +#endif /* cplusplus */ +/******** End C++ ************/ + + +#endif /* End of Section for MEMCHECK Defined */ + +/* -------------------------------------------------------------------------- */ + +#endif /* _MEMCHECK_H_ */ + + +/******************************** + * End of MemCheck 3.0 Header * + ********************************/ + diff --git a/MENUS.CPP b/MENUS.CPP new file mode 100644 index 0000000..873e5c1 --- /dev/null +++ b/MENUS.CPP @@ -0,0 +1,932 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\menus.cpv 2.17 16 Oct 1995 16:50:48 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MENUS.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : May 17, 1995 [BRR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Main_Menu -- Menu processing * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "ccdde.h" + +/***************************** +** Function prototypes +******************************/ + +#ifdef SCENARIO_EDITOR + +PRIVATE int Coordinates_In_Region(int x,int y,int inx1,int iny1,int inx2,int iny2); +PRIVATE int Select_To_Entry(int select, unsigned long bitfield, int index); +PRIVATE void Flash_Line(char const *text,int xpix,int ypix,unsigned nfgc,unsigned hfgc,unsigned bgc); + +int UnknownKey; + +PRIVATE int MenuUpdate=1; +PRIVATE int MenuSkip; + + +/*=========================================================================*/ +/* SELECT_TO_ENTRY: */ +/* */ +/* This routine converts a selection to the correct string entry. It */ +/* does this by search through a long bitfield starting at position index */ +/* until it finds the correct conversion to entries. */ +/* */ +/* INPUTS: int selection from menu, long the bit field to search, int */ +/* the starting index within the bit field. */ +/* RETURNS: int the index into the table of entries */ +/*=========================================================================*/ +PRIVATE int Select_To_Entry(int select, unsigned long bitfield, int index) +{ + int placement; + + if (bitfield==0xFFFFFFFFL) /* if all bits are set */ + return(select); /* then it as is */ + + placement=0; /* current pos zero */ + while (select) { /* while still ones */ + if (bitfield & (1L<<(placement+index))) /* if this flagged then */ + select--; /* decrement counter */ + placement++; /* and we moved a place */ + } + while (!(bitfield & (1L<<(placement+index)))) { + placement++; + } + + return(placement); /* return the position */ +} + + +/*=========================================================================*/ +/* FLASH_LINE: */ +/* */ +/* This routine will flash the line at the desired location for the */ +/* menu routine. It is way cool awesome! */ +/* */ +/* INPUTS: char *text, int x position on line, int y position, char */ +/* normal foreground color, char hilight foreground color, char */ +/* background color */ +/* RETURNS: none */ +/*=========================================================================*/ +PRIVATE void Flash_Line(char const *text,int xpix,int ypix,unsigned nfgc,unsigned hfgc,unsigned bgc) +{ + int loop; + + for (loop=0;loop<3;loop++) { + Hide_Mouse(); + Fancy_Text_Print(text,xpix,ypix,hfgc,bgc, TPF_8POINT|TPF_DROPSHADOW); + Delay(2); + Fancy_Text_Print(text,xpix,ypix,nfgc,bgc, TPF_8POINT|TPF_DROPSHADOW); + Show_Mouse(); + Delay(2); + } +} + +/*=========================================================================*/ +/* COORDINATES_IN_REGION: */ +/* */ +/* Test to see if a given pair of coordinates are within the given */ +/* rectangular region. */ +/* */ +/* INPUTS: int x to be tested, int y to be tested, int left x pos, */ +/* int top y pos, int right x pos, int bottom y pos */ +/* RETURNS: none */ +/*=========================================================================*/ +PRIVATE int Coordinates_In_Region(int x,int y,int inx1,int iny1,int inx2,int iny2) +{ + return((x>=inx1)&&(x<=inx2)&&(y>=iny1)&&(y<=iny2)); +} + +#ifdef NEVER +/*=========================================================================*/ +/* FIND_MENU_ITEMS: */ +/* */ +/* This routine finds the real total items in a menu when certain items */ +/* may be disabled by bit fields and the like. This is done by looping */ +/* through the fields, starting at the position passed in index and */ +/* counting the number of bits that are set. */ +/* */ +/* INPUTS: int the maximum number of items possible on the menu, long */ +/* the bit field of enabled and disabled items, char the index */ +/* point to start at within the list. */ +/* RETURNS: int the total number of items in the menu */ +/*=========================================================================*/ + int Find_Menu_Items(int maxitems, unsigned long field, char index) + { + int loop,ctr; + + if (field==0xFFFFFFFFL) /* if all bits are set */ + return(maxitems); /* then maxitems set */ + + for (loop=ctr=0;loop>1; /* adjustment for menus */ + + menuy = WinY+menuptr[MENUY]; /* get the absolute */ + menux = (WinX+menuptr[MENUX])<<3; /* coords of menu */ + normcol = menuptr[NORMCOL]; + litcol = menuptr[HILITE]; + + /* + ** Fetch a pending keystroke from the buffer if there is a keystroke + ** present. If no keystroke is pending then simple mouse tracking will + ** be done. + */ + key = 0; + UnknownKey = 0; + if (Keyboard::Check()) { + key = (Keyboard::Get()&0x18FF); /* mask off all but release bit */ + } + + /* + ** if we are using the mouse and it is installed, then find the mouse + ** coordinates of the menu and if we are not somewhere on the menu get + ** the heck outta here. If we are somewhere on the menu, then figure + ** out the new selected item, and continue forward. + */ + mx1=(WinX<<3)+(menuptr[MENUX]*FontWidth); /* get menu coords */ + my1=(WinY)+(menuptr[MENUY])-halfskip; /* from the menu */ + mx2=mx1+(menuptr[ITEMWIDTH]*FontWidth)-1; /* structure as */ + my2=my1+(menuptr[ITEMSHIGH]*menuskip)-1; /* necessary */ + + tempy=Get_Mouse_Y(); + if (Coordinates_In_Region(Get_Mouse_X(),tempy,mx1,my1,mx2,my2)&& MenuUpdate) { + newitem=(tempy-my1)/menuskip; + } + + switch (key) { + + case KN_UP: /* if the key moves up */ + newitem--; /* new item up one */ + if (newitem<0) /* if invalid new item */ + newitem=maxitem; /* put at list bottom */ + break; + case KN_DOWN: /* if key moves down */ + newitem++; /* new item down one */ + if (newitem>maxitem) /* if new item past */ + newitem=0; /* list end, clear */ + break; + case KN_HOME: /* if top of list key */ + case KN_PGUP: /* is selected then */ + newitem=0; /* new item = top */ + break; + case KN_END: /* if bottom of list is */ + case KN_PGDN: /* selected then */ + newitem=maxitem; /* new item = bottom */ + break; + + /* + ** Handle mouse button press. Set selection and then fall into the + ** normal menu item select logic. + */ + case KN_RMOUSE: + case KN_LMOUSE: + if (Coordinates_In_Region(_Kbd->MouseQX,_Kbd->MouseQY,mx1,my1,mx2,my2)) { + newitem = (_Kbd->MouseQY - my1) / menuskip; + } else { + UnknownKey = key; // Pass the unprocessed button click back. + break; + } + + /* + ** Normal menu item select logic. Will flash line and exit with menu + ** selection number. + */ + case KN_RETURN: /* if a selection is */ + case KN_SPACE: /* made with key */ + case KN_CENTER: + select=newitem; /* flag it made. */ + break; + + case 0: + break; + + /* + ** When no key was pressed or an unknown key was pressed, set the + ** global record of the key and exit normally. + ** EXCEPTION: If the key matches the first letter of any of the + ** menu entries, then presume it as a selection of + ** that entry. + */ + default: + for (idx = 0; idx < menuptr[ITEMSHIGH]; idx++) { + if (toupper(*(text[Select_To_Entry(idx,field,index)])) == toupper(Keyboard::To_ASCII((KeyNumType)(key&0x0FF)))) { + newitem = select = idx; + break; + } + } + UnknownKey = key; + break; + } + + if (newitem!=item) { + Hide_Mouse(); + idx=Select_To_Entry(item,field,index); + drawy=menuy+(item*menuskip); + Fancy_Text_Print(text[idx],menux,drawy,normcol,TBLACK, TPF_8POINT|TPF_DROPSHADOW); + idx=Select_To_Entry(newitem,field,index); + drawy=menuy+(newitem*menuskip); + Fancy_Text_Print(text[idx],menux,drawy,litcol,TBLACK, TPF_8POINT|TPF_DROPSHADOW); + Show_Mouse(); /* resurrect the mouse */ + } + + if (select!=-1) { + idx=Select_To_Entry(select,field,index); + Hide_Mouse(); /* get rid of the mouse */ + drawy=menuy+(newitem*menuskip); + Flash_Line(text[idx], menux, drawy, normcol, litcol, TBLACK); + Show_Mouse(); + select=idx; + } + + menuptr[MSELECTED]=newitem; /* update menu select */ + + return(select); +} + + +/*************************************************************************** + * Do_Menu -- Generic menu processor. * + * * + * This helper function displays a menu of specified entries and waits * + * for the player to make a selection. If a selection is made, then * + * a whole number (starting at 0) is returned matching the entry * + * selected. If ESC is pressed, then -1 is returned. * + * * + * INPUT: strings -- A pointer to an array of pointers to text strings. * + * Each entry in the list will be a menu entry that * + * can be selected. * + * * + * blue -- Should the special blue color be used to display * + * the menu? * + * * + * OUTPUT: Returns with the cardinal number of the selected menu entry. * + * If ESC was pressed, then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1994 JLB : Created. * + *=========================================================================*/ +int Do_Menu(char const **strings, bool blue) +{ + int count; // Number of entries in this menu. + int length; // The width of the menu (in pixels). + char const **ptr; // Working menu text pointer. + int selection; // Selection from user. + + if (!strings) return(-1); + Set_Logic_Page(SeenBuff); + Keyboard::Clear(); + + /* + ** Determine the number of entries in this string. + */ + ptr = strings; + count = 0; + while (*ptr++) { + count++; + } + MenuList[0][ITEMSHIGH] = count; + + /* + ** Determine the width of the menu by finding the length of the + ** longest menu entry. + */ + Fancy_Text_Print(TXT_NONE, 0, 0, 0, 0, TPF_8POINT|TPF_DROPSHADOW); + length = 0; + ptr = strings; + while (*ptr) { + length = MAX(length, (int)String_Pixel_Width(*ptr)); + ptr++; + } + length += 7; + MenuList[0][ITEMWIDTH] = length >> 3; + + /* + ** Adjust the window values to match the size of the + ** specified menu. + */ + WindowList[WINDOW_MENU][WINDOWWIDTH] = MenuList[0][ITEMWIDTH] + 2; + WindowList[WINDOW_MENU][WINDOWX] = 19 - (length >> 4); + WindowList[WINDOW_MENU][WINDOWY] = 174 - (unsigned)(MenuList[0][ITEMSHIGH] * (FontHeight+FontYSpacing)); + WindowList[WINDOW_MENU][WINDOWHEIGHT] = MenuList[0][ITEMSHIGH] * FontHeight + 5 /*11*/; + + /* + ** Display the menu. + */ + Change_Window((int)WINDOW_MENU); + Show_Mouse(); + Window_Box(WINDOW_MENU, blue ? BOXSTYLE_BLUE_UP : BOXSTYLE_RAISED); + Setup_Menu(0, strings, 0xFFFFL, 0, 0); + + Keyboard::Clear(); + selection = -1; + UnknownKey = 0; + while (selection == -1) { + Call_Back(); + selection = Check_Menu(0, strings, NULL, 0xFFL, 0); + if (UnknownKey != 0 || UnknownKey == KN_ESC || UnknownKey==KN_LMOUSE || UnknownKey==KN_RMOUSE) break; + } + Keyboard::Clear(); + Hide_Mouse(); + + HidPage.Blit(SeenBuff); + Change_Window((int)WINDOW_MAIN); + Map.Flag_To_Redraw(true); + return(selection); +} +#endif + + +/*************************************************************************** + * Main_Menu -- Menu processing * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * index of item selected, -1 if time out * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/17/1995 BRR : Created. * + *=========================================================================*/ +int Main_Menu(unsigned long timeout) +{ + enum { + D_DIALOG_W = 152*2, + D_DIALOG_H = 136*2, + D_DIALOG_X = 85*2, + D_DIALOG_Y = 0, + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), + + D_START_W = 125*2, + D_START_H = 9*2, + D_START_X = 98*2, + D_START_Y = 35*2, + +#ifdef BONUS_MISSIONS + D_BONUS_W = 125*2, + D_BONUS_H = 9*2, + D_BONUS_X = 98*2, + D_BONUS_Y = 0, +#endif //BONUS_MISSIONS + + D_INTERNET_W = 125*2, + D_INTERNET_H = 9*2, + D_INTERNET_X = 98*2, + D_INTERNET_Y = 36*2, + + D_LOAD_W = 125*2, + D_LOAD_H = 9*2, + D_LOAD_X = 98*2, + D_LOAD_Y = 53*2, + + D_MULTI_W = 125*2, + D_MULTI_H = 9*2, + D_MULTI_X = 98*2, + D_MULTI_Y = 71*2, + + D_INTRO_W = 125*2, + D_INTRO_H = 9*2, + D_INTRO_X = 98*2, + D_INTRO_Y = 89*2, +#if (GERMAN | FRENCH) + D_EXIT_W = 83*2, +#else + D_EXIT_W = 63*2, +#endif + D_EXIT_H = 9*2, +#if (GERMAN | FRENCH) + D_EXIT_X = 118*2, +#else + D_EXIT_X = 128*2, +#endif + D_EXIT_Y = 111*2, + + }; + +#ifdef NEWMENU + int starty = 25*2; +#endif + + enum { +#ifdef NEWMENU + BUTTON_EXPAND=100*2, + BUTTON_START, +#ifdef BONUS_MISSIONS + BUTTON_BONUS, +#endif //BONUS_MISSIONS + BUTTON_INTERNET, +#else + BUTTON_START=100*2, +#endif + BUTTON_LOAD, + BUTTON_MULTI, + BUTTON_INTRO, + BUTTON_EXIT, + }; + +#ifdef NEWMENU + bool expansions = Expansion_Present(); +#endif + KeyNumType input; // input from user + int retval; // return value + int curbutton; +#ifdef NEWMENU +#ifdef BONUS_MISSIONS + TextButtonClass *buttons[8]; +#else + TextButtonClass *buttons[7]; +#endif //BONUS_MISSIONS +#else + TextButtonClass *buttons[5]; +#endif + unsigned long starttime; + + ControlClass *commands = NULL; // the button list + +#ifdef NEWMENU +#ifdef BONUS_MISSIONS + int ystep = 13*2; +#else + int ystep = 15*2; +#endif //BONUS_MISSIONS + + if (expansions) ystep -= 2*2; + TextButtonClass expandbtn (BUTTON_EXPAND, TXT_NEW_MISSIONS, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_START_X, starty, D_START_W, D_START_H); + if (expansions) starty += ystep; + + TextButtonClass startbtn (BUTTON_START, TXT_START_NEW_GAME, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_START_X, starty, D_START_W, D_START_H); + starty += ystep; + +#ifdef BONUS_MISSIONS + TextButtonClass bonusbtn (BUTTON_BONUS, TXT_BONUS_MISSIONS, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_BONUS_X, starty, D_BONUS_W, D_BONUS_H); + starty += ystep; +#endif //BONUS_MISSIONS + + TextButtonClass internetbutton(BUTTON_INTERNET, TXT_INTERNET, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_INTERNET_X, starty, D_INTERNET_W, D_INTERNET_H); + starty += ystep; + + TextButtonClass loadbtn (BUTTON_LOAD, TXT_LOAD_MISSION, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_LOAD_X, starty, D_LOAD_W, D_LOAD_H); + starty += ystep; +#else + + TextButtonClass startbtn (BUTTON_START, TXT_START_NEW_GAME, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_START_X, D_START_Y, D_START_W, D_START_H); + + TextButtonClass loadbtn (BUTTON_LOAD, TXT_LOAD_MISSION, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_LOAD_X, D_LOAD_Y, D_LOAD_W, D_LOAD_H); + +#endif + + +#ifdef DEMO + TextButtonClass multibtn (BUTTON_MULTI, TXT_ORDER_INFO, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI_X, D_MULTI_Y, D_MULTI_W, D_MULTI_H); +#else + +#ifdef NEWMENU + TextButtonClass multibtn (BUTTON_MULTI, TXT_MULTIPLAYER_GAME, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI_X, starty, D_MULTI_W, D_MULTI_H); + starty += ystep; + + //TextButtonClass internetbutton(BUTTON_INTERNET, TXT_INTERNET, + // TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + // D_INTERNET_X, starty, D_INTERNET_W, D_INTERNET_H); + //starty += ystep; +#else + TextButtonClass multibtn (BUTTON_MULTI, TXT_MULTIPLAYER_GAME, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_MULTI_X, D_MULTI_Y, D_MULTI_W, D_MULTI_H); +#endif +#endif + +#ifdef NEWMENU +#ifdef DEMO + TextButtonClass introbtn (BUTTON_INTRO, TXT_JUST_INTRO, +#else //DEMO + TextButtonClass introbtn (BUTTON_INTRO, TXT_INTRO, +#endif //DEMO + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_INTRO_X, starty, D_INTRO_W, D_INTRO_H); + starty += ystep; + + TextButtonClass exitbtn (BUTTON_EXIT, TXT_EXIT_GAME, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + //D_EXIT_X, starty); + D_EXIT_X, starty, D_EXIT_W, D_EXIT_H); +#else + D_EXIT_X, starty, D_EXIT_W, D_EXIT_H); +#endif + starty += ystep; + +#else + +#ifdef DEMO + TextButtonClass introbtn (BUTTON_INTRO, TXT_JUST_INTRO, +#else //DEMO + TextButtonClass introbtn (BUTTON_INTRO, TXT_INTRO, +#endif //DEMO + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + D_INTRO_X, D_INTRO_Y, D_INTRO_W, D_INTRO_H); + + TextButtonClass exitbtn (BUTTON_EXIT, TXT_EXIT_GAME, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + //D_EXIT_X, D_EXIT_Y); + D_EXIT_X, D_EXIT_Y, D_EXIT_W, D_EXIT_H); +#else + D_EXIT_X, D_EXIT_Y, D_EXIT_W, D_EXIT_H); +#endif +#endif + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + Keyboard::Clear(); + starttime = TickCount.Time(); + + /* + ** Create the list + */ + commands = &startbtn; +#ifdef NEWMENU + if (expansions) { + expandbtn.Add_Tail(*commands); + } +#endif +#ifdef BONUS_MISSIONS + bonusbtn.Add_Tail(*commands); +#endif //BONUS_MISSIONS + + +#ifndef DEMO + internetbutton.Add_Tail(*commands); +#endif //DEMO + loadbtn.Add_Tail(*commands); + multibtn.Add_Tail(*commands); + introbtn.Add_Tail(*commands); + exitbtn.Add_Tail(*commands); + + /* + ** Fill array of button ptrs + */ +#ifdef NEWMENU + if (expansions) { + curbutton = 0; + } else { + curbutton = 1; + } + int butt = 0; + + buttons[butt++] = &expandbtn; + buttons[butt++] = &startbtn; +#ifdef BONUS_MISSIONS + buttons[butt++] = &bonusbtn; +#endif //BONUS_MISSIONS + buttons[butt++] = &internetbutton; + buttons[butt++] = &loadbtn; + buttons[butt++] = &multibtn; + buttons[butt++] = &introbtn; + buttons[butt++] = &exitbtn; +#else + curbutton = 0; + buttons[0] = &startbtn; + buttons[1] = &loadbtn; + buttons[2] = &multibtn; + buttons[3] = &introbtn; + buttons[4] = &exitbtn; +#endif + buttons[curbutton]->Turn_On(); + + Keyboard::Clear(); + + Fancy_Text_Print(TXT_NONE, 0, 0, CC_GREEN, TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + while (Get_Mouse_State() > 0) Show_Mouse(); + + /* + ** Main Processing Loop. + */ + bool display = true; + bool process = true; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=TRUE; + } + + /* + ** If timeout expires, bail + */ + if (timeout && TickCount.Time() - starttime > timeout) { + retval = -1; + process = false; + } + + /* + ** Invoke game callback. + */ + Call_Back(); + + /* + ** Refresh display if needed. + */ + if (display) { + + /* + ** Load the background picture. + */ + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + + /* + ** Display the title and text overlay for the menu. + */ + Set_Logic_Page(HidPage); + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption (TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); +#ifdef VIRGIN_CHEAT_KEYS +#ifdef DEMO + Version_Number(); + Fancy_Text_Print("Demo%s", D_DIALOG_X+D_DIALOG_W-5*2, D_DIALOG_Y+D_DIALOG_H-10*2, DKGREY, TBLACK, TPF_6POINT|TPF_FULLSHADOW|TPF_RIGHT, VersionText); +#else + Fancy_Text_Print("V.%d%s", D_DIALOG_X+D_DIALOG_W-5*2, D_DIALOG_Y+D_DIALOG_H-10*2, DKGREY, TBLACK, TPF_6POINT|TPF_FULLSHADOW|TPF_RIGHT, Version_Number(), VersionText, FOREIGN_VERSION_NUMBER); +#endif +// Fancy_Text_Print("V.%d%s%02d", D_DIALOG_X+D_DIALOG_W-5, D_DIALOG_Y+D_DIALOG_H-10, DKGREY, TBLACK, TPF_6POINT|TPF_FULLSHADOW|TPF_RIGHT, Version_Number(), VersionText, FOREIGN_VERSION_NUMBER); +#else +#ifdef DEMO + Version_Number(); + Fancy_Text_Print("Demo%s", D_DIALOG_X+D_DIALOG_W-5*2, D_DIALOG_Y+D_DIALOG_H-10*2, DKGREY, TBLACK, TPF_6POINT|TPF_FULLSHADOW|TPF_RIGHT, VersionText); +#else + Fancy_Text_Print("V.%d%s", D_DIALOG_X+D_DIALOG_W-5*2, D_DIALOG_Y+D_DIALOG_H-10*2, DKGREY, TBLACK, TPF_6POINT|TPF_FULLSHADOW|TPF_RIGHT, Version_Number(), VersionText); +#endif +#endif + + /* + ** Copy the menu to the visible page. + */ + Hide_Mouse(); + HidPage.Blit(SeenBuff); + Show_Mouse(); + + Set_Logic_Page(SeenBuff); + startbtn.Draw_All(); + if (ScreenWidth==320){ + ModeX_Blit (SeenBuff.Get_Graphic_Buffer()); + } + display = false; + } + +#ifndef DEMO + /* + ** Check to see if WChat has told us to start playing an internet game + */ + if (DDEServer.Get_MPlayer_Game_Info()){ + retval = BUTTON_INTERNET - BUTTON_EXPAND; + process = false; + } +#endif //DEMO + + /* + ** Get and process player input. + */ + input = commands->Input(); + switch (input) { +#ifdef NEWMENU + case (BUTTON_EXPAND | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; + + case (BUTTON_INTERNET | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; + +#else +#define BUTTON_EXPAND BUTTON_START +#endif + + case (BUTTON_START | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; + +#ifdef BONUS_MISSIONS + case (BUTTON_BONUS | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; +#endif //BONUS_MISSIONS + + case (BUTTON_LOAD | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; +#ifdef DEMO + retval += 1; +#endif //DEMO + process = false; + break; + + case (BUTTON_MULTI | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; +#ifdef DEMO + retval += 1; +#endif //DEMO + process = false; + break; + + case (BUTTON_INTRO | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; +#ifdef DEMO + retval += 1; +#endif //DEMO + process = false; + break; + + case (BUTTON_EXIT | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; +#ifdef DEMO + retval += 1; +#endif //DEMO + process = false; + break; + + case KN_UP: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton--; +#ifdef NEWMENU + if (expansions) { + if (curbutton < 0) { + curbutton = 6; + } + } else { + if (curbutton < 1) { + curbutton = 6; + } + } +#else + if (curbutton < 0) { + curbutton = 4; + } +#endif + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_DOWN: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton++; +#ifdef NEWMENU + if (curbutton > 6) { + if (expansions) { + curbutton = 0; + } else { + curbutton = 1; + } + } +#else + if (curbutton > 4) { + curbutton = 0; + } +#endif + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_RETURN: + buttons[curbutton]->IsPressed = true; + buttons[curbutton]->Draw_Me(true); + retval = curbutton; + process = false; + break; + + default: + break; + } + } + return(retval); +} diff --git a/MESSAGE.H b/MESSAGE.H new file mode 100644 index 0000000..25d1a4d --- /dev/null +++ b/MESSAGE.H @@ -0,0 +1,47 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +#define MESSAGE_NONE 0 // +#define MESSAGE_BUILD_WINDTRAP 1 // You must build a Windtrap +#define MESSAGE_STRUCT_CONCRETE 2 // Concrete: Use concrete to +#define MESSAGE_STRUCT_PALACE 3 // Palace: This is your +#define MESSAGE_STRUCT_LIGHT 4 // Light Factory: The Light +#define MESSAGE_STRUCT_HEAVY 5 // Heavy Factory: The Heavy +#define MESSAGE_STRUCT_HITECH 6 // Hi-Tech Factory: The +#define MESSAGE_STRUCT_IX 7 // House IX: The IX Research +#define MESSAGE_STRUCT_WOR 8 // WOR: Wor is used to train +#define MESSAGE_STRUCT_CONST 9 // Construction Facility: All +#define MESSAGE_STRUCT_WINDTRAP 10 // Windtrap: The windtrap +#define MESSAGE_STRUCT_BARRACKS 11 // Barracks: The Barracks is +#define MESSAGE_STRUCT_STARPORT 12 // Startport: The Starport is +#define MESSAGE_STRUCT_REFINERY 13 // Spice Refinery: The +#define MESSAGE_STRUCT_REPAIR 14 // Repair Facility: The Repair +#define MESSAGE_STRUCT_WALL 15 // Wall: The wall is used for +#define MESSAGE_STRUCT_TURRET 16 // Gun Turret: The cannon +#define MESSAGE_STRUCT_RTURRET 17 // Rocket Turret: The +#define MESSAGE_STRUCT_SILO 18 // Spice Silo: The Spice silo +#define MESSAGE_STRUCT_OUTPOST 19 // Outpost: The Outpost +#define MESSAGE_NEED_CONCRETE 20 // There isn't enough open +#define MESSAGE_SAND 21 // Sand: This is sand terrain. +#define MESSAGE_DUNE 22 // Sand Dunes: These are an +#define MESSAGE_ROCK 23 // Rock: This is rock terrain. +#define MESSAGE_MOUNT 24 // Mountain: Mountains on +#define MESSAGE_SPICE 25 // Spice Field: This is the +#define MESSAGE_ADJACENT 26 // Structures must be placed +#define MESSAGE_SEARCH4SPICE 27 // Search for spice fields to +#define MESSAGE_SANDWORM 28 // Warning: Sandworms diff --git a/MESSAGE.TXT b/MESSAGE.TXT new file mode 100644 index 0000000..5fc1bb2 --- /dev/null +++ b/MESSAGE.TXT @@ -0,0 +1,57 @@ +# MESSAGE_NONE +You must build a Windtrap to provide power to your base. Without power, your structures will decay. +# MESSAGE_BUILD_WINDTRAP +Concrete: Use concrete to make a sturdy foundation for your structures. +# MESSAGE_STRUCT_CONCRETE +Palace: This is your Palace. +# MESSAGE_STRUCT_PALACE +Light Factory: The Light Factory produces light attack vehicles. +# MESSAGE_STRUCT_LIGHT +Heavy Factory: The Heavy Factory produces tracked vehicles. +# MESSAGE_STRUCT_HEAVY +Hi-Tech Factory: The Hi-Tech Factory produces flying vehicles. +# MESSAGE_STRUCT_HITECH +House IX: The IX Research Facility advances your House's technology. +# MESSAGE_STRUCT_IX +WOR: Wor is used to train your Heavy infantry. +# MESSAGE_STRUCT_WOR +Construction Facility: All structures are built by the construction facility. +# MESSAGE_STRUCT_CONST +Windtrap: The windtrap supplies power to your base. Without power your structures will decay. +# MESSAGE_STRUCT_WINDTRAP +Barracks: The Barracks is used to train your Light infantry. +# MESSAGE_STRUCT_BARRACKS +Startport: The Starport is used to order and receive shipments from C.H.O.A.M. +# MESSAGE_STRUCT_STARPORT +Spice Refinery: The Refinery converts spice into credits. +# MESSAGE_STRUCT_REFINERY +Repair Facility: The Repair Facility is used to repair your vehicles. +# MESSAGE_STRUCT_REPAIR +Wall: The wall is used for passive defense. +# MESSAGE_STRUCT_WALL +Gun Turret: The cannon turret is used for short range active defense. +# MESSAGE_STRUCT_TURRET +Rocket Turret: The rocket/cannon turret is used for both short and medium range active defense. +# MESSAGE_STRUCT_RTURRET +Spice Silo: The Spice silo is used to store refined spice. +# MESSAGE_STRUCT_SILO +Outpost: The Outpost provides radar and aids control of distant vehicles. +# MESSAGE_STRUCT_OUTPOST +There isn't enough open concrete to place this structure. You may proceed, but without enough concrete the building will need repairs. +# MESSAGE_NEED_CONCRETE +Sand: This is sand terrain. Plenty of this stuff on Arrakis, to be sure. +# MESSAGE_SAND +Sand Dunes: These are an ubiquitous feature of Arrakian landscape. +# MESSAGE_DUNE +Rock: This is rock terrain. This valuable terrain is the only place structures can be built. +# MESSAGE_ROCK +Mountain: Mountains on Arrakis are rare (and an inconvenience). +# MESSAGE_MOUNT +Spice Field: This is the Spice, Melange. It is the most precious substance in the universe. +# MESSAGE_SPICE +Structures must be placed on clear rock or concrete and adjacent to another friendly structure. +# MESSAGE_ADJACENT +Search for spice fields to harvest. +# MESSAGE_SEARCH4SPICE +Warning: Sandworms (Shai-Hulud) roam Dune devouring anything on the sand. +# MESSAGE_SANDWORM diff --git a/MISSION.CPP b/MISSION.CPP new file mode 100644 index 0000000..5b1ec6f --- /dev/null +++ b/MISSION.CPP @@ -0,0 +1,477 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\mission.cpv 2.18 16 Oct 1995 16:49:12 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MISSION.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : June 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MissionClass::AI -- Processes order script. * + * MissionClass::Assign_Mission -- Give an order to a unit. * + * MissionClass::Commence -- Start script with new order. * + * MissionClass::Debug_Dump -- Dumps status values to mono screen. * + * MissionClass::Get_Mission -- Fetches the mission that this object is acting under. * + * MissionClass::MissionClass -- Default constructor for the mission object type. * + * MissionClass::Mission_From_Name -- Fetch order pointer from its name. * + * MissionClass::Mission_Name -- Converts a mission number into an ASCII string. * + * MissionClass::Overide_Mission -- temporarily overides the units mission * + * MissionClass::Restore_Mission -- Restores overidden mission * + * MissionClass::Set_Mission -- Sets the mission to the specified value. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * MissionClass::MissionClass -- Default constructor for the mission object type. * + * * + * This is the default constructor for the mission class object. It sets the mission * + * handler into a default -- do nothing -- state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +MissionClass::MissionClass(void) +{ + Status = 0; + Timer = 0; + Mission = MISSION_NONE; + SuspendedMission = MISSION_NONE; + MissionQueue = MISSION_NONE; +} + + +int MissionClass::Mission_Sleep(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Ambush(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Attack(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Capture(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Guard(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Guard_Area(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Harvest(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Hunt(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Timed_Hunt(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Move(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Retreat(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Return(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Stop(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Unload(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Enter(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Construction(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Deconstruction(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Repair(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Missile(void) {return TICKS_PER_SECOND*30;}; + + +/*********************************************************************************************** + * MissionClass::Set_Mission -- Sets the mission to the specified value. * + * * + * Use this routine to set the current mission for this object. This routine will blast * + * over the current mission, bypassing the queue method. Call it when the mission needs * + * to be changed immediately. * + * * + * INPUT: mission -- The mission to set to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +void MissionClass::Set_Mission(MissionType mission) +{ + Mission = mission; + MissionQueue = MISSION_NONE; +} + + +/*********************************************************************************************** + * MissionClass::Get_Mission -- Fetches the mission that this object is acting under. * + * * + * Use this routine to fetch the mission that this object is CURRENTLY acting under. The * + * mission queue may be filled with a immanent mission change, but this routine does not * + * consider that. It only returns the CURRENT mission. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the mission that this unit is currently following. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +MissionType MissionClass::Get_Mission(void) const +{ + return(Mission == MISSION_NONE ? MissionQueue : Mission); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * MissionClass::Debug_Dump -- Dumps status values to mono screen. * + * * + * This is a debugging function that dumps this class' status to the monochrome screen * + * for review. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void MissionClass::Debug_Dump(MonoClass *mono) const +{ + mono->Set_Cursor(21, 1);mono->Printf("%5.5s[%4.4s]", MissionClass::Mission_Name(Mission), MissionClass::Mission_Name(MissionQueue)); +// mono->Text_Print(MissionClass::Mission_Name(Mission), 21, 1); + mono->Set_Cursor(20, 7);mono->Printf("%2d", (long)Timer); + mono->Set_Cursor(74, 1);mono->Printf("%2d", Status); + + ObjectClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * MissionClass::AI -- Processes order script. * + * * + * This routine will process the order script for as much time as * + * possible or until a script delay is detected. This routine should * + * be called for every unit once per game loop (if possible). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/23/1994 JLB : Created. * + * 06/25/1995 JLB : Added new missions. * + *=============================================================================================*/ +void MissionClass::AI(void) +{ + ObjectClass::AI(); + + /* + ** This is the script AI equivalent processing. + */ + if (Timer.Expired() && Strength > 0) { + switch (Mission) { + default: + case MISSION_STICKY: + case MISSION_SLEEP: + Timer = Mission_Sleep(); + break; + + case MISSION_GUARD: + Timer = Mission_Guard(); + break; + + case MISSION_ENTER: + Timer = Mission_Enter(); + break; + + case MISSION_CONSTRUCTION: + Timer = Mission_Construction(); + break; + + case MISSION_DECONSTRUCTION: + Timer = Mission_Deconstruction(); + break; + + case MISSION_CAPTURE: + case MISSION_SABOTAGE: + Timer = Mission_Capture(); + break; + + case MISSION_MOVE: + Timer = Mission_Move(); + break; + + case MISSION_ATTACK: + Timer = Mission_Attack(); + break; + + case MISSION_RETREAT: + Timer = Mission_Retreat(); + break; + + case MISSION_HARVEST: + Timer = Mission_Harvest(); + break; + + case MISSION_GUARD_AREA: + Timer = Mission_Guard_Area(); + break; + + case MISSION_RETURN: + Timer = Mission_Return(); + break; + + case MISSION_STOP: + Timer = Mission_Stop(); + break; + + case MISSION_AMBUSH: + Timer = Mission_Ambush(); + break; + + case MISSION_HUNT: + case MISSION_RESCUE: + Timer = Mission_Hunt(); + break; + + case MISSION_TIMED_HUNT: + Timer = Mission_Timed_Hunt(); + break; + + case MISSION_UNLOAD: + Timer = Mission_Unload(); + break; + + case MISSION_REPAIR: + Timer = Mission_Repair(); + break; + + case MISSION_MISSILE: + Timer = Mission_Missile(); + break; + } + } +} + + +/*********************************************************************************************** + * MissionClass::Commence -- Start script with new order. * + * * + * This routine will start script processing according to any queued * + * order it may have. If there is no queued order, then this routine * + * does nothing. Call this routine whenever the unit is in a good * + * position to change its order (such as when it is stopped). * + * * + * INPUT: none * + * * + * OUTPUT: Did the mission actually change? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/23/1994 JLB : Created. * + * 07/14/1994 JLB : Simplified. * + * 06/17/1995 JLB : Returns success flag. * + *=============================================================================================*/ +bool MissionClass::Commence(void) +{ + if (MissionQueue != MISSION_NONE) { + Mission = MissionQueue; + MissionQueue = MISSION_NONE; + + /* + ** Force immediate state machine processing at the first state machine state value. + */ + Timer = 0; + Status = 0; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * MissionClass::Assign_Mission -- Give an order to a unit. * + * * + * This assigns an order to a unit. It does NOT do the AI, but merely * + * flags the unit for normal AI processing. At that time the computer * + * will determine the unit's course of action. * + * * + * INPUT: order -- Mission to give the unit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1991 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void MissionClass::Assign_Mission(MissionType order) +{ + if (order == MISSION_NONE || Mission == order) return; + +// Status = 0; + MissionQueue = order; +} + + +/*********************************************************************************************** + * MissionClass::Mission_From_Name -- Fetch order pointer from its name. * + * * + * This routine is used to convert an ASCII order name into the actual * + * order number it represents. Typically, this is used when processing * + * a scenario INI file. * + * * + * INPUT: name -- The ASCII order name to process. * + * * + * OUTPUT: Returns with the actual order number that the ASCII name * + * represents. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 04/22/1994 JLB : Converted to static member function. * + *=============================================================================================*/ +MissionType MissionClass::Mission_From_Name(char const *name) +{ + MissionType order; + + if (name) { + for (order = MISSION_FIRST; order < MISSION_COUNT; order++) { + if (stricmp(Missions[order], name) == 0) { + return(order); + } + } + } + return(MISSION_NONE); +} + + +/*********************************************************************************************** + * MissionClass::Mission_Name -- Converts a mission number into an ASCII string. * + * * + * Use this routine to convert a mission number into the ASCII string that represents * + * it. Typical use of this is when generating an INI file. * + * * + * INPUT: mission -- The mission number to convert. * + * * + * OUTPUT: Returns with a pointer to the ASCII string that represents the mission type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +char const * MissionClass::Mission_Name(MissionType mission) +{ + return(mission == MISSION_NONE ? "None" : Missions[mission]); +} + + +/*********************************************************************************************** + * MissionClass::Override_Mission -- temporarily overides the units mission * + * * + * * + * * + * INPUT: MissionType mission - the mission we want to overide * + * TARGET tarcom - the new target we want to overide * + * TARGET navcom - the new navigation point to overide * + * * + * OUTPUT: none * + * * + * WARNINGS: If a mission is already overidden, the current mission is * + * just re-assigned. * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=============================================================================================*/ +void MissionClass::Override_Mission(MissionType mission, TARGET, TARGET) +{ + if (MissionQueue != MISSION_NONE) { + SuspendedMission = MissionQueue; + } else { + SuspendedMission = Mission; + } + + Assign_Mission(mission); +} + + +/*********************************************************************************************** + * MissionClass::Restore_Mission -- Restores overidden mission * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=============================================================================================*/ +bool MissionClass::Restore_Mission(void) +{ + if (SuspendedMission != MISSION_NONE) { + Assign_Mission(SuspendedMission); + SuspendedMission= MISSION_NONE; + return(true); + } + return(false); +} + + +/*********************************************************************************************** +** Unit order names. These names correspond to the player selectable orders +** a unit can have. The system initiated orders have no use for the ASCII name +** associated, but they are listed here for completeness sake. +*/ +char const * MissionClass::Missions[MISSION_COUNT] = { + "Sleep", + "Attack", + "Move", + "Retreat", + "Guard", + "Sticky", + "Enter", + "Capture", + "Harvest", + "Area Guard", + "Return", + "Stop", + "Ambush", + "Hunt", + "Timed Hunt", + "Unload", + "Sabotage", + "Construction", + "Selling", + "Repair", + "Rescue", + "Missile", +}; diff --git a/MISSION.H b/MISSION.H new file mode 100644 index 0000000..d19919d --- /dev/null +++ b/MISSION.H @@ -0,0 +1,136 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\mission.h_v 2.16 16 Oct 1995 16:45:46 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MISSION.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MISSION_H +#define MISSION_H + +#include "object.h" +#include "monoc.h" + +/**************************************************************************** +** This handles order assignment and tracking. The order is used to guide +** overall AI processing. +*/ +class MissionClass : public ObjectClass +{ + public: + + /* + ** This the tactical strategy to use. It is used by the unit script. This + ** is a general guide for unit AI processing. + */ + MissionType Mission; + MissionType SuspendedMission; + + /* + ** The order queue is used for orders that should take effect when the vehicle + ** has reached the center point of a cell. The queued order number is +1 when stored here + ** so that 0 will indicated there is no queued order. + */ + MissionType MissionQueue; + + char Status; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + MissionClass(void); + virtual ~MissionClass(void) {}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const; + #endif + + virtual MissionType Get_Mission(void) const; + virtual void Assign_Mission(MissionType mission); + virtual bool Commence(void); + virtual void AI(void); + + /* + ** Support functions. + */ + virtual int Mission_Sleep(void); + virtual int Mission_Ambush(void); + virtual int Mission_Attack(void); + virtual int Mission_Capture(void); + virtual int Mission_Guard(void); + virtual int Mission_Guard_Area(void); + virtual int Mission_Harvest(void); + virtual int Mission_Hunt(void); + virtual int Mission_Timed_Hunt(void); + virtual int Mission_Move(void); + virtual int Mission_Retreat(void); + virtual int Mission_Return(void); + virtual int Mission_Stop(void); + virtual int Mission_Unload(void); + virtual int Mission_Enter(void); + virtual int Mission_Construction(void); + virtual int Mission_Deconstruction(void); + virtual int Mission_Repair(void); + virtual int Mission_Missile(void); + virtual void Set_Mission(MissionType mission); + + static char const * Mission_Name(MissionType order); + static MissionType Mission_From_Name(char const *name); + virtual void Override_Mission(MissionType mission, TARGET, TARGET); + virtual bool Restore_Mission(void); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + private: + + /* + ** This the thread processing timer. When this value counts down to zero, then + ** more script processing may occur. + */ + TCountDownTimerClass Timer; + + /* + ** These are the order names as ASCII strings. + */ + static char const * Missions[MISSION_COUNT]; +}; + + +#endif diff --git a/MIXFILE.CPP b/MIXFILE.CPP new file mode 100644 index 0000000..5954d05 --- /dev/null +++ b/MIXFILE.CPP @@ -0,0 +1,516 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\mixfile.cpv 2.18 16 Oct 1995 16:48:46 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MIXFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : January 23, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MixFileClass::Cache -- Caches the named mixfile into RAM. * + * MixFileClass::Cache -- Loads this particular mixfile's data into RAM. * + * MixFileClass::Finder -- Finds the mixfile object that matches the name specified. * + * MixFileClass::Free -- Frees the allocated raw data block (not the index block). * + * MixFileClass::MixFileClass -- Constructor for mixfile object. * + * MixFileClass::Offset -- Determines if the file is in a mixfile and where its sublocation is.* + * MixFileClass::Offset -- Searches in mixfile for matching file and returns offset if found.* + * MixFileClass::Retrieve -- Retrieves a pointer to the specified data file. * + * MixFileClass::~MixFileClass -- Destructor for the mixfile object. * + * MixFileClass::Offset -- Determines the offset of the requested file from the mixfile system.* + * MixFileClass::Free -- Uncaches a cached mixfile. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include +#include +#include +#include +#include +#include +#include "mixfile.h" + + +template int Compare(T const *obj1, T const *obj2) { + if (*obj1 < *obj2) return(-1); + if (*obj1 > *obj2) return(1); + return(0); +}; + + +/* +** This is the pointer to the first mixfile in the list of mixfiles registered +** with the mixfile system. +*/ +MixFileClass * MixFileClass::First = 0; + + +/*********************************************************************************************** + * MixFileClass::Free -- Uncaches a cached mixfile. * + * * + * Use this routine to uncache a mixfile that has been cached. * + * * + * INPUT: filename -- Pointer to the filename of the mixfile that is to be uncached. * + * * + * OUTPUT: bool; Was the mixfile found and freed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +bool MixFileClass::Free(char const *filename) +{ + MixFileClass * ptr = Finder(filename); + + if (ptr) { + ptr->Free(); + return(true); + } + return(false); +} + + +#ifndef NOMEMCHECK +void MixFileClass::Free_All(void) +{ + while (First) { + delete First; + } +} +#endif + + +/*********************************************************************************************** + * MixFileClass::~MixFileClass -- Destructor for the mixfile object. * + * * + * This destructor will free all memory allocated by this mixfile and will remove it from * + * the system. A mixfile removed in this fashion must be created anew in order to be * + * subsequent used. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + * 01/06/1995 JLB : Puts mixfile header table into EMS. * + *=============================================================================================*/ +MixFileClass::~MixFileClass(void) +{ + + /* + ** Deallocate any allocated memory. + */ + if (Filename) { + free((char *)Filename); + } + if (Data) { + delete [] Data; + } + if (Buffer) { + delete [] Buffer; + } + + /* + ** Unlink this mixfile object from the chain. + */ + if (this == First) { + First = (MixFileClass *)Get_Next(); + } else { + Remove(); + } + Zap(); +} + + +/*********************************************************************************************** + * MixFileClass::MixFileClass -- Constructor for mixfile object. * + * * + * This is the constructor for the mixfile object. It takes a filename and a memory * + * handler object and registers the mixfile object with the system. The index block is * + * allocated and loaded from disk by this routine. * + * * + * INPUT: filename -- Pointer to the filename of the mixfile object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +MixFileClass::MixFileClass(char const *filename) +{ + CCFileClass file; // Working file object. + + /* + ** Load in the control block. It always remains resident. + */ + Data = 0; + Count = 0; + Buffer = 0; + file.Set_Name(filename); + Filename = strdup(file.File_Name()); + + if (!Force_CD_Available(RequiredCD)) { + Prog_End(); + exit(EXIT_FAILURE); + } + + if (file.Is_Available(true)) { + FileHeader fileheader; + + file.Open(); + file.Read(&fileheader, sizeof(fileheader)); + Count = fileheader.count; + DataSize = fileheader.size; + + /* + ** Load up the offset control array. This could be located in + ** EMS if possible. + */ + Buffer = new SubBlock [Count]; + if (Buffer) { + file.Read(Buffer, Count * sizeof(SubBlock)); + } + file.Close(); + } else { +// delete this; + return; + } + + /* + ** Raw data block starts uncached. + */ + Data = 0; + + /* + ** Attach to list of mixfiles. + */ + Zap(); + if (!First) { + First = this; + } else { + Add_Tail(*First); + } +} + + +/*********************************************************************************************** + * MixFileClass::Retrieve -- Retrieves a pointer to the specified data file. * + * * + * This routine will return with a pointer to the specified data file if the file resides * + * in memory. Otherwise, this routine returns NULL. Use this routine to access a resident * + * file directly rather than going through the process of pseudo disk access. This will * + * save both time and RAM. * + * * + * INPUT: filename -- Pointer to the filename of the data file to retrieve a pointer to. * + * * + * OUTPUT: Returns with a pointer to the data file's data. If the file is not in RAM, then * + * NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1994 JLB : Created. * + *=============================================================================================*/ +void const * MixFileClass::Retrieve(char const *filename) { + void *ptr = 0; + Offset(filename, &ptr); +// if (!ptr) { +// errno = ENOENT; +// File_Fatal(filename); +// } + return(ptr); +}; + + +/*********************************************************************************************** + * MixFileClass::Finder -- Finds the mixfile object that matches the name specified. * + * * + * This routine will scan through all registered mixfiles and return with a pointer to * + * the matching mixfile. If no mixfile could be found that matches the name specified, * + * then NULL is returned. * + * * + * INPUT: filename -- Pointer to the filename to search for. * + * * + * OUTPUT: Returns with a pointer to the matching mixfile -- if found. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +MixFileClass * MixFileClass::Finder(char const *filename) +{ + MixFileClass * ptr; + + ptr = First; + while (ptr) { + if (stricmp(&ptr->Filename[strlen(ptr->Filename)-strlen(filename)], filename) == 0) { + return(ptr); + } + ptr = (MixFileClass *)ptr->Get_Next(); + } + return(0); +} + + +/*********************************************************************************************** + * MixFileClass::Cache -- Caches the named mixfile into RAM. * + * * + * This routine will cache the mixfile, specified by name, into RAM. * + * * + * INPUT: filename -- The name of the mixfile that should be cached. * + * * + * OUTPUT: bool; Was the cache successful? * + * * + * WARNINGS: This routine could go to disk for a very long time. * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +bool MixFileClass::Cache(char const *filename) +{ + MixFileClass * mixer = Finder(filename); + + if (mixer) { + return(mixer->Cache()); + } + return(false); +} + + +/*********************************************************************************************** + * MixFileClass::Cache -- Loads this particular mixfile's data into RAM. * + * * + * This load the mixfile data into ram for this mixfile object. This is the counterpart * + * to the Free() function. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the file load successful? It could fail if there wasn't enough room * + * to allocate the raw data block. * + * * + * WARNINGS: This routine goes to disk for a potentially very long time. * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +bool MixFileClass::Cache(void) +{ + if (Data) return(true); + + Data = new char [DataSize]; + if (Data) { + CCFileClass file(Filename); + + file.Open(); + file.Seek(sizeof(SubBlock) * Count + sizeof(FileHeader)); + long actual = file.Read(Data, DataSize); + if (actual != DataSize) { +#ifdef GERMAN + Fatal("Korrupte .MIX-Datei \"%s\". Beim Versuch, %ld zu lesen, nur %ld gefunden.", Filename, DataSize, actual); +#else +#ifdef FRENCH + Fatal("Fichier .MIX corrumpu \"%s\". Essai de lecture de %ld, mais %ld obtenu.", Filename, DataSize, actual); +#else + Fatal("Corrupt .MIX file \"%s\". Tried to read %ld, but got %ld.", Filename, DataSize, actual); +#endif +#endif + } + file.Close(); + return(true); + } +#ifdef GERMAN + Fatal("Kann Datei \"%s\" nicht laden.", Filename); +#else +#ifdef FRENCH + Fatal("Impossible de charger \"%s\".", Filename); +#else + Fatal("Unable to load \"%s\".", Filename); +#endif +#endif + return(false); +} + + +/*********************************************************************************************** + * MixFileClass::Free -- Frees the allocated raw data block (not the index block). * + * * + * This routine will free the (presumably large) raw data block, but leave the index * + * block intact. By using this in conjunction with the Cache() function, one can maintain * + * tight control of memory usage. If the index block is desired to be freed, then the * + * mixfile object must be deleted. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +void MixFileClass::Free(void) +{ + if (Data) { + delete [] Data; + Data = 0; + } +} + + +/*********************************************************************************************** + * MixFileClass::Offset -- Determines if the file is in a mixfile and where its sublocation is.* + * * + * This routine will scan through all registers mixfiles in an attempt to locate the file * + * in question. Whether the file is located in RAM or on disk does not restrict this * + * search operation. * + * * + * INPUT: filename -- The filename of the file to search within the mixfile system. * + * * + * realptr -- Pointer to pointer that will be filled with the RAM pointer to * + * the file if it happens to be in RAM. NULL if otherwise. * + * * + * mixfile -- Pointer to the mixfile object that contains the file in question. * + * * + * offset -- The offset to the start of the data of the file. If the file is * + * on disk then this is the offset from the start of the file, if * + * it is located in RAM, then it is the offset from the start of the * + * raw data block. * + * * + * size -- Pointer to where the size of the file will be stored. * + * * + * OUTPUT: bool; Was the file found in one of the mixfiles? The value stored in "realptr" * + * will be NULL if it is on disk, otherwise it is in RAM. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +//int _USERENTRY Compare(MixFileClass::SubBlock const *, MixFileClass::SubBlock const *); + +// int _USERENTRY compfunc(void const *ptr1, void const *ptr2) +int compfunc(void const *ptr1, void const *ptr2) +{ +// long diff = *(long const *)ptr1 - *(long const *)ptr2; +// return FP_SEG(diff); + + if (*(long const *)ptr1 < *(long const *)ptr2) return(-1); + if (*(long const *)ptr1 > *(long const *)ptr2) return(1); + return(0); +} + + +/*********************************************************************************************** + * MixFileClass::Offset -- Determines the offset of the requested file from the mixfile system.* + * * + * This routine will take the falename specified and search through the mixfile system * + * looking for it. If the file was found, then the mixfile it was found int, the offset * + * from the start of the mixfile, and the size of the embedded file will be returned. * + * Using this method it is possible for the CCFileClass system to process it as a normal * + * file. * + * * + * INPUT: filename -- The filename to search for. * + * * + * realptr -- Stores a pointer to the start of the file in memory here. If the * + * file is not in memory, then NULL is stored here. * + * * + * mixfile -- The pointer to the corresponding mixfile is placed here. If no * + * mixfile was found that contains the file, then NULL is stored here. * + * * + * offset -- The starting offset from the beginning of the parent mixfile is * + * stored here. * + * * + * size -- The size of the embedded file is stored here. * + * * + * OUTPUT: bool; Was the file found? The file may or may not be resident, but it does exist * + * and can be opened. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +bool MixFileClass::Offset(char const *filename, void ** realptr, MixFileClass ** mixfile, long * offset, long * size) +{ + MixFileClass * ptr; + + if (!filename) return(false); + + /* + ** Create the key block that will be used to binary search for the file. + */ + long crc = Calculate_CRC(strupr((char *)filename), strlen(filename)); + SubBlock key; + key.CRC = crc; + + /* + ** Sweep through all registered mixfiles, trying to find the file in question. + */ + ptr = First; + while (ptr) { + SubBlock * block; + + /* + ** Binary search for the file in this mixfile. If it is found, then extract the + ** appropriate information and store it in the locations provided and then return. + */ + block = (SubBlock *)bsearch(&key, ptr->Buffer, ptr->Count, sizeof(SubBlock), compfunc); + if (block) { + if (mixfile) *mixfile = ptr; + if (size) *size = block->Size; + if (realptr) *realptr = 0; + if (offset) *offset = block->Offset; + if (realptr && ptr->Data) { + *realptr = Add_Long_To_Pointer(ptr->Data, block->Offset); + } + if (!ptr->Data && offset) { + *offset += sizeof(SubBlock) * ptr->Count + sizeof(FileHeader); + } + return(true); + } + + /* + ** Advance to next mixfile. + */ + ptr = (MixFileClass *)ptr->Get_Next(); + } + + /* + ** All the mixfiles have been examined but no match was found. Return with the non success flag. + */ + return(false); +} + diff --git a/MIXFILE.H b/MIXFILE.H new file mode 100644 index 0000000..148314b --- /dev/null +++ b/MIXFILE.H @@ -0,0 +1,87 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\mixfile.h_v 2.18 16 Oct 1995 16:47:22 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MIXFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 18, 1994 * + * * + * Last Update : October 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MIXFILE_H +#define MIXFILE_H + +#include +#include "link.h" + +class MixFileClass : public LinkClass +{ + public: + char const *Filename; // Filename of mixfile. + + MixFileClass(char const *filename); + ~MixFileClass(void); + + static bool Free(char const *filename); + static void Free_All(void); + void Free(void); + bool Cache(void); + static bool Cache(char const *filename); + static bool Offset(char const *filename, void ** realptr = 0, MixFileClass ** mixfile = 0, long * offset = 0, long * size = 0); + static void const * Retrieve(char const *filename); + + struct SubBlock { + long CRC; // CRC code for embedded file. + long Offset; // Offset from start of data section. + long Size; // Size of data subfile. + + int operator < (SubBlock & two) const {return (CRC < two.CRC);}; + int operator > (SubBlock & two) const {return (CRC > two.CRC);}; + int operator == (SubBlock & two) const {return (CRC == two.CRC);}; + }; + + private: + static MixFileClass * Finder(char const *filename); + long Offset(long crc, long *size = 0); + + typedef struct { + short count; + long size; + } FileHeader; + + int Count; // Number of sub-blocks. + long DataSize; // Size of raw data. + SubBlock * Buffer; // Array of sub blocks (could be in EMS). + void *Data; // Pointer to raw data. + + static MixFileClass * First; +}; + +#endif diff --git a/MMX.ASM b/MMX.ASM new file mode 100644 index 0000000..d4fa7ff --- /dev/null +++ b/MMX.ASM @@ -0,0 +1,325 @@ +; +; Command & Conquer(tm) +; Copyright 2025 Electronic Arts Inc. +; +; 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 3 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 General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . +; + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S I N C ** +;*************************************************************************** +;* * +;* Project Name : Command & Conquer * +;* * +;* File Name : MMX.ASM * +;* * +;* Programmer : Steve Tall * +;* * +;* Start Date : May 19th, 1996 * +;* * +;* Last Update : May 19th 1996 [ST] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + + include + + + .model flat + .586 + + + externdef C Detect_MMX_Availability:near + externdef C Single_Line_Trans_Entry:near + externdef C Next_Line:near + externdef C Init_MMX:near + externdef C MMX_Done:near + + externdef EndNewShapeJumpTable:byte + externdef NewShapeJumpTable:dword + externdef Single_Line_Trans:near + externdef MMX_Single_Line_Trans:near + + + .code + +externdef C CPUType:byte + + + +;********************************************************************************************* +;* Detect_MMX_Availability -- Detect the presence of MMX technology. * +;* * +;* * +;* INPUT: Nothing * +;* * +;* OUTPUT: True if MMX technology is available. * +;* * +;* Warnings: * +;* * +;* Note: Based in part on CPUID32.ASM by Intel * +;* * +;* HISTORY: * +;* 05/19/96 ST : Created. * +;*===========================================================================================* + +Detect_MMX_Availability proc C + + local idflag:byte + local cputype:byte + +;assume processor is at least 386 +; +;check whether AC bit in eflags can be toggled. +;If not then processor is 386 + + mov [idflag],0 + + pushfd ;get Eflags in EAX + pop eax + mov ecx,eax ;save eflags + xor eax,40000h ;toggle AC bit in eflags + push eax ;new eflags on stack + popfd ;move new value into eflags + pushfd ;get new eflags back into eax + pop eax + xor eax,ecx ;if AC bit not toggled then CPU=386 + mov [cputype],3 + jz @@end_get_cpu ;cpu is 386 + + push ecx + popfd ;restore AC bit in eflags + + +;processor is at least 486 +; +;Check for ability to set/clear ID flag in EFLAGS +;ID flag indicates ability of processor to execute the CPUID instruction. +;486 not guaranteed to have CPUID inst? +; + mov [cputype],4 + mov eax,ecx ;original EFLAGS + xor eax,200000h ;toggle ID bit + push eax + popfd + pushfd + pop eax + xor eax,ecx ;check if still toggled + jz @@end_get_cpu + + +; Execute CPUID instruction to determine vendor, family, +; model and stepping. +; + + mov [idflag],1 ;flag ID is available + + xor eax,eax + cpuid + + mov dword ptr [VendorID],ebx + mov dword ptr [VendorID+4],edx + mov dword ptr [VendorID+8],ecx + mov dword ptr [VendorID+12]," " + + cmp eax,1 ;check if 1 is valid + jl @@end_get_cpu ;inp for cpuid inst. + + xor eax,eax + inc eax + + cpuid ;get stepping, model and family + + and ax,0f00H + shr ax,08H + + mov [cputype],al + +@@end_get_cpu: mov al,[cputype] + mov [CPUType],al + + +; +; We have the CPU type in al now. +; If we arent on at least a pentium then we can assume there is no MMX +; + cmp al,5 + jl @@no_mmx + + mov eax,1 + cpuid + test edx,00800000h + jz @@no_mmx + +; +; MMX detected - return true +; + mov eax,1 + ret + + +@@no_mmx: xor eax,eax + ret + + +Detect_MMX_Availability endp + + + +;********************************************************************************************* +;* Init_MMX -- Do any special inits required for MMX support * +;* * +;* * +;* INPUT: Nothing * +;* * +;* OUTPUT: None * +;* * +;* Warnings: * +;* * +;* HISTORY: * +;* 05/19/96 ST : Created. * +;*===========================================================================================* + +Init_MMX proc C + + mov edi,offset NewShapeJumpTable + mov ecx,offset EndNewShapeJumpTable + sub ecx,edi + shr ecx,2 + mov eax,offset Single_Line_Trans + mov ebx,offset MMX_Single_Line_Trans + cld + + +@@patch_loop: repnz scasd + jnz @@done + mov [edi-4],ebx + test ecx,ecx + jnz @@patch_loop + +@@done: ret + +Init_MMX endp + + + + + + +;********************************************************************************************* +;* MMX_Done -- Restores floating point capability after MMX usage * +;* * +;* * +;* INPUT: Nothing * +;* * +;* OUTPUT: None * +;* * +;* Warnings: * +;* * +;* HISTORY: * +;* 05/19/96 ST : Created. * +;*===========================================================================================* + +MMX_Done proc C + + emms + ret + +MMX_Done endp + + + + + + + + code segment page public use32 'code' ; Need stricter segment alignment + ; for pentium optimisations + + +;********************************************************************************************* +;* MMX_Single_Line_Trans -- draw a single line of transparent pixels using MMX technology * +;* * +;* * +;* INPUT: Esi - ptr to source data * +;* Edi - ptr to destination data * +;* Ecx - width to draw in bytes * +;* * +;* OUTPUT: None * +;* * +;* Warnings: * +;* * +;* HISTORY: * +;* 05/19/96 ST : Created. * +;*===========================================================================================* + + align 16 + +MMX_Single_Line_Trans proc near + +; +; If we are doing less than 8 bytes then dont use MMX +; + cmp ecx,8 + jge @@mmx_loop + push offset Single_Line_Trans_Entry + ret + +; +; Use MMX instructions to mask 8 bytes at once +; +; Creates a bitmask based on the source bytes equality with zero and then uses this to mask +; out the source bytes in the destination data. The advatage that MMX gives us is that there is +; no 'test for zero then jump' required to mask. +; + align 64 ;MMX instructions like 64 byte alignment! + +@@mmx_loop: + movq mm0,[esi] ; move 8 bytes of source into mm0 + pxor mm1,mm1 ; zero out mm1 + pcmpeqb mm1,mm0 ; compare mm0 with 0. Bits get set in mm1 + lea esi,[esi+8] ; adjust the source data pointer + pand mm1,[edi] ; and in the destination data to throw away the bytes which arent zero in the source + sub ecx,8 ; adjust the byte counter + por mm1,mm0 ; or in the source with the destination data + movq [edi],mm1 ; write back the destination data + lea edi,[edi+8] ; adjust the destination pointer + + cmp ecx,8 + jg @@mmx_loop + +; +; Jump to the approprite code for drawing the end of this line or going to the next one +; + push offset Next_Line + jcxz @@next_line + push offset Single_Line_Trans_Entry +@@next_line: ret + + +MMX_Single_Line_Trans endp + + +code ends + + .data + +CPUType db 0 +VendorID db "Not available",0,0,0,0,0,0 + + +end + diff --git a/MONOC.CPP b/MONOC.CPP new file mode 100644 index 0000000..533f984 --- /dev/null +++ b/MONOC.CPP @@ -0,0 +1,807 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\monoc.cpv 2.13 16 Oct 1995 16:50:36 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MONO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : October 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MonoClass::Clear -- Clears the monochrome screen object. * + * MonoClass::Draw_Box -- Draws a box using the IBM linedraw characters. * + * MonoClass::MonoClass -- The default constructor for monochrome screen object. * + * MonoClass::operator = -- Handles making one mono object have the same imagery as another. * + * MonoClass::Print -- Prints the text string at the current cursor coordinates. * + * MonoClass::Printf -- Prints a formatted string to the monochrome screen. * + * MonoClass::Scroll -- Scroll the monochrome screen up by the specified lines. * + * MonoClass::Set_Cursor -- Sets the monochrome cursor to the coordinates specified. * + * MonoClass::Text_Print -- Prints text to the monochrome object at coordinates indicated. * + * MonoClass::View -- Brings the mono object to the main display. * + * MonoClass::~MonoClass -- The default destructor for a monochrome screen object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//#pragma inline +#include "function.h" +#include "monoc.h" + +#include +#include +#include +#include +#include +#include + + +//extern void output(short port, short data); +//#pragma aux output parm [dx] [ax] = \ +// "out dx,al" \ +// "inc dx" \ +// "mov al,ah" \ +// "out dx,al" + + + +int MonoClass::Enabled = 0; +MonoClass * MonoClass::PageUsage[MonoClass::MAX_MONO_PAGES] = {0,0,0,0,0,0,0,0}; +//DOSSegmentClass MonoClass::MonoSegment(MonoClass::SEGMENT); +void * MonoClass::MonoSegment = (void*)0x000b0000; + +/* +** These are the IBM linedraw characters. +*/ +MonoClass::BoxDataType const MonoClass::CharData[MonoClass::COUNT] = { + {0xDA,0xC4,0xBF,0xB3,0xD9,0xC4,0xC0,0xB3}, // Single line + {0xD5,0xCD,0xB8,0xB3,0xBE,0xCD,0xD4,0xB3}, // Double horz. + {0xD6,0xC4,0xB7,0xBA,0xBD,0xC4,0xD3,0xBA}, // Double vert. + {0xC9,0xCD,0xBB,0xBA,0xBC,0xCD,0xC8,0xBA} // Double horz and vert. +}; + + +/*********************************************************************************************** + * MonoClass::MonoClass -- The default constructor for monochrome screen object. * + * * + * This is the constructor for monochrome screen objects. It handles allocating a free * + * monochrome page. If there are no more pages available, then this is a big error. The * + * page allocated may not be the visible one. Call the View function in order to bring * + * it to the displayed page. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +MonoClass::MonoClass(void) +{ + int index; + + Attrib = DEFAULT_ATTRIBUTE; // Normal text color. + X = Y = 0; + for (index = 0; index < MAX_MONO_PAGES; index++) { + if (!PageUsage[index]) { + PageUsage[index] = this; + Page = (char)index; + break; + } + } + if (index == MAX_MONO_PAGES) { + // Major error message should pop up here! + delete this; + } +} + + +/*********************************************************************************************** + * MonoClass::~MonoClass -- The default destructor for a monochrome screen object. * + * * + * This is the default destructor for a monochrome screen object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +MonoClass::~MonoClass(void) +{ + PageUsage[Page] = 0; +} + + +/*********************************************************************************************** + * MonoClass::Draw_Box -- Draws a box using the IBM linedraw characters. * + * * + * Use this routine to draw a box to the monochrome screen. The IBM line draw characters * + * are used to give the it a fancy appearance. There are several line draw modes supported. * + * * + * INPUT: x,y -- The coordinates of the upper left corner of the box. * + * * + * w,y -- The width and height (respectively) to make the box. * + * * + * attrib -- The text attribute to use when drawing the box outline characters. * + * * + * thick -- The thickness style to use. Examine the BoxStyleType enum for * + * elaboration on the supported styles. * + * * + * OUTPUT: none * + * * + * WARNINGS: The interior of the box is NOT cleared by this routine. It is advised that this * + * area be cleared before the box is drawn. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Draw_Box(int x, int y, int w, int h, char attrib, BoxStyleType thick) +{ + CellType cell; + char oldattrib = Attrib; + + if (!Enabled || !w || !h) return; + + cell.Attribute = attrib; + + /* + ** Draw the horizontal lines. + */ + for (int xpos = 0; xpos < w-2; xpos++) { + cell.Character = CharData[thick].TopEdge; + Store_Cell(cell, x+xpos+1, y); +// MonoSegment.Copy_Word_To(*(short*)&cell, Offset(x+xpos+1, y)); + cell.Character = CharData[thick].BottomEdge; + Store_Cell(cell, x+xpos+1, y+h-1); +// MonoSegment.Copy_Word_To(*(short*)&cell, Offset(x+xpos+1, y+h-1)); + } + + /* + ** Draw the vertical lines. + */ + for (int ypos = 0; ypos < h-2; ypos++) { + cell.Character = CharData[thick].LeftEdge; + Store_Cell(cell, x, y+ypos+1); +// MonoSegment.Copy_Word_To(*(short*)&cell, Offset(x, y+ypos+1)); + cell.Character = CharData[thick].RightEdge; + Store_Cell(cell, x+w-1, y+ypos+1); +// MonoSegment.Copy_Word_To(*(short*)&cell, Offset(x+w-1, y+ypos+1)); + } + + /* + ** Draw the four corners. + */ + if (w > 1 && h > 1) { + cell.Character = CharData[thick].UpperLeft; + Store_Cell(cell, x, y); +// MonoSegment.Copy_Word_To(*(short*)&cell, Offset(x, y)); + cell.Character = CharData[thick].UpperRight; + Store_Cell(cell, x+w-1, y); +// MonoSegment.Copy_Word_To(*(short*)&cell, Offset(x+w-1, y)); + cell.Character = CharData[thick].BottomRight; + Store_Cell(cell, x+w-1, y+h-1); +// MonoSegment.Copy_Word_To(*(short*)&cell, Offset(x+w-1, y+h-1)); + cell.Character = CharData[thick].BottomLeft; + Store_Cell(cell, x, y+h-1); +// MonoSegment.Copy_Word_To(*(short*)&cell, Offset(x, y+h-1)); + } + + Attrib = oldattrib; +} + + +/*********************************************************************************************** + * MonoClass::Set_Cursor -- Sets the monochrome cursor to the coordinates specified. * + * * + * Use this routine to set the monochrome's cursor position to the coordinates specified. * + * This is the normal way of controlling where the next Print or Printf will output the * + * text to. * + * * + * INPUT: x,y -- The coordinate to position the monochrome cursor. 0,0 is the upper left * + * corner. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Set_Cursor(int x, int y) +{ + #ifdef FIX_ME_LATER + int pos = (y*COLUMNS)+x; + + if (!Enabled) return; + + X = (char)(x%COLUMNS); + Y = (char)(y%LINES); + + if (Page == 0) { + _DX = CONTROL_PORT; + _AX = (short)(0x0E|(pos&0xFF00)); + asm { + out dx,al + inc dx + mov al,ah + out dx,al + } + + _DX = CONTROL_PORT; + _AX = (short)(0x0F|(pos<<8)); + asm { + out dx,al + inc dx + mov al,ah + out dx,al + } + +// outportb(CONTROL_PORT,0x0E|(pos&0xFF00)); +// outportb(CONTROL_PORT,0x0F|(pos<<8)); + } + + #endif //FIX_ME_LATER + +x=y; +y=x; +} + + +/*********************************************************************************************** + * MonoClass::Clear -- Clears the monochrome screen object. * + * * + * This routine will fill the monochrome screen object with spaces. It is clearing the * + * screen of data, making it free for output. The cursor is positioned at the upper left * + * corner of the screen by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Clear(void) +{ + CellType cell; +// int offset; + + if (!Enabled) return; + + Set_Cursor(0, 0); + + cell.Attribute = Attrib; + cell.Character = ' '; + +// offset = Offset(0, 0); + for (int y = 0; y < LINES; y++) { + for (int x = 0; x < COLUMNS; x++) { + Store_Cell(cell, x, y); + } + } +} + + +/*********************************************************************************************** + * MonoClass::Scroll -- Scroll the monochrome screen up by the specified lines. * + * * + * Use this routine to scroll the monochrome screen up by the number of lines specified. * + * This routine is typically called by the printing functions so that the monochrome screen * + * behaves in the expected manner -- printing at the bottom of the screen scrolls it up * + * to make room for new text. * + * * + * INPUT: lines -- The number of lines to scroll the monochrome screen. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Scroll(int lines) +{ + CellType cell; + + if (!Enabled || lines <= 0) return; + + memmove( (void*)((long)MonoSegment + Offset(0, 0)), + (void*)((long)MonoSegment + Offset(0, lines)), + (LINES-lines)*COLUMNS*sizeof(CellType)); + +// DOSSegmentClass::Copy(MonoSegment, Offset(0, lines), MonoSegment, Offset(0, 0), (LINES-lines)*COLUMNS*sizeof(CellType)); + + Y--; + cell.Attribute = Attrib; + cell.Character = ' '; + + for (int l = LINES-lines; l < LINES; l++) { + for (int index = 0; index < COLUMNS; index++) { + Store_Cell(cell, index, l); +// MonoSegment.Copy_Word_To(*(short*)&cell, Offset(index, l)); + } + } +} + + +/*********************************************************************************************** + * MonoClass::Printf -- Prints a formatted string to the monochrome screen. * + * * + * Use this routine to output a formatted string, using the standard formatting options, * + * to the monochrome screen object's current cursor position. * + * * + * INPUT: text -- Pointer to the text to print. * + * * + * ... -- Any optional parameters to supply in formatting the text. * + * * + * OUTPUT: none * + * * + * WARNINGS: The total formatted text length must not exceed 255 characters. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Printf(char const *text, ...) +{ + va_list va; + /* + ** The buffer object is placed at the end of the local variable list + ** so that if the sprintf happens to spill past the end, it isn't likely + ** to trash anything (important). The buffer is then manually truncated + ** to maximum allowed size before being printed. + */ + char buffer[256]; + + if (!Enabled) return; + + va_start(va, text); + vsprintf(buffer, text, va); + buffer[sizeof(buffer)-1] = '\0'; + + Print(buffer); + va_end(va); +} + + +#ifdef NEVER +void MonoClass::Printf(int text, ...) +{ + va_list va; + /* + ** The buffer object is placed at the end of the local variable list + ** so that if the sprintf happens to spill past the end, it isn't likely + ** to trash anything (important). The buffer is then manually truncated + ** to maximum allowed size before being printed. + */ + char buffer[256]; + + if (!Enabled) return; + + va_start(va, text); + vsprintf(buffer, Text_String(text), va); + buffer[sizeof(buffer)-1] = '\0'; + + Print(buffer); + va_end(va); +} +#endif + + +/*********************************************************************************************** + * MonoClass::Print -- Prints the text string at the current cursor coordinates. * + * * + * Use this routine to output the specified text string at the monochrome object's current * + * text coordinate position. * + * * + * INPUT: ptr -- Pointer to the string to print. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Print(char const *ptr) +{ +// int optr; + char startcol = X; + char const * text; + CellType cell; + + if (!ptr || !Enabled) return; + + text = ptr; + cell.Attribute = Attrib; +// optr = Offset(X, Y); + while (*text) { + + /* + ** Sometimes the character string is used for cursor control instead + ** of plain text output. Check for this case. + */ + switch (*text) { + + /* + ** The "return" code behaves as it did in the old C library + ** mono system. That is, it returns the cursor position to + ** the next line but at the starting column of the print. + */ + case '\r': + X = startcol; + Y++; + Scroll(Y-(LINES-1)); +// optr = Offset(X, Y); + break; + + /* + ** The "newline" code behaves like the console newline character. + ** That is, it moves the cursor down one line and at the first + ** column. + */ + case '\n': + X = 0; + Y++; + Scroll(Y-(LINES-1)); +// optr = Offset(X, Y); + break; + + /* + ** All other characters are output directly and the cursor moves + ** rightward to match. If the cursor wraps past the right + ** edge is it moved to the next now down at left margin. If the + ** cursor goes off the bottom of the display, the display is scrolled + ** upward a line. + */ + default: + cell.Character = *text; + Store_Cell(cell, X, Y); +// MonoSegment.Copy_Word_To(*(short*)&cell, optr); +// optr += sizeof(CellType); + + X++; + if (X >= COLUMNS) { + X = 0; + Y++; + + if (Y > (LINES-1)) { + Scroll(Y-(LINES-1)); +// optr = Offset(X, Y); + } + } + break; + + } + text++; + } + + Set_Cursor(X, Y); +} + + +/*********************************************************************************************** + * MonoClass::Text_Print -- Prints text to the monochrome object at coordinates indicated. * + * * + * Use this routine to output text to the monochrome object at the X and Y coordinates * + * specified. * + * * + * INPUT: text -- Pointer to the text string to display. * + * * + * x,y -- The X and Y character coordinates to start the printing at. * + * * + * attrib-- Optional parameter that specifies what text attribute code to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Text_Print(char const *text, int x, int y, char attrib) +{ + char oldx = X; + char oldy = Y; + char oldattrib = Attrib; + + X = (char)x; + Y = (char)y; + Attrib = attrib; + Print(text); + Attrib = oldattrib; + Set_Cursor(oldx, oldy); +} + +#ifdef NEVER +void MonoClass::Text_Print(int text, int x, int y, char attrib) +{ + char oldx = X; + char oldy = Y; + char oldattrib = Attrib; + + if (text != TXT_NONE) { + X = (char)x; + Y = (char)y; + Attrib = attrib; + Print(Text_String(text)); + Attrib = oldattrib; + Set_Cursor(oldx, oldy); + } +} + +void MonoClass::Print(int text) +{ + Print(Text_String(text)); +} +#endif + + +/*********************************************************************************************** + * MonoClass::operator = -- Handles making one mono object have the same imagery as another. * + * * + * The assignment operator will handle copying the imagery from one monochrome object to * + * another. Use this routine in to make two monochrome class objects visually identical. * + * * + * INPUT: src -- A reference to the source (right side) monochrome object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +MonoClass & MonoClass::operator = (MonoClass const & src) +{ + memcpy((void*)((long)MonoSegment + src.Offset(0, 0)), (void*)((long)MonoSegment + Offset(0, 0)), SIZE_OF_PAGE); +// DOSSegmentClass::Copy(MonoSegment, src.Offset(0, 0), MonoSegment, Offset(0,0), SIZE_OF_PAGE); + Set_Cursor(src.X, src.Y); + return(*this); +} + + +/*********************************************************************************************** + * MonoClass::View -- Brings the mono object to the main display. * + * * + * Use this routine to display the mono object on the monochrome screen. It is possible * + * that the mono object exists on some background screen memory. Calling this routine will * + * perform the necessary memory swapping to bring the data to the front. The mono object * + * that was currently being viewed is not destroyed by this function. It is merely moved * + * off to some background page. It can be treated normally, except that is just isn't * + * visible. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::View(void) +{ + MonoClass *displace; // The page that is being displaced. + + if (Get_Current() == this) return; + + /* + ** If the visible page is already assigned to a real monochrome page + ** object, then it must be swapped with the new one. + */ + displace = Get_Current(); + if (displace) { + char temp[SIZE_OF_PAGE]; + + memcpy(&temp[0], MonoSegment, SIZE_OF_PAGE); + memcpy(MonoSegment, (void*)((long)MonoSegment + Offset(0, 0)), SIZE_OF_PAGE); + memcpy((void*)((long)MonoSegment + Offset(0, 0)), &temp[0], SIZE_OF_PAGE); + +// DOSSegmentClass::Swap(MonoSegment, Offset(0, 0), MonoSegment, 0, SIZE_OF_PAGE); + displace->Page = Page; + + } else { + + /* + ** Just copy the new page over since the display page is not assigned + ** to a real monochrome page object. + */ + memcpy(MonoSegment, (void*)((long)MonoSegment + Offset(0, 0)), SIZE_OF_PAGE); +// DOSSegmentClass::Copy(MonoSegment, Offset(0, 0), MonoSegment, 0, SIZE_OF_PAGE); + } + PageUsage[Page] = displace; + PageUsage[0] = this; + Page = 0; + + Set_Cursor(X, Y); +} + + + +/************************************************************************************ +** This is the set of C wrapper functions that access the MonoClass support routines. +** Since the C interface doesn't have the ability to write to non-visible pages, it +** will just blast the output to whichever mono page is currently visible. If there is +** no mono class object that is visible, then one will be created -- BUT NOT FREED. +** Typically, this is ok, since the C interface will create only one MonoClass object +** and the system supports up to 8. +*/ +void Mono_Set_Cursor(int x, int y) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Set_Cursor(x, y); + } +} + +int Mono_Printf(char const *string, ...) +{ + va_list va; + char buffer[256]; + + buffer[0] = '\0'; + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + + va_start(va, string); + vsprintf(buffer, string, va); + + mono->Print(buffer); + + va_end(va); + } + return((short)strlen(buffer)); +} + + +void Mono_Clear_Screen(void) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Clear(); + } +} + +void Mono_Text_Print(void const *text, int x, int y, int attrib) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Text_Print((const char*)text, x, y, (char)attrib); + } +} + +void Mono_Draw_Rect(int x, int y, int w, int h, int attrib, int thick) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Draw_Box(x, y, w, h, (char)attrib, (MonoClass::BoxStyleType)thick); + } +} + +void Mono_Print(void const *text) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Print((const char*)text); + } +} + +int Mono_X(void) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + return(short)mono->Get_X(); + } + return(0); +} + +int Mono_Y(void) +{ + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + return(short)mono->Get_X(); + } + return(0); +} + + +void Mono_Put_Char(char , int ) +{ +} + +void Mono_Scroll(int ) +{ +} + +void Mono_View_Page(int ) +{ +} + + +#ifdef NEVER +int Mono_Printf(int string, ...) +{ + va_list va; + char buffer[256]; + + buffer[0] = '\0'; + if (MonoClass::Is_Enabled()) { + MonoClass *mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + + va_start(va, string); + vsprintf(buffer, Text_String(string), va); + + mono->Print(buffer); + + va_end(va); + } + return((short)strlen(buffer)); +} +#endif + diff --git a/MONOC.H b/MONOC.H new file mode 100644 index 0000000..d5601c6 --- /dev/null +++ b/MONOC.H @@ -0,0 +1,191 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\monoc.h_v 2.17 16 Oct 1995 16:45:28 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MONO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : July 2, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MONOC_H +#define MONOC_H + +//#include "dpmi.h" +//#include "function.h" + +class MonoClass { + /* + ** This is a private structure that is used to control which characters + ** are used when a box is drawn. Line drawing on the monochrome screen is + ** really made up of characters. This specifies which characters to use. + */ + typedef struct { + char UpperLeft; + char TopEdge; + char UpperRight; + char RightEdge; + char BottomRight; + char BottomEdge; + char BottomLeft; + char LeftEdge; + } BoxDataType; + + /* + ** Each cell is constructed of the actual character that is displayed and the + ** attribute to use. This character pair is located at every position on the + ** display (80 x 25). Since this cell pair can be represented by a "short" + ** integer, certain speed optimizations are taken in the monochrome drawing + ** code. + */ + typedef struct { + char Character; // Character to display. + char Attribute; // Attribute. + } CellType; + + /* + ** These private constants are used in the various monochrome operations. + */ + enum MonoClassPortEnums { + CONTROL_PORT=0x03B4, // CRTC control register. + DATA_PORT=0x03B5, // CRTC data register. + COLUMNS=80, // Number of columns. + LINES=25, // Number of lines. + SIZE_OF_PAGE=LINES*COLUMNS*sizeof(CellType), // Entire page size. + DEFAULT_ATTRIBUTE=0x02 // Normal white on black color attribute. + }; + + public: + enum MonoClassPageEnums { + MAX_MONO_PAGES=16, // Maximum RAM pages on mono card. + SEGMENT=0xB000 // Monochrome screen segment. + }; + + /* + ** These are the various box styles that may be used. + */ + typedef enum BoxStyleType { + SINGLE, // Single thickness. + DOUBLE_HORZ, // Double thick on the horizontal axis. + DOUBLE_VERT, // Double thick on the vertical axis. + DOUBLE, // Double thickness. + + COUNT + } BoxStyleType; + + MonoClass(void); + ~MonoClass(void); + + static void Enable(void) {Enabled = 1;}; + static void Disable(void) {Enabled = 0;}; + static int Is_Enabled(void) {return Enabled;}; + static MonoClass * Get_Current(void) {return PageUsage[0];}; + + void Draw_Box(int x, int y, int w, int h, char attrib=DEFAULT_ATTRIBUTE, BoxStyleType thick=SINGLE); + void Set_Default_Attribute(char attrib) {Attrib = attrib;}; + void Clear(void); + void Set_Cursor(int x, int y); + void Print(char const *text); + void Print(int text); + void Printf(char const *text, ...); + void Printf(int text, ...); + void Text_Print(char const *text, int x, int y, char attrib=DEFAULT_ATTRIBUTE); + void Text_Print(int text, int x, int y, char attrib=DEFAULT_ATTRIBUTE); + void View(void); + int Get_X(void) const {return X;}; + int Get_Y(void) const {return Y;}; + + /* + ** Handles deep copies for the mono class objects. This performs what is essentially + ** a screen copy. + */ + MonoClass & operator = (MonoClass const & ); + + /* + ** This merely makes a duplicate of the mono object into a newly created mono + ** object. + */ + MonoClass (MonoClass const &); + + private: + char X; // Cursor X position. + char Y; // Cursor Y position. + char Attrib; // Normal attribute to use if none specified. + char Page; // The current page to write to. + + /* + ** Helper functions to help with display operations. + */ + int Offset(int x=0, int y=0) const {return (SIZE_OF_PAGE*Page) + sizeof(CellType)*(x + (y*COLUMNS));}; + void Scroll(int lines); + void Store_Cell(CellType &cell, int x, int y) { + *(CellType *)((long)MonoSegment + Offset(x, y)) = cell; + }; + + /* + ** This is the segment/selector of the monochrome screen. + */ +// static DOSSegmentClass MonoSegment; + static void * MonoSegment; + + /* + ** This the the arrays of characters used for drawing boxes. + */ + static BoxDataType const CharData[4]; + + /* + ** This array contains pointers to the monochrome objects that are assigned + ** to each of the monochrome pages. As the monochrome pages are made visible, + ** they can be shuffled around between the actual locations. The first entry + ** in this table is the one that is visible. + */ + static MonoClass * PageUsage[MAX_MONO_PAGES]; + + /* + ** If this is true, then monochrome output is allowed. It defaults to false + ** so that monochrome output must be explicitly enabled. + */ + static int Enabled; +}; + +//extern int cdecl Mono_Printf(int string, ...); + + +void Mono_Set_Cursor(int x, int y); +int Mono_Printf(char const *string, ...); +void Mono_Clear_Screen(void); +void Mono_Text_Print(void const *text, int x, int y, int attrib); +void Mono_Draw_Rect(int x, int y, int w, int h, int attrib, int thick); +void Mono_Print(void const *text); +int Mono_X(void); +int Mono_Y(void); + +#endif + diff --git a/MOUSE.CPP b/MOUSE.CPP new file mode 100644 index 0000000..99687ea --- /dev/null +++ b/MOUSE.CPP @@ -0,0 +1,360 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\mouse.cpv 2.18 16 Oct 1995 16:49:56 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MOUSE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : June 30, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MouseClass::AI -- Process player input as it relates to the mouse * + * MouseClass::Override_Mouse_Shape -- Alters the shape of the mouse. * + * MouseClass::Init_Clear -- Sets the mouse system to a known state * + * MouseClass::MouseClass -- Default constructor for the mouse handler class. * + * MouseClass::One_Time -- Performs the one time initialization of the mouse system. * + * MouseClass::Set_Default_Mouse -- Sets the mouse to match the shape specified. * + * MouseClass::Revert_Mouse_Shape -- Reverts the mouse shape to the non overridden shape. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/* +** This points to the loaded mouse shapes. +*/ +void const * MouseClass::MouseShapes; + +/* +** This is the timer that controls the mouse animation. It is always at a fixed +** rate so it uses the constant system timer. +*/ +CountDownTimerClass MouseClass::Timer; + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * MouseClass::VTable; + + +/*********************************************************************************************** + * MouseClass::Set_Default_Mouse -- Sets the mouse to match the shape specified. * + * * + * This routine is used to inform the display system as to which mouse shape is desired. * + * * + * INPUT: mouse -- The mouse shape number to set the mouse to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +void MouseClass::Set_Default_Mouse(MouseType mouse, bool size) +{ + NormalMouseShape = mouse; + Override_Mouse_Shape(mouse, size); +} + + +/*********************************************************************************************** + * MouseClass::Revert_Mouse_Shape -- Reverts the mouse shape to the non overridden shape. * + * * + * Use this routine to cancel the effects of Override_Mouse_Shape(). It will revert the * + * mouse back to the original shape. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/27/1995 JLB : Created. * + *=============================================================================================*/ +void MouseClass::Revert_Mouse_Shape(void) +{ + Override_Mouse_Shape(NormalMouseShape, false); +} + + +void MouseClass::Mouse_Small(bool wwsmall) +{ + MouseStruct const * control = &MouseControl[CurrentMouseShape]; + + if (IsSmall == wwsmall) { + return; + } + + IsSmall = wwsmall; + + if (wwsmall) { + if (control->SmallFrame != -1) { + Set_Mouse_Cursor(control->X, control->Y, Extract_Shape(MouseShapes, control->SmallFrame + Frame/4)); + } else { + Set_Mouse_Cursor(MouseControl[MOUSE_NORMAL].X, MouseControl[MOUSE_NORMAL].Y, Extract_Shape(MouseShapes, MOUSE_NORMAL)); + } + } else { + Set_Mouse_Cursor(control->X, control->Y, Extract_Shape(MouseShapes, control->StartFrame + Frame/4)); + } +} + + +/*********************************************************************************************** + * MouseClass::Override_Mouse_Shape -- Alters the shape of the mouse. * + * * + * This routine is used to alter the shape of the mouse as needed. * + * Typical mouse shape change occurs when scrolling the map or * + * selecting targets. * + * * + * INPUT: mouse -- The mouse shape number to use. * + * * + * OUTPUT: bool; Was the mouse shape changed? * + * * + * WARNINGS: This is not intended to be used as a means to hide the * + * mouse. Nor will it work correctly if the mouse shape * + * file hasn't been loaded. * + * * + * HISTORY: * + * 03/10/1994 JLB : Created. * + * 06/03/1994 JLB : Made into member function. * + * 12/24/1994 JLB : Added small control parameter. * + *=============================================================================================*/ +bool MouseClass::Override_Mouse_Shape(MouseType mouse, bool wwsmall) +{ + MouseStruct const * control = &MouseControl[mouse]; + static bool startup = false; + int baseshp; + + /* + ** Only certain mouse shapes have a small counterpart. If the requested mouse + ** shape is not one of these, then force the small size override flag to false. + */ + if (control->SmallFrame == -1) { + wwsmall = false; + } + + /* + ** If the mouse shape is going to change, then inform the mouse driver of the + ** change. + */ + if (!startup || (MouseShapes && ((mouse != CurrentMouseShape) || (wwsmall != IsSmall)))) { + startup = true; + + Timer.Set(control->FrameRate); + Frame = 0; + +#ifdef OBSOLETE + Control.Set_Stage(0); + int rate = Options.Normalize_Delay(control->FrameRate); + Control.Set_Rate(MAX(rate, 1)); +#endif + baseshp = (wwsmall) ? control->SmallFrame : control->StartFrame; + + Set_Mouse_Cursor(control->X, control->Y, Extract_Shape(MouseShapes, baseshp + Frame/4)); + CurrentMouseShape = mouse; + IsSmall = wwsmall; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * MouseClass::AI -- Process player input as it relates to the mouse * + * * + * This routine will is to be called once per game tick and is passed the player keyboard * + * or mouse input code. It processes this code and updates the mouse shape as appropriate. * + * * + * INPUT: input -- The player input code as returned from Keyboard::Get(). * + * * + * x,y -- The mouse coordinate values to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + * 03/27/1995 JLB : New animation control. * + * 05/28/1995 JLB : Moderates animation so is more steady regardless of speed. * + * 06/30/1995 JLB : Uses constant timer system. * + *=============================================================================================*/ +void MouseClass::AI(KeyNumType &input, int x, int y) +{ +// bool doit = false; + void *mouse_shape_ptr; + MouseStruct const * control = &MouseControl[CurrentMouseShape]; + + if (control->FrameRate && Timer.Time() == 0) { + + Frame++; + Frame %= control->FrameCount; + Timer.Set(control->FrameRate); + +#ifdef OBSOLETE + Control.Set_Stage(Control.Fetch_Stage() % control->FrameCount); +#endif + + if (!IsSmall || control->SmallFrame != -1) { + int baseframe = (IsSmall) ? control->SmallFrame : control->StartFrame; + mouse_shape_ptr = Extract_Shape(MouseShapes, baseframe + Frame); + if (mouse_shape_ptr){ + Set_Mouse_Cursor(control->X, control->Y, mouse_shape_ptr); + } + } + } + + ScrollClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * MouseClass::MouseClass -- Default constructor for the mouse handler class. * + * * + * This is the default constructor for the mouse handling class. It merely sets up the * + * mouse system to its default state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +MouseClass::MouseClass(void) +{ + CurrentMouseShape = MOUSE_NORMAL; + NormalMouseShape = MOUSE_NORMAL; + Timer.Start(); +} + + +/*********************************************************************************************** + * MouseClass::One_Time -- Performs the one time initialization of the mouse system. * + * * + * Use this routine to load the mouse data file and perform any other necessary one time * + * preparations for the game. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine ONCE. * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void MouseClass::One_Time(void) +{ + ScrollClass::One_Time(); + + /* + ** Override the mouse shape file with the one in the current directory, but only if there + ** is an override file available. + */ + RawFileClass file("MOUSE.SHP"); + if (file.Is_Available()) { + MouseShapes = Load_Alloc_Data(file); + } else { + MouseShapes = MixFileClass::Retrieve("MOUSE.SHP"); + } + + VTable = ((void **)(((char *)this) + sizeof(VectorClass) - 4))[0]; +} + + +/*********************************************************************************************** + * MouseClass::Init_Clear -- Sets the mouse system to a known state * + * * + * This routine will reset the mouse handling system. Typically, this routine is called * + * when preparing for the beginning of a new scenario. * + * * + * INPUT: theater -- The theater that the scenario will take place. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void MouseClass::Init_Clear(void) +{ + ScrollClass::Init_Clear(); + IsSmall = false; + NormalMouseShape = MOUSE_NORMAL; +} + + +/* +** This array of structures is used to control the mouse animation +** sequences. +*/ +MouseClass::MouseStruct MouseClass::MouseControl[MOUSE_COUNT] = { + {0, 1, 0, 86, 0, 0}, // MOUSE_NORMAL + {1, 1, 0, -1, 15, 0}, // MOUSE_N + {2, 1, 0, -1, 29, 0}, // MOUSE_NE + {3, 1, 0, -1, 29, 12}, // MOUSE_E + {4, 1, 0, -1, 29, 23}, // MOUSE_SE + {5, 1, 0, -1, 15, 23}, // MOUSE_S + {6, 1, 0, -1, 0, 23}, // MOUSE_SW + {7, 1, 0, -1, 0, 13}, // MOUSE_W + {8, 1, 0, -1, 0, 0}, // MOUSE_NW + + {130, 1, 0, -1, 15, 0}, // MOUSE_NO_N + {131, 1, 0, -1, 29, 0}, // MOUSE_NO_NE + {132, 1, 0, -1, 29, 12}, // MOUSE_NO_E + {133, 1, 0, -1, 29, 23}, // MOUSE_NO_SE + {134, 1, 0, -1, 15, 23}, // MOUSE_NO_S + {135, 1, 0, -1, 0, 23}, // MOUSE_NO_SW + {136, 1, 0, -1, 0, 13}, // MOUSE_NO_W + {137, 1, 0, -1, 0, 0}, // MOUSE_NO_NW + + {11, 1, 0, 27, 15, 12}, // MOUSE_NO_MOVE + {10, 1, 0, 26, 15, 12}, // MOUSE_CAN_MOVE + {119, 3, 4, 148, 15, 12}, // MOUSE_ENTER + {53, 9, 4, -1, 15, 12}, // MOUSE_DEPLOY + {12, 6, 4, -1, 15, 12}, // MOUSE_CAN_SELECT + {18, 8, 4, 140, 15, 12}, // MOUSE_CAN_ATTACK + {62, 24, 2, -1, 15, 12}, // MOUSE_SELL_BACK + {154, 24, 2, -1, 15, 12}, // MOUSE_SELL_UNIT + {29, 24, 2, -1, 15, 12}, // MOUSE_REPAIR + {126, 1, 0, -1, 15, 12}, // MOUSE_NO_REPAIR + {125, 1, 0, -1, 15, 12}, // MOUSE_NO_SELL_BACK + {87, 1, 0, 151, 0, 0}, // MOUSE_RADAR_CURSOR + {103, 16, 2, -1, 15, 12}, // MOUSE_ION_CANNON + {96, 7, 4, -1, 15, 12}, // MOUSE_NUCLEAR_BOMB + {88, 8, 2, -1, 15, 12}, // MOUSE_AIR_STRIKE + {122, 3, 4, 127, 15, 12}, // MOUSE_DEMOLITIONS + {153, 1, 0, 152, 15, 12}, // MOUSE_AREA_GUARD +}; diff --git a/MOUSE.H b/MOUSE.H new file mode 100644 index 0000000..aa7f3f1 --- /dev/null +++ b/MOUSE.H @@ -0,0 +1,130 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\mouse.h_v 2.16 16 Oct 1995 16:45:06 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MOUSE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : December 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MOUSE_H +#define MOUSE_H + +#include "stage.h" +#include "scroll.h" + +class MouseClass: public ScrollClass +{ + public: + MouseClass(void); + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time inits + virtual void Init_Clear(void); // Clears all to known state + + virtual void AI(KeyNumType &input, int x, int y); + virtual bool Override_Mouse_Shape(MouseType mouse, bool wwsmall=false); + virtual void Revert_Mouse_Shape(void); + virtual MouseType Get_Mouse_Shape(void) const {return NormalMouseShape;}; + virtual void Mouse_Small(bool wwsmall); + + /* + ** File I/O. + */ + virtual bool Load(FileClass & file); + virtual bool Save(FileClass & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + virtual void Set_Default_Mouse(MouseType mouse, bool wwsmall = false); + + /* + ** This allows the tactical map input gadget access to change the + ** mouse shapes. + */ + friend class TacticalClass; + + private: + + /* + ** This type is used to control the frames and rates of the mouse + ** pointer. Some mouse pointers are actually looping animations. + */ + typedef struct MouseStruct + { + int StartFrame; // Starting frame number. + int FrameCount; // Number of animation frames. + int FrameRate; // Frame delay between changing frames. + int SmallFrame; // Start frame number for small version (if any). + int X,Y; // Hotspot X and Y offset. + } MouseStruct; + + /* + ** The control frames and rates for the various mouse pointers are stored + ** in this static array. + */ + static MouseStruct MouseControl[MOUSE_COUNT]; + + /* + ** If the small representation of the mouse is active, then this flag is true. + */ + unsigned IsSmall:1; + + /* + ** This points to the loaded mouse shapes. + */ + static void const * MouseShapes; + + /* + ** The mouse shape is controlled by these variables. These + ** hold the current mouse shape (so resetting won't be needlessly performed) and + ** the normal default mouse shape (when arrow shapes are needed). + */ + MouseType CurrentMouseShape; + MouseType NormalMouseShape; + + /* + ** For animating mouse shapes, this controls the frame and animation rate. + */ + static CountDownTimerClass Timer; + int Frame; +// StageClass Control; + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; +}; + + +#endif diff --git a/MPLAYER.CPP b/MPLAYER.CPP new file mode 100644 index 0000000..b05c557 --- /dev/null +++ b/MPLAYER.CPP @@ -0,0 +1,1408 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\mplayer.cpv 1.9 16 Oct 1995 16:51:08 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MPLAYER.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : April 14, 1995 * + * * + * Last Update : July 5, 1995 [BRR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Select_MPlayer_Game -- prompts user for NULL-Modem, Modem, or Network game * + * Read_MultiPlayer_Settings -- reads multi-player settings from conquer.ini * + * Write_MultiPlayer_Settings -- writes multi-player settings to conquer.ini * + * Read_Scenario_Descriptions -- reads multi-player scenario #'s # descriptions * + * Free_Scenario_Descriptions -- frees memory for the scenario descriptions * + * Computer_Message -- "sends" a message from the computer * + * Garble_Message -- "garbles" a message * + * Surrender_Dialog -- Prompts user for surrendering * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "tcpip.h" + +static void Garble_Message(char *buf); + +int Choose_Internet_Game(void); +int Get_Internet_Host_Or_Join(void); +int Get_IP_Address(void); +void Show_Internet_Connection_Progress(void); + +/*********************************************************************************************** + * Select_MPlayer_Game -- prompts user for NULL-Modem, Modem, or Network game * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * GAME_NORMAL, GAME_MODEM, etc. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +GameType Select_MPlayer_Game (void) +{ + + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + bool ipx_avail = FALSE; + int number_of_buttons; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 190*factor; + int d_dialog_h = 26*4*factor; + int d_dialog_x = ((320*factor - d_dialog_w) / 2); +// d_dialog_y = ((200 - d_dialog_h) / 2), + int d_dialog_y = ((136*factor - d_dialog_h) / 2); + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); + + int d_txt6_h = 11 * factor; + int d_margin = 7 *factor; + + int d_modemserial_w = 80*factor; + int d_modemserial_h = 9*factor; + int d_modemserial_x = d_dialog_cx - d_modemserial_w / 2; + int d_modemserial_y = d_dialog_y + d_margin + d_txt6_h + d_margin; +#if (0) + int d_internet_w = 80*factor; + int d_internet_h = 9*factor; + int d_internet_x = d_dialog_cx - d_internet_w / 2; + int d_internet_y = d_modemserial_y + d_modemserial_h + 2*factor; +#endif //(0) + int d_ipx_w = 80*factor; + int d_ipx_h = 9*factor; + int d_ipx_x = d_dialog_cx - d_ipx_w / 2; + int d_ipx_y = d_modemserial_y + d_modemserial_h + 2*factor; +// int d_ipx_y = d_internet_y + d_internet_h + 2*factor; + + int d_cancel_w = 60*factor; + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_ipx_y + d_ipx_h + d_margin; + + CountDownTimerClass delay; + + /*........................................................................ + Button enumerations: + ........................................................................*/ + enum { + BUTTON_MODEMSERIAL = 100, +#if (0) + BUTTON_INTERNET, +#endif //(0) + BUTTON_IPX, + BUTTON_CANCEL, + + NUM_OF_BUTTONS = 3, + }; + number_of_buttons = NUM_OF_BUTTONS; + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, // includes map interior & coord values + REDRAW_BACKGROUND, // includes box, map bord, key, coord labels, btns + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + /*........................................................................ + Dialog variables: + ........................................................................*/ + KeyNumType input; // input from user + bool process; // loop while true + RedrawType display; // true = re-draw everything + GameType retval; // return value + int selection; + bool pressed; + int curbutton; + TextButtonClass *buttons[NUM_OF_BUTTONS]; + + /*........................................................................ + Buttons + ........................................................................*/ + ControlClass *commands = NULL; // the button list + + // + // If neither IPX or winsock are active then do only the modem serial dialog + // + if (Ipx.Is_IPX){ + ipx_avail = TRUE; + } + + + TextButtonClass modemserialbtn (BUTTON_MODEMSERIAL, TXT_MODEM_SERIAL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_modemserial_x, d_modemserial_y, d_modemserial_w, d_modemserial_h); +#if (0) + TextButtonClass internetbtn (BUTTON_INTERNET, TXT_INTERNET, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_internet_x, d_internet_y, d_internet_w, d_internet_h); +#endif //(0) + TextButtonClass ipxbtn (BUTTON_IPX, TXT_NETWORK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_ipx_x, d_ipx_y, d_ipx_w, d_ipx_h); + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + /* + ............................ Create the list ............................. + */ + commands = &modemserialbtn; +#if (0) + internetbtn.Add_Tail(*commands); +#endif //(0) + if (ipx_avail){ + ipxbtn.Add_Tail(*commands); + } + cancelbtn.Add_Tail(*commands); + + /* + ......................... Fill array of button ptrs ...................... + */ + curbutton = 0; + buttons[0] = &modemserialbtn; +#if (0) + buttons[1] = &internetbtn; +#endif //(0) + if (ipx_avail){ + buttons[1] = &ipxbtn; + buttons[2] = &cancelbtn; + }else{ + buttons[1] = &cancelbtn; + number_of_buttons--; + } + + buttons[curbutton]->Turn_On(); + + Keyboard::Clear(); + + Fancy_Text_Print(TXT_NONE, 0, 0, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + display = REDRAW_ALL; + process = true; + pressed = false; + while (process) { + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + /* + ..................... Refresh the backdrop ...................... + */ + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + /* + ..................... Draw the background ....................... + */ + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption (TXT_SELECT_MPLAYER_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + } + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (BUTTON_MODEMSERIAL | KN_BUTTON): + selection = BUTTON_MODEMSERIAL; + pressed = true; + break; + +#if (0) + case (BUTTON_INTERNET | KN_BUTTON): + selection = BUTTON_INTERNET; + pressed = true; + break; +#endif //(0) + + case (BUTTON_IPX | KN_BUTTON): + selection = BUTTON_IPX; + pressed = true; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + selection = BUTTON_CANCEL; + pressed = true; + break; + + case KN_UP: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton--; + if (curbutton < 0) + curbutton = (number_of_buttons - 1); + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_DOWN: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton++; + if (curbutton > (number_of_buttons - 1) ) + curbutton = 0; + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_RETURN: + selection = curbutton + BUTTON_MODEMSERIAL; + if (!ipx_avail) selection--; + pressed = true; + break; + + default: + break; + } + + if (pressed) { + // + // to make sure the selection is correct in case they used the mouse + // + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton = selection - BUTTON_MODEMSERIAL; + buttons[curbutton]->Turn_On(); +// buttons[curbutton]->Flag_To_Redraw(); + buttons[curbutton]->IsPressed = true; + buttons[curbutton]->Draw_Me(true); + + switch (selection) { + case (BUTTON_MODEMSERIAL): + + // + // Pop up the modem/serial/com port dialog + // + retval = Select_Serial_Dialog(); + + if (retval != GAME_NORMAL) { + process = false; + } else { + buttons[curbutton]->IsPressed = false; + display = REDRAW_ALL; + } + break; + +#if (0) + case (BUTTON_INTERNET): +//#define ONLY_FOR_E3 +#ifdef ONLY_FOR_E3 + Call_Back(); + Show_Internet_Connection_Progress(); //changed to do nothing + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Show_Mouse(); + Call_Back(); + CCMessageBox().Process("Error! - Unable to ping KANE.WESTWOOD.COM"); + + buttons[curbutton]->IsPressed = false; + display = REDRAW_ALL; + + +#endif //ONLY_FOR_E3 + + +#ifdef FORCE_WINSOCK + if (Winsock.Init()){ + Read_MultiPlayer_Settings (); + int result = Choose_Internet_Game(); + + if (!result){ + Winsock.Close(); + buttons[curbutton]->IsPressed = false; + display = REDRAW_ALL; + break; + } + + result = Get_Internet_Host_Or_Join(); + if (result == 1){ + Winsock.Close(); + buttons[curbutton]->IsPressed = false; + display = REDRAW_ALL; + break; + } + Server = !result; + + if (Server){ +#if (0) + ModemGameToPlay = INTERNET_HOST; + Winsock.Start_Server(); +#else + result = Get_IP_Address(); + if (!result){ + Winsock.Close(); + buttons[curbutton]->IsPressed = false; + display = REDRAW_ALL; + break; + } + Winsock.Set_Host_Address(PlanetWestwoodIPAddress); + Winsock.Start_Server(); +#endif + + }else{ + ModemGameToPlay = INTERNET_JOIN; + result = Get_IP_Address(); + if (!result){ + Winsock.Close(); + buttons[curbutton]->IsPressed = false; + display = REDRAW_ALL; + break; + } + Winsock.Set_Host_Address(PlanetWestwoodIPAddress); + Winsock.Start_Client(); + } + + //CountDownTimerClass connect_timeout; + //connect_timeout.Set(15*60); + + ////Show_Internet_Connection_Progress(); + + if (!Winsock.Get_Connected()){ + Winsock.Close(); + return(GAME_NORMAL); + } + + SerialSettingsType *settings; + settings = &SerialDefaults; + Init_Null_Modem(settings); + if (Server){ + if (Com_Scenario_Dialog()){ + return (GAME_INTERNET); + }else{ + Winsock.Close(); + return (GAME_NORMAL); + } + }else{ + if (Com_Show_Scenario_Dialog()){ + return (GAME_INTERNET); + }else{ + Winsock.Close(); + return (GAME_NORMAL); + } + } + } +#endif //FORCE_WINSOCK + break; + +#endif //(0) + + case (BUTTON_IPX): + retval = GAME_IPX; + process = false; + break; + + case (BUTTON_CANCEL): + retval = GAME_NORMAL; + process = false; + break; + } + + pressed = false; + } + } + return(retval); +} + + +/*********************************************************************************************** + * Read_MultiPlayer_Settings -- reads multi-player settings from conquer.ini * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +void Read_MultiPlayer_Settings (void) +{ + char *buffer; // INI staging buffer pointer. + char *tbuffer; // Accumulation buffer of trigger IDs. + int len; // Length of data in buffer. + char *tokenptr; // ptr to token + PhoneEntryClass *phone; // a phone book entry + char *entry; // a phone book entry + char buf[128]; // buffer for parsing INI entry + int i; + CELL cell; + + /*------------------------------------------------------------------------ + Fetch working pointer to the INI staging buffer. Make sure that the buffer + is cleared out before proceeding. (Don't use the HidPage for this, since + the HidPage may be needed for various uncompressions during the INI + parsing.) + ------------------------------------------------------------------------*/ + buffer = (char *)_ShapeBuffer; + memset(buffer, '\0', _ShapeBufferSize); + + /*------------------------------------------------------------------------ + Clear the initstring entries + ------------------------------------------------------------------------*/ + for (i = 0; i < InitStrings.Count(); i++) { + delete[] InitStrings[i]; + } + InitStrings.Clear(); + + /*------------------------------------------------------------------------ + Clear the dialing entries + ------------------------------------------------------------------------*/ + for (i = 0; i < PhoneBook.Count(); i++) { + delete PhoneBook[i]; + } + PhoneBook.Clear(); + + /*------------------------------------------------------------------------ + Create filename and read the file. + ------------------------------------------------------------------------*/ + CCFileClass file ("CONQUER.INI"); + if (!file.Is_Available()) { + return; + } else { + file.Read(buffer, _ShapeBufferSize-1); + } + file.Close(); + + if (!Special.IsFromWChat){ + /*------------------------------------------------------------------------ + Get the player's last-used Handle + ------------------------------------------------------------------------*/ + WWGetPrivateProfileString("MultiPlayer", "Handle", "Noname", MPlayerName, + sizeof(MPlayerName), buffer); + + /*------------------------------------------------------------------------ + Get the player's last-used Color + ------------------------------------------------------------------------*/ + MPlayerPrefColor = WWGetPrivateProfileInt("MultiPlayer", "Color", 0, buffer); + MPlayerHouse = (HousesType)WWGetPrivateProfileInt("MultiPlayer", "Side", + HOUSE_GOOD, buffer); + CurPhoneIdx = WWGetPrivateProfileInt("MultiPlayer", "PhoneIndex", -1, buffer); + }else{ + CurPhoneIdx = -1; + } + +#if 1 + TrapCheckHeap = WWGetPrivateProfileInt( "MultiPlayer", "CheckHeap", 0, buffer); +#endif + + /*------------------------------------------------------------------------ + Read in default serial settings + ------------------------------------------------------------------------*/ + WWGetPrivateProfileString ("SerialDefaults", "ModemName", "NoName", SerialDefaults.ModemName, MODEM_NAME_MAX, buffer); + if (!strcmp ( SerialDefaults.ModemName, "NoName")) { + SerialDefaults.ModemName[0] = 0; + } + WWGetPrivateProfileString ("SerialDefaults", "Port", "0", buf, 5, buffer); + sscanf (buf, "%x", &SerialDefaults.Port); + SerialDefaults.IRQ = WWGetPrivateProfileInt("SerialDefaults", "IRQ", -1, buffer); + SerialDefaults.Baud = WWGetPrivateProfileInt("SerialDefaults", "Baud", -1, buffer); + SerialDefaults.Init = WWGetPrivateProfileInt("SerialDefaults", "Init", 0, buffer); + SerialDefaults.Compression = WWGetPrivateProfileInt ("SerialDefaults", "Compression", 0, buffer); + SerialDefaults.ErrorCorrection = WWGetPrivateProfileInt ("SerialDefaults", "ErrorCorrection", 0, buffer); + SerialDefaults.HardwareFlowControl = WWGetPrivateProfileInt ("SerialDefaults", "HardwareFlowControl", 1, buffer); + WWGetPrivateProfileString ("SerialDefaults", "DialMethod", "T", + buf, 2, buffer); + + + // find dial method + + for (i = 0; i < DIAL_METHODS; i++) { + if ( !strcmpi( buf, DialMethodCheck[ i ]) ) { + SerialDefaults.DialMethod = (DialMethodType)i; + break; + } + } + + // if method not found set to touch tone + + if (i == DIAL_METHODS) { + SerialDefaults.DialMethod = DIAL_TOUCH_TONE; + } + + SerialDefaults.InitStringIndex = + WWGetPrivateProfileInt("SerialDefaults", + "InitStringIndex", 0, buffer); + + SerialDefaults.CallWaitStringIndex = + WWGetPrivateProfileInt("SerialDefaults", + "CallWaitStringIndex", CALL_WAIT_CUSTOM, + buffer); + + WWGetPrivateProfileString ("SerialDefaults", "CallWaitString", "", + SerialDefaults.CallWaitString, CWAITSTRBUF_MAX, buffer); + + if (SerialDefaults.IRQ == 0 || + SerialDefaults.Baud == 0) { + + SerialDefaults.Port = 0; + SerialDefaults.IRQ = -1; + SerialDefaults.Baud = -1; + } + + /*------------------------------------------------------------------------ + Set 'tbuffer' to point past the actual INI data + ------------------------------------------------------------------------*/ + len = strlen(buffer) + 2; + tbuffer = buffer + len; + + /*------------------------------------------------------------------------ + Read all Base-Scenario names into 'tbuffer' + ------------------------------------------------------------------------*/ + WWGetPrivateProfileString("InitStrings", NULL, NULL, tbuffer, + ShapeBufferSize-len, buffer); + + /*------------------------------------------------------------------------ + Read in & store each entry + ------------------------------------------------------------------------*/ + while (*tbuffer != '\0') { + entry = new char[ INITSTRBUF_MAX ]; + + entry[0] = 0; + + WWGetPrivateProfileString("InitStrings", tbuffer, NULL, entry, + INITSTRBUF_MAX, buffer); + + strupr( entry ); + + InitStrings.Add( entry ); + + tbuffer += strlen(tbuffer) + 1; + } + + // if no entries then have at least one + + if ( tbuffer == (buffer + len) ) { + entry = new char[ INITSTRBUF_MAX ]; + strcpy( entry, "ATZ" ); + InitStrings.Add( entry ); + SerialDefaults.InitStringIndex = 0; + } else { + len = strlen(buffer) + 2; + } + + /*------------------------------------------------------------------------ + Repeat the process for the phonebook + ------------------------------------------------------------------------*/ + tbuffer = buffer + len; + + /*------------------------------------------------------------------------ + Read in all phone book listings. + Format: Name=PhoneNum,Port,IRQ,Baud,InitString + ------------------------------------------------------------------------*/ + + /*........................................................................ + Read the entry names in + ........................................................................*/ + WWGetPrivateProfileString("PhoneBook", NULL, NULL, tbuffer, + ShapeBufferSize-len, buffer); + + while (*tbuffer != '\0') { + /*..................................................................... + Create a new phone book entry + .....................................................................*/ + phone = new PhoneEntryClass(); + + /*..................................................................... + Read the entire entry in + .....................................................................*/ + WWGetPrivateProfileString("PhoneBook", tbuffer, NULL, buf, 128, buffer); + + /*..................................................................... + Extract name, phone # & serial port settings + .....................................................................*/ + tokenptr = strtok( buf, "|" ); + if (tokenptr) { + strcpy( phone->Name, tokenptr ); + strupr( phone->Name ); + } else { + phone->Name[0] = 0; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + strcpy( phone->Number, tokenptr ); + strupr( phone->Number ); + } else { + phone->Number[0] = 0; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + sscanf( tokenptr, "%x", &phone->Settings.Port ); + } else { + phone->Settings.Port = 0; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.IRQ = atoi( tokenptr ); + } else { + phone->Settings.IRQ = -1; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.Baud = atoi( tokenptr ); + } else { + phone->Settings.Baud = -1; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.Compression = atoi( tokenptr ); + } else { + phone->Settings.Compression = 0; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.ErrorCorrection = atoi( tokenptr ); + } else { + phone->Settings.ErrorCorrection = 0; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.HardwareFlowControl = atoi( tokenptr ); + } else { + phone->Settings.HardwareFlowControl = 1; + } + + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + strcpy( buf, tokenptr ); + + // find dial method + + for (i = 0; i < DIAL_METHODS; i++) { + if ( !strcmpi( buf, DialMethodCheck[ i ]) ) { + phone->Settings.DialMethod = (DialMethodType)i; + break; + } + } + + // if method not found set to touch tone + + if (i == DIAL_METHODS) { + phone->Settings.DialMethod = DIAL_TOUCH_TONE; + } + } else { + phone->Settings.DialMethod = DIAL_TOUCH_TONE; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.InitStringIndex = atoi( tokenptr ); + } else { + phone->Settings.InitStringIndex = 0; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.CallWaitStringIndex = atoi( tokenptr ); + } else { + phone->Settings.CallWaitStringIndex = CALL_WAIT_CUSTOM; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + strcpy (phone->Settings.CallWaitString, tokenptr); + } else { + phone->Settings.CallWaitString[0] = 0; + } + + /*..................................................................... + Add it to our list + .....................................................................*/ + PhoneBook.Add(phone); + + tbuffer += strlen(tbuffer) + 1; + } + + /*------------------------------------------------------------------------ + Read special recording playback values, to help find sync bugs + ------------------------------------------------------------------------*/ + if (PlaybackGame) { + TrapFrame = WWGetPrivateProfileInt ("SyncBug","Frame",0x7fffffff, buffer); + + TrapObjType = (RTTIType)WWGetPrivateProfileInt ("SyncBug","Type",RTTI_NONE, buffer); + WWGetPrivateProfileString ("SyncBug","Type","NONE",buf,80,buffer); + if (!stricmp(buf,"AIRCRAFT")) + TrapObjType = RTTI_AIRCRAFT; + else if (!stricmp(buf,"ANIM")) + TrapObjType = RTTI_ANIM; + else if (!stricmp(buf,"BUILDING")) + TrapObjType = RTTI_BUILDING; + else if (!stricmp(buf,"BULLET")) + TrapObjType = RTTI_BULLET; + else if (!stricmp(buf,"INFANTRY")) + TrapObjType = RTTI_INFANTRY; + else if (!stricmp(buf,"UNIT")) + TrapObjType = RTTI_UNIT; + else { + TrapObjType = RTTI_NONE; + } + + WWGetPrivateProfileString ("SyncBug","Coord","0",buf,80,buffer); + sscanf(buf,"%x",&TrapCoord); + + WWGetPrivateProfileString ("SyncBug","this","0",buf,80,buffer); + sscanf(buf,"%x",&TrapThis); + + WWGetPrivateProfileString ("SyncBug","Cell","0",buf,80,buffer); + cell = atoi(buf); + if (cell) { + TrapCell = &(Map[cell]); + } + } +} + + +/*********************************************************************************************** + * Write_MultiPlayer_Settings -- writes multi-player settings to conquer.ini * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +void Write_MultiPlayer_Settings (void) +{ + char * buffer; // INI staging buffer pointer. + CCFileClass file; + int i; + char entrytext[4]; + char buf[128]; // buffer for parsing INI entry + + /*------------------------------------------------------------------------ + Get a working pointer to the INI staging buffer. Make sure that the buffer + starts cleared out of any data. + ------------------------------------------------------------------------*/ + buffer = (char *)_ShapeBuffer; + memset(buffer, '\0', _ShapeBufferSize); + + file.Set_Name("CONQUER.INI"); + if (file.Is_Available()) { + file.Open(READ); + file.Read(buffer, _ShapeBufferSize-1); + file.Close(); + } + + /*------------------------------------------------------------------------ + Save the player's last-used Handle & Color + ------------------------------------------------------------------------*/ + WWWritePrivateProfileInt("MultiPlayer", "PhoneIndex", CurPhoneIdx, buffer); + WWWritePrivateProfileInt ("MultiPlayer", "Color", MPlayerPrefColor, buffer); + WWWritePrivateProfileInt ("MultiPlayer", "Side", MPlayerHouse, buffer); + WWWritePrivateProfileString("MultiPlayer", "Handle", MPlayerName, buffer); + + /*------------------------------------------------------------------------ + Clear all existing SerialDefault entries. + ------------------------------------------------------------------------*/ + WWWritePrivateProfileString ("SerialDefaults", NULL, NULL, buffer); + + /*------------------------------------------------------------------------ + Save default serial settings in opposite order you want to see them + ------------------------------------------------------------------------*/ + WWWritePrivateProfileString("SerialDefaults", "CallWaitString", + SerialDefaults.CallWaitString, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "CallWaitStringIndex", SerialDefaults.CallWaitStringIndex, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "InitStringIndex", SerialDefaults.InitStringIndex, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "Init", SerialDefaults.Init, buffer); + WWWritePrivateProfileString("SerialDefaults", "DialMethod", + DialMethodCheck[ SerialDefaults.DialMethod ], buffer); + WWWritePrivateProfileInt ("SerialDefaults", "Baud", SerialDefaults.Baud, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "IRQ", SerialDefaults.IRQ, buffer); + sprintf(buf, "%x", SerialDefaults.Port); + WWWritePrivateProfileString("SerialDefaults", "Port", buf, buffer); + WWWritePrivateProfileString("SerialDefaults", "ModemName", SerialDefaults.ModemName, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "Compression", SerialDefaults.Compression , buffer); + WWWritePrivateProfileInt ("SerialDefaults", "ErrorCorrection", SerialDefaults.ErrorCorrection, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "HardwareFlowControl", SerialDefaults.HardwareFlowControl, buffer); + + /*------------------------------------------------------------------------ + Clear all existing InitString entries. + ------------------------------------------------------------------------*/ + WWWritePrivateProfileString ("InitStrings", NULL, NULL, buffer); + + /*------------------------------------------------------------------------ + Save all InitString entries. In descending order so they come out in + ascending order. + ------------------------------------------------------------------------*/ + for (i = (InitStrings.Count() - 1); i >= 0; i--) { + sprintf( buf, "%03d", i); + WWWritePrivateProfileString ("InitStrings", buf, InitStrings[i], buffer); + } + + /*------------------------------------------------------------------------ + Clear all existing Phone Book entries. + ------------------------------------------------------------------------*/ + WWWritePrivateProfileString ("PhoneBook", NULL, NULL, buffer); + + /*------------------------------------------------------------------------ + Save all Phone Book entries. + Format: Entry=Name,PhoneNum,Port,IRQ,Baud,InitString + ------------------------------------------------------------------------*/ + for (i = (PhoneBook.Count() - 1); i >= 0; i--) { + sprintf(buf,"%s|%s|%x|%d|%d|%d|%d|%d|%s|%d|%d|%s", + PhoneBook[i]->Name, + PhoneBook[i]->Number, + PhoneBook[i]->Settings.Port, + PhoneBook[i]->Settings.IRQ, + PhoneBook[i]->Settings.Baud, + PhoneBook[i]->Settings.Compression, + PhoneBook[i]->Settings.ErrorCorrection, + PhoneBook[i]->Settings.HardwareFlowControl, + DialMethodCheck[ PhoneBook[i]->Settings.DialMethod ], + PhoneBook[i]->Settings.InitStringIndex, + PhoneBook[i]->Settings.CallWaitStringIndex, + PhoneBook[i]->Settings.CallWaitString); + sprintf( entrytext, "%03d", i ); + WWWritePrivateProfileString ("PhoneBook", entrytext, buf, buffer); + } + + /*------------------------------------------------------------------------ + Write the INI data out to a file. + ------------------------------------------------------------------------*/ + file.Open(WRITE); + file.Write(buffer,strlen(buffer)); + file.Close(); +} + + +/*********************************************************************************************** + * Read_Scenario_Descriptions -- reads multi-player scenario #'s # descriptions * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +void Read_Scenario_Descriptions (void) +{ + char *buffer; // INI staging buffer pointer. + CCFileClass file; + int i; + char fname[20]; + + /*------------------------------------------------------------------------ + Clear the scenario description lists + ------------------------------------------------------------------------*/ + MPlayerScenarios.Clear(); + MPlayerFilenum.Clear(); + + /*------------------------------------------------------------------------ + Loop through all possible scenario numbers; if a file is available, add + its number to the FileNum list. + ------------------------------------------------------------------------*/ + for (i = 0; i < 100; i++) { + Set_Scenario_Name(ScenarioName, i, SCEN_PLAYER_MPLAYER, + SCEN_DIR_EAST, SCEN_VAR_A); + sprintf(fname,"%s.INI",ScenarioName); + file.Set_Name (fname); + + if (file.Is_Available()) { + MPlayerFilenum.Add(i); + } + } + + /*------------------------------------------------------------------------ + Now, for every file in the FileNum list, read in the INI file, and extract + its description. + ------------------------------------------------------------------------*/ + for (i = 0; i < MPlayerFilenum.Count(); i++) { + /*..................................................................... + Fetch working pointer to the INI staging buffer. Make sure that the + buffer is cleared out before proceeding. + .....................................................................*/ + buffer = (char *)_ShapeBuffer; + memset(buffer, '\0', _ShapeBufferSize); + + /*..................................................................... + Create filename and read the file. + .....................................................................*/ + Set_Scenario_Name(ScenarioName, MPlayerFilenum[i], SCEN_PLAYER_MPLAYER, + SCEN_DIR_EAST, SCEN_VAR_A); + sprintf(fname,"%s.INI",ScenarioName); + file.Set_Name (fname); + file.Read(buffer, _ShapeBufferSize-1); + file.Close(); + + /*..................................................................... + Extract description & add it to the list. + .....................................................................*/ + WWGetPrivateProfileString("Basic", "Name", "Nulls-Ville", + MPlayerDescriptions[i], 40, buffer); + MPlayerScenarios.Add(MPlayerDescriptions[i]); + } +} + + +/*********************************************************************************************** + * Free_Scenario_Descriptions -- frees memory for the scenario descriptions * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/05/1995 BRR : Created. * + *=============================================================================================*/ +void Free_Scenario_Descriptions(void) +{ + int i; + + /*------------------------------------------------------------------------ + Clear the scenario descriptions & filenames + ------------------------------------------------------------------------*/ + MPlayerScenarios.Clear(); + MPlayerFilenum.Clear(); + + /*------------------------------------------------------------------------ + Clear the initstring entries + ------------------------------------------------------------------------*/ + for (i = 0; i < InitStrings.Count(); i++) { + delete InitStrings[i]; + } + InitStrings.Clear(); + + /*------------------------------------------------------------------------ + Clear the dialing entries + ------------------------------------------------------------------------*/ + for (i = 0; i < PhoneBook.Count(); i++) { + delete PhoneBook[i]; + } + PhoneBook.Clear(); +} + + +/*************************************************************************** + * Computer_Message -- "sends" a message from the computer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/06/1995 BRR : Created. * + *=========================================================================*/ +void Computer_Message(void) +{ + int color; + char txt[160]; + HousesType house; + HouseClass *ptr; + + /*------------------------------------------------------------------------ + Find the computer house that the message will be from + ------------------------------------------------------------------------*/ + for (house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + MPlayerMax); house++) { + ptr = HouseClass::As_Pointer(house); + + if (!ptr || ptr->IsHuman || ptr->IsDefeated) { + continue; + } + + /*..................................................................... + Decode this house's color + .....................................................................*/ + color = MPlayerTColors[ptr->RemapColor]; + + /*..................................................................... + We now have a 1/4 chance of echoing one of the human players' messages + back. + .....................................................................*/ + if (IRandom(0,3) == 2) { + /*.................................................................. + Now we have a 1/3 chance of garbling the human message. + ..................................................................*/ + if (IRandom(0,2) == 1) { + Garble_Message(LastMessage); + } + + /*.................................................................. + Only add the message if there is one to add. + ..................................................................*/ + if (strlen(LastMessage)) { + sprintf(txt,"%s %s",Text_String(TXT_FROM_COMPUTER),LastMessage); + Messages.Add_Message(txt, color, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, 0, 0); + } + } else { + sprintf(txt,"%s %s",Text_String(TXT_FROM_COMPUTER), + Text_String(TXT_COMP_MSG1 + IRandom(0,12))); + Messages.Add_Message(txt, color, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, 0, 0); + } + + return; + } +} + + +/*************************************************************************** + * Garble_Message -- "garbles" a message * + * * + * INPUT: * + * buf buffer to garble; stores output message * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/06/1995 BRR : Created. * + *=========================================================================*/ +static void Garble_Message(char *buf) +{ + char txt[80]; + char punct[20]; // for punctuation + char *p; // working ptr + int numwords; // # words in the phrase + char *words[40]; // ptrs to various words in the phrase + int i,j; + + /*------------------------------------------------------------------------ + Pull off any trailing punctuation + ------------------------------------------------------------------------*/ + p = buf + strlen(buf) - 1; + while (1) { + if (p < buf) + break; + if (p[0]=='!' || p[0]=='.' || p[0]=='?') { + p--; + } else { + p++; + break; + } + if (strlen(p) >= (sizeof(punct) - 1) ) { + break; + } + } + strcpy (punct, p); + p[0] = 0; + + for (i = 0; i < 40; i++) { + words[i] = NULL; + } + + /*------------------------------------------------------------------------ + Copy the original buffer + ------------------------------------------------------------------------*/ + strcpy(txt,buf); + + /*------------------------------------------------------------------------ + Split it up into words + ------------------------------------------------------------------------*/ + p = strtok (txt, " "); + numwords = 0; + while (p) { + words[numwords] = p; + numwords++; + p = strtok (NULL, " "); + } + + /*------------------------------------------------------------------------ + Now randomly put the words back. Don't use the real random-number + generator, since different machines will have different LastMessage's, + and will go out of sync. + ------------------------------------------------------------------------*/ + buf[0] = 0; + for (i = 0; i < numwords; i++) { + j = Sim_IRandom(0,numwords); + if (words[j] == NULL) { // this word has been used already + i--; + continue; + } + strcat(buf,words[j]); + words[j] = NULL; + if (i < numwords-1) + strcat(buf," "); + } + strcat(buf,punct); +} + + +/*************************************************************************** + * Surrender_Dialog -- Prompts user for surrendering * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = user cancels, 1 = user wants to surrender. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/05/1995 BRR : Created. * + *=========================================================================*/ +int Surrender_Dialog(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 170*factor; // dialog width + int d_dialog_h = 53*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // centered x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // coord of x-center + + int d_txt6_h = 6*factor+1; // ht of 6-pt text + int d_margin = 5*factor; // margin width/height + int d_topmargin = 20*factor; // top margin + + int d_ok_w = 45*factor; // ok width + int d_ok_h = 9*factor; // ok height + int d_ok_x = d_dialog_cx - d_ok_w - 5*factor; // ok x + int d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin; // ok y + + int d_cancel_w = 45*factor; // cancel width + int d_cancel_h = 9*factor; // cancel height + int d_cancel_x = d_dialog_cx + 5*factor; // cancel x + int d_cancel_y = d_dialog_y + d_dialog_h - d_cancel_h - d_margin; // cancel y + + + /*........................................................................ + Button enumerations + ........................................................................*/ + enum { + BUTTON_OK = 100, + BUTTON_CANCEL, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display; // requested redraw level + bool process; // loop while true + KeyNumType input; + int retcode; + + /*........................................................................ + Buttons + ........................................................................*/ + ControlClass *commands = NULL; // the button list + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_ok_x, d_ok_y, d_ok_w, d_ok_h); + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + /* + ......................... Create the button list ......................... + */ + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + display = REDRAW_ALL; + process = true; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + if (Main_Loop()) { + retcode = 0; + process = false; + } + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + + /* + ...................... Display the dialog box ...................... + */ + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption(TXT_NONE, d_dialog_x, d_dialog_y, d_dialog_w); + + /* + ....................... Draw the captions ....................... + */ + Fancy_Text_Print(Text_String(TXT_SURRENDER), + d_dialog_cx, d_dialog_y + d_topmargin, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + + /* + ........................ Redraw the buttons ........................ + */ + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + retcode = 1; + process = false; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + retcode = 0; + process = false; + break; + + default: + break; + } + } + + /* + --------------------------- Redraw the display --------------------------- + */ + HiddenPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + + return (retcode); +} diff --git a/MSGBOX.CPP b/MSGBOX.CPP new file mode 100644 index 0000000..e29dc49 --- /dev/null +++ b/MSGBOX.CPP @@ -0,0 +1,482 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\msgbox.cpv 2.18 16 Oct 1995 16:50:48 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : August 24, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CCMessageBox::Process -- Handles all the options graphic interface. * + * CCMessageBox::Process -- Displays message box. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "msgbox.h" +#include "gadget.h" + +#ifdef JAPANESE +CCMessageBox::CCMessageBox(int caption, bool pict) : Caption(caption), IsPicture(pict) +{ +} +#endif + +/*********************************************************************************************** + * CCMessageBox::Process -- pops up a message with yes/no, etc * + * * + * This function displays a dialog box with a one-line message, and * + * up to two buttons. The 2nd button is optional. The buttons default * + * to "OK" and nothing, respectively. The hotkeys for the buttons are * + * RETURN and ESCAPE. * + * * + * INPUT: * + * msg message to display * + * b1txt text for 1st button * + * b2txt text for 2nd button; NULL = no 2nd button * + * * + * OUTPUT: * + * # of button selected (0 = 1st) * + * * + * WARNINGS: * + * 'msg' text must be <= 38 chars * + * 'b1txt' and 'b2txt' must each be <= 18 chars * + * * + * HISTORY: * + * 11/08/1994 BR : Created. * + * 05/18/1995 JLB : Uses new font and dialog style. * + * 08/24/1995 JLB : Handles three buttons. * + *=============================================================================================*/ +#define BUTTON_1 1 +#define BUTTON_2 2 +#define BUTTON_3 3 +#define BUTTON_FLAG 0x8000 +int CCMessageBox::Process(const char *msg, const char *b1txt, const char *b2txt, const char *b3txt, bool preserve) +{ +#define BUFFSIZE (511) +//#define BUFFSIZE (255) + char buffer[BUFFSIZE]; + bool retval; + bool process; // loop while true + KeyNumType input; // user input + int selection; + bool pressed; + int curbutton; + TextButtonClass *buttons[3]; + void *back; + BOOL display; // display level + int realval[5]; + + GraphicBufferClass seen_buff_save(VisiblePage.Get_Width(),VisiblePage.Get_Height(),(void*)NULL); + + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + if (b1txt && *b1txt == '\0') b1txt = 0; + if (b2txt && *b2txt == '\0') b2txt = 0; + if (b3txt && *b3txt == '\0') b3txt = 0; + + /* + ** Examine the optional button parameters. Fetch the width and starting + ** characters for each. + */ + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_6PT_GRAD|TPF_NOSHADOW); + char b1char, b2char, b3char; // 1st char of each string + int bwidth, bheight; // button width and height + int numbuttons = 0; + if (b1txt) { + b1char = toupper(b1txt[0]); + + /* + ** Build the button list. + */ + bheight = FontHeight + FontYSpacing + (2*factor); + bwidth = MAX((String_Pixel_Width(b1txt) + (8 *factor)), 30 * factor); + + if (b2txt) { + numbuttons = 2; + b2char = toupper(b2txt[0]); + bwidth = MAX((String_Pixel_Width( b2txt ) + (8*factor)), bwidth); + + if (b3txt) { + numbuttons = 3; + b3char = toupper(b3txt[0]); + } + + } else { + numbuttons = 1; + } + } + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + buffer[BUFFSIZE-1] = 0; + strncpy(buffer, msg, BUFFSIZE-2); + int width; + int height; + +#ifdef JAPANESE + if(IsPicture) { + width = 90; + height = 140 - 60; + } else +#endif + Format_Window_String(buffer, 255 * factor, width, height); + +//BG #ifdef JAPANESE +//BG if(!IsPicture) { +//BG #else + width = MAX(width, 50 * factor); + width += 40 * factor; +//BG #endif +//BG #ifdef JAPANESE +//BG } +//BG #endif + height += (numbuttons == 0) ? (30 * factor) : (60 * factor); + + int x = (SeenBuff.Get_Width() - width) / 2; + int y = (SeenBuff.Get_Height() - height) / 2; + + /* + ** Other inits. + */ + Set_Logic_Page(SeenBuff); + VisiblePage.Blit(seen_buff_save); + + /* + ** Initialize the button structures. All are initialized, even though one (or none) may + ** actually be added to the button list. + */ + TextButtonClass button1(BUTTON_1, b1txt, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW, + x + ((numbuttons == 1) ? ((width - bwidth) >> 1) : (10*factor)), y + height - (bheight + (5*factor)), bwidth); + + TextButtonClass button2(BUTTON_2, b2txt, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW, + x + width - (bwidth + (10*factor)), y + height - (bheight + (5*factor)), bwidth); + + TextButtonClass button3(BUTTON_3, b3txt, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW, 0, y + height - (bheight + (5*factor))); + button3.X = x + ((width - button3.Width) >> 1); + + TextButtonClass * buttonlist = 0; + curbutton = 0; + + /* + ** Add and inialize the buttons to the button list. + */ + if (numbuttons) { + buttonlist = &button1; + buttons[0] = &button1; + realval[0] = BUTTON_1; + if (numbuttons > 2) { + button3.Add(*buttonlist); + buttons[1] = &button3; + realval[1] = BUTTON_3; + button2.Add(*buttonlist); + buttons[2] = &button2; + realval[2] = BUTTON_2; + buttons[curbutton]->Turn_On(); + } else if (numbuttons == 2) { + button2.Add(*buttonlist); + buttons[1] = &button2; + realval[1] = BUTTON_2; + buttons[curbutton]->Turn_On(); + } + } + + /* + ** Draw the dialog. + */ + Hide_Mouse(); + if (preserve) { + back = new char[width * height]; + SeenBuff.To_Buffer(x, y, width, height, back, width * height); + } + //display = TRUE; +#ifdef JAPANESE + if(IsPicture) { + Load_Uncompress(CCFileClass(msg), SysMemPage, SysMemPage); + SysMemPage.Blit(SeenBuff, 160, 100); + } else { +#endif + Dialog_Box(x, y, width, height); + Draw_Caption(Caption, x, y, width); + + /* + ** Draw the caption. + */ + Fancy_Text_Print(buffer, x + (20*factor), y + (25*factor), CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); +#ifdef JAPANESE + } +#endif + /* + ** Redraw the buttons. + */ + if (buttonlist) { + buttonlist->Draw_All(); + } + Show_Mouse(); + + /* + ** Main Processing Loop. + */ + if (buttonlist) { + process = true; + pressed = false; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + seen_buff_save.Blit(VisiblePage); + display = TRUE; + } + + if (display){ + display = FALSE; + + Hide_Mouse(); + +#ifdef JAPANESE + if(IsPicture) { + Load_Uncompress(CCFileClass(msg), SysMemPage, SysMemPage); + SysMemPage.Blit(SeenBuff, 160, 100); + } else { +#endif + Dialog_Box(x, y, width, height); + Draw_Caption(Caption, x, y, width); + + /* + ** Draw the caption. + */ + Fancy_Text_Print(buffer, x + (20 * factor), y + (25 * factor), CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); +#ifdef JAPANESE + } +#endif + /* + ** Redraw the buttons. + */ + if (buttonlist) { + buttonlist->Draw_All(); + } + Show_Mouse(); + } + + /* + ** Invoke game callback. + */ + Call_Back(); + + /* + ** Fetch and process input. + */ + input = buttonlist->Input(); + switch (input) { + case (BUTTON_1|BUTTON_FLAG): + selection = realval[0]; + pressed = true; + break; + + case (KN_ESC): + if (numbuttons > 2) { + selection = realval[1]; + pressed = true; + } else { + selection = realval[2]; + pressed = true; + } + break; + + + case (BUTTON_2|BUTTON_FLAG): + selection = realval[1]; + pressed = true; + break; + + case (BUTTON_3|BUTTON_FLAG): + selection = realval[2]; + pressed = true; + break; + + case (KN_LEFT): + if (numbuttons > 1) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton--; + if (curbutton < 0) { + curbutton = numbuttons - 1; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RIGHT): + if (numbuttons > 1) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton++; + if (curbutton > (numbuttons - 1) ) { + curbutton = 0; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_1; + pressed = true; + break; + + /* + ** Check 'input' to see if it's the 1st char of button text + */ + default: +#ifdef NEVER + if (b1char == toupper(Keyboard::To_ASCII((KeyNumType)(input & 0xFF)))) { + selection = BUTTON_1; + pressed = true; + } else if (b2txt!=NULL && + b2char == toupper(Keyboard::To_ASCII((KeyNumType)(input & 0xFF)))) { + selection = BUTTON_2; + pressed = true; + } +#endif + break; + } + + if (pressed) { + switch (selection) { + case (BUTTON_1): + retval = 0; + process = false; + break; + + case (BUTTON_2): + retval = 1; + process = false; + break; + + case BUTTON_3: + retval = 2; + process = false; + break; + } + + pressed = false; + } + } + + } else { + + /* + ** Delay for a brief moment so that the dialog box will be visible long + ** enough to read the text. + */ + CountDownTimerClass timer(BT_SYSTEM, 0); + timer.Start(); + timer.Set(TICKS_PER_SECOND*4); + while (timer.Time() > 0) { + Call_Back(); + } + Keyboard::Clear(); + } + + /* + ** Restore the screen if necessary. + */ + if (preserve) { + Hide_Mouse(); + if (SeenBuff.Lock()){ + Buffer_To_Page(x, y, width, height, back, &SeenBuff); + } + SeenBuff.Unlock(); + delete[] back; + back = NULL; + Show_Mouse(); + } + return(retval); +} + + +/*********************************************************************************************** + * CCMessageBox::Process -- this one takes integer text arguments * + * * + * INPUT: * + * msg message to display * + * b1txt text for 1st button * + * b2txt text for 2nd button; NULL = no 2nd button * + * * + * OUTPUT: * + * # of button selected (0 = 1st) * + * * + * WARNINGS: * + * 'msg' text must be <= 38 chars * + * 'b1txt' and 'b2txt' must each be <= 18 chars * + * * + * HISTORY: * + * 12/12/1994 BR : Created. * + * 06/18/1995 JLB : Simplified. * + *=============================================================================================*/ +int CCMessageBox::Process(int msg, int b1txt, int b2txt, int b3txt, bool preserve) +{ + return(Process(Text_String(msg), b1txt, b2txt, b3txt, preserve)); +} + + +/*********************************************************************************************** + * CCMessageBox::Process -- Displays message box. * + * * + * This routine will display a message box and wait for player input. It varies from the * + * other message box functions only in the type of parameters it takes. * + * * + * INPUT: msg -- Pointer to text string for the message itself. * + * * + * b1txt -- Text number for the "ok" button. * + * * + * b2txt -- Text number for the "cancel" button. * + * * + * OUTPUT: Returns with the button selected. "true" if "OK" was pressed, and "false" if * + * "CANCEL" was pressed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1995 JLB : Created. * + *=============================================================================================*/ +int CCMessageBox::Process(char const *msg, int b1txt, int b2txt, int b3txt, bool preserve) +{ + return(Process(msg, Text_String(b1txt), Text_String(b2txt), Text_String(b3txt), preserve)); +} diff --git a/MSGBOX.H b/MSGBOX.H new file mode 100644 index 0000000..c4e8e09 --- /dev/null +++ b/MSGBOX.H @@ -0,0 +1,60 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\msgbox.h_v 2.17 16 Oct 1995 16:47:50 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : June 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MSGBOX_H +#define MSGBOX_H + +#include "jshell.h" + +class CCMessageBox +{ + int Caption; +#ifdef JAPANESE + bool IsPicture; +#endif + public: +#ifdef JAPANESE + CCMessageBox(int caption=TXT_NONE, bool pict=false); +#else + CCMessageBox(int caption=TXT_NONE) {Caption = caption;}; +#endif + int Process(const char *msg, const char *b1txt, const char *b2txt=NULL, const char *b3txt=NULL, bool preserve=false); + int Process(int msg, int b1txt=TXT_OK, int b2txt=TXT_NONE, int b3txt=TXT_NONE, bool preserve=false); + int Process(char const *msg, int b1txt=TXT_OK, int b2txt=TXT_NONE, int b3txt=TXT_NONE, bool preserve=false); +}; + +#endif diff --git a/MSGLIST.CPP b/MSGLIST.CPP new file mode 100644 index 0000000..ebb4ba4 --- /dev/null +++ b/MSGLIST.CPP @@ -0,0 +1,808 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\msglist.cpv 1.4 16 Oct 1995 16:48:20 JOE_BOSTIC $ */ +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MSGLIST.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 05/22/95 * + * * + * Last Update : June 26, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * MessageListClass::Add_Edit -- Adds editable string to message list * + * MessageListClass::Add_Message -- displays the given message * + * MessageListClass::Draw -- Draws the messages * + * MessageListClass::Get_Edit_Buf -- gets edit buffer * + * MessageListClass::Init -- Inits message system, sets options * + * MessageListClass::Input -- Handles input for sending messages * + * MessageListClass::Manage -- Manages multiplayer messages * + * MessageListClass::MessageListClass -- constructor * + * MessageListClass::~MessageListClass -- destructor * + * MessageListClass::Num_Messages -- returns # messages in the list * + * MessageListClass::Set_Width -- sets allowable width of messages * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +char MessageListClass::MessageBuffers[MAX_NUM_MESSAGES][MAX_MESSAGE_LENGTH + 30]; +char MessageListClass::BufferAvail[MAX_NUM_MESSAGES]; + +/*************************************************************************** + * MessageListClass::MessageListClass -- constructor * + * * + * INPUT: * + * x,y coord of upper-left of top message * + * max_msg max messages allowed, including edit message * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/21/1995 BRR : Created. * + *=========================================================================*/ +MessageListClass::MessageListClass(void) +{ + int i; + + MessageList = 0; + MessageX = 0; + MessageY = 0; + MaxMessages = 0; + MaxChars = 0; + Height = 0; + EditLabel = 0; + EditBuf = 0; + EditCurPos = 0; + EditInitPos = 0; + + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + BufferAvail[i] = 1; + } + +} + + +/*************************************************************************** + * MessageListClass::~MessageListClass -- destructor * + * * + * INPUT: * + * x,y coord of upper-left of top message * + * max_msg max messages allowed, including edit message * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/21/1995 BRR : Created. * + *=========================================================================*/ +MessageListClass::~MessageListClass() +{ + Init(0,0,0,0,0); +} + + +/*************************************************************************** + * MessageListClass::Init -- Inits message system, sets options * + * * + * INPUT: * + * x,y coord of upper-left of top message * + * max_msg max messages allowed, including edit message * + * maxchars max # characters allowed per message * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/21/1995 BRR : Created. * + *=========================================================================*/ +void MessageListClass::Init(int x, int y, int max_msg, int maxchars, int height) +{ + TextLabelClass *txtlabel; + int i; + + /*------------------------------------------------------------------------ + Remove every entry in the list + ------------------------------------------------------------------------*/ + txtlabel = MessageList; + while (txtlabel) { + MessageList = (TextLabelClass *)txtlabel->Remove(); + delete txtlabel; + txtlabel = MessageList; + } + + /*------------------------------------------------------------------------ + Mark all buffers as available + ------------------------------------------------------------------------*/ + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + BufferAvail[i] = 1; + } + + /*------------------------------------------------------------------------ + Init variables + ------------------------------------------------------------------------*/ + MessageList = 0; + MessageX = x; + MessageY = y; + + MaxMessages = max_msg; + if (MaxMessages > MAX_NUM_MESSAGES) + MaxMessages = MAX_NUM_MESSAGES; + + MaxChars = maxchars; + if (MaxChars > MAX_MESSAGE_LENGTH) + MaxChars = MAX_MESSAGE_LENGTH; + + Height = height; + EditLabel = 0; + EditBuf = 0; + EditCurPos = 0; + EditInitPos = 0; +} + + +/*************************************************************************** + * MessageListClass::Add_Message -- displays the given message * + * * + * INPUT: * + * txt text to display * + * color color to draw text in * + * style style to use * + * timeout # of ticks the thing is supposed to last (-1 = forever)* + * * + * OUTPUT: * + * ptr to new TextLabelClass object. * + * * + * WARNINGS: * + * The TextLabelClass's text buffer is free'd when the class is free'd, * + * so never pass it a static buffer. * + * * + * HISTORY: * + * 05/05/1995 BRR : Created. * + *=========================================================================*/ +TextLabelClass * MessageListClass::Add_Message(char *txt, int color, + TextPrintType style, int timeout, unsigned short magic_number, unsigned short crc) +{ + int num_msg; + TextLabelClass *txtlabel; + int x,y; + GadgetClass *gadg; + int i,j; + int found; + int position; + char *raw_string; + char *current_string; + char *s1,*s2; + bool same; +#if (0) +#if (GERMAN) + static int from_adjust = -1; +#else +#if (FRENCH) + static int from_adjust = -2; +#else + static int from_adjust = 0; +#endif +#endif +#endif //(0) + + /*------------------------------------------------------------------------ + Prevent a duplicate message. (The IPXManager Global Channel cannot detect + a resend of a packet, so sometimes two identical messages appear in a row.) + ------------------------------------------------------------------------*/ + if (MessageList) { + txtlabel = MessageList; + while (txtlabel) { + /* + ** Dont check for duplicates in multi-segment strings + */ + if (!txtlabel->Segments){ + if (!strcmp (txtlabel->Text,txt) && txtlabel->Color == color && + txtlabel->Style == style) { + return(txtlabel); + } + } + txtlabel = (TextLabelClass *)txtlabel->Get_Next(); + } + } + + + /* + ** If the magic number is a valid message tail then see if we + ** can add this message to the tail of an existing message (crc's must also match) + */ + if (magic_number > MESSAGE_HEAD_MAGIC_NUMBER && + magic_number < MESSAGE_HEAD_MAGIC_NUMBER+MAX_MESSAGE_SEGMENTS){ + + position = magic_number - MESSAGE_HEAD_MAGIC_NUMBER; + txtlabel = MessageList; + + while (txtlabel) { + if (txtlabel->Color == color && txtlabel->Style == style && txtlabel->CRC == crc) { + + same = true; + + s1 = strchr(txtlabel->Text, ':'); + s2 = strchr(txt, ':'); + + if (s1 && s2){ + *s1 = 0; + *s2 = 0; + + same = !strcmp (txtlabel->Text, txt); + + *s1 = ':'; + *s2 = ':'; + } + + if (same){ + + /* + ** If this message segment hasnt already come through then add it to the existing text + */ + if (! (txtlabel->Segments & (1 << position)) ) { + /* + ** Search for the ':' to find the actual message after the players name + */ + raw_string = s2; + current_string = s1; + if (raw_string++ && current_string++){ + memcpy (current_string + (position*(COMPAT_MESSAGE_LENGTH-5))/*+from_adjust*/, raw_string, COMPAT_MESSAGE_LENGTH-4); + /* + ** Flag this string segment as complete + */ + txtlabel->Segments |= 1<Get_Next(); + } + } + + + + /*------------------------------------------------------------------------ + Count the # of messages; if MaxMessages is going to be exceeded, remove + the top-most message. + ------------------------------------------------------------------------*/ + num_msg = 0; + if (MessageList) { + gadg = MessageList; + while (gadg) { + num_msg++; + gadg = gadg->Get_Next(); + } + } + /*........................................................................ + Remove the top-most message, but don't remove the edit message. + ........................................................................*/ + if ( (MaxMessages > 0) && ((num_msg + 1) > MaxMessages)) { + txtlabel = MessageList; + /*..................................................................... + If the top label is the edit label, go to the next one; if there is + no next one, just return. + .....................................................................*/ + if (txtlabel == EditLabel) + txtlabel = (TextLabelClass *)txtlabel->Get_Next(); + if (txtlabel==NULL) + return(NULL); + + /*..................................................................... + Remove this message from the list; mark its buffer as being available. + .....................................................................*/ + MessageList = (TextLabelClass *)txtlabel->Remove(); + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + if (txtlabel->Text == MessageBuffers[i]) + BufferAvail[i] = 1; + } + delete txtlabel; + + /*..................................................................... + Recompute everyone's y-coordinate + .....................................................................*/ + y = MessageY; + if (MessageList) { + gadg = MessageList; + while (gadg) { + gadg->Y = y; + gadg = gadg->Get_Next(); + y += Height; + } + } + } + + /*------------------------------------------------------------------------ + Figure out the message's y-coordinate; put it below the other messages + ------------------------------------------------------------------------*/ + x = MessageX; + y = MessageY; + if (MessageList) { + gadg = MessageList; + while (gadg) { + gadg = gadg->Get_Next(); + y += Height; + } + } + + /*------------------------------------------------------------------------ + Create the message + ------------------------------------------------------------------------*/ + txtlabel = new TextLabelClass (txt, x, y, color, style); + if (timeout==-1) { + txtlabel->UserData = 0; + } else { + txtlabel->UserData = TickCount.Time() + timeout; + } + + /*------------------------------------------------------------------------ + Find a buffer to store our message in; if there are none, don't add the + message. + ------------------------------------------------------------------------*/ + found = 0; + txtlabel->Segments = 0; + txtlabel->CRC = crc; + + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + if (BufferAvail[i]) { + BufferAvail[i] = 0; + memset (MessageBuffers[i],0,MAX_MESSAGE_LENGTH + 30); + strcpy (MessageBuffers[i],txt); + + /* + ** If this is a segment from a larger message then put it in the right place + ** in the buffer and clear out the rest with spaces + */ + if (magic_number >= MESSAGE_HEAD_MAGIC_NUMBER && + magic_number < MESSAGE_HEAD_MAGIC_NUMBER+MAX_MESSAGE_SEGMENTS){ + raw_string = strchr(txt, ':'); + char *dest_str = strchr(MessageBuffers[i], ':'); + if (dest_str){ + dest_str++; + }else{ + dest_str = MessageBuffers[i]; + } + + if (raw_string++){ + for (j=0 ; j<3 ; j++){ + if (! ((magic_number - j) == MESSAGE_HEAD_MAGIC_NUMBER)){ + memset (dest_str + j*(COMPAT_MESSAGE_LENGTH-4)/*+from_adjust*/, 32, COMPAT_MESSAGE_LENGTH-4); + }else{ + strcpy(dest_str + j*(COMPAT_MESSAGE_LENGTH-4)/*+from_adjust*/, raw_string); + } + } + *(dest_str +((COMPAT_MESSAGE_LENGTH-4)*MAX_MESSAGE_SEGMENTS-1)) = 0; + + } + position = magic_number - MESSAGE_HEAD_MAGIC_NUMBER; + txtlabel->Segments = 1<Text = MessageBuffers[i]; + found = 1; + break; + } + } + if (!found) { + delete txtlabel; + return (NULL); + } + + /*------------------------------------------------------------------------ + Attach the message to our list + ------------------------------------------------------------------------*/ + if (MessageList) { + txtlabel->Add_Tail (*MessageList); + } else { + MessageList = txtlabel; + } + + return(txtlabel); +} + + +/*************************************************************************** + * MessageListClass::Add_Edit -- Adds editable string to message list * + * * + * INPUT: * + * color color of edit message * + * style style of edit message * + * to string: who to send to * + * width width of editbox in pixels * + * * + * OUTPUT: * + * ptr to new TextLabelClass * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/22/1995 BRR : Created. * + *=========================================================================*/ +TextLabelClass * MessageListClass::Add_Edit(int color, TextPrintType style, + char *to, int width) +{ + /*------------------------------------------------------------------------ + Do nothing if we're already in "edit" mode + ------------------------------------------------------------------------*/ + if (EditLabel) + return(NULL); + + /*------------------------------------------------------------------------ + Initialize the buffer positions; add a new label to the label list. + ------------------------------------------------------------------------*/ + EditCurPos = EditInitPos = strlen(to); + EditLabel = Add_Message (to, color, style, -1, 0, 0); + Width = width; + + /*------------------------------------------------------------------------ + Save our edit buffer pointer. + ------------------------------------------------------------------------*/ + if (EditLabel) + EditBuf = EditLabel->Text; + else + EditBuf = NULL; + + return(EditLabel); +} + + +/*************************************************************************** + * MessageListClass::Get_Edit_Buf -- gets edit buffer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * ptr to edit buffer, minus the "To:" header * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/21/1995 BRR : Created. * + *=========================================================================*/ +char * MessageListClass::Get_Edit_Buf(void) +{ + if (!EditBuf) + return(NULL); + + return(EditBuf + EditInitPos); +} + + +/*************************************************************************** + * MessageListClass::Manage -- Manages multiplayer messages * + * * + * If this routine returns TRUE, the caller should update the display. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * 0 = no change has occurred, 1 = changed * + * * + * HISTORY: * + * 05/05/1995 BRR : Created. * + *=========================================================================*/ +int MessageListClass::Manage (void) +{ + TextLabelClass *txtlabel; + TextLabelClass *next; + int changed = 0; + int y; + GadgetClass *gadg; + int i; + + /*------------------------------------------------------------------------ + Loop through all messages + ------------------------------------------------------------------------*/ + txtlabel = MessageList; + while (txtlabel) { + /*..................................................................... + If this message's time is up, remove it from the list + .....................................................................*/ + if (txtlabel->UserData != 0 && TickCount.Time() > txtlabel->UserData) { + /*.................................................................. + If we're about to delete the edit message, clear our edit message + values. + ..................................................................*/ + if (txtlabel == EditLabel) { + EditLabel = 0; + EditBuf = 0; + } + /*.................................................................. + Save the next ptr in the list; remove this entry + ..................................................................*/ + next = (TextLabelClass *)txtlabel->Get_Next(); + MessageList = (TextLabelClass *)txtlabel->Remove(); + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + if (txtlabel->Text == MessageBuffers[i]) + BufferAvail[i] = 1; + } + delete txtlabel; + changed = 1; + txtlabel = next; + } else { + txtlabel = (TextLabelClass *)txtlabel->Get_Next(); + } + } + + /*------------------------------------------------------------------------ + If a changed has been made, recompute the y-coord of all messages + ------------------------------------------------------------------------*/ + if (changed) { + + y = MessageY; + if (MessageList) { + gadg = MessageList; + while (gadg) { + gadg->Y = y; + gadg = gadg->Get_Next(); + y += Height; + } + } + } + + return(changed); +} + + +/*************************************************************************** + * MessageListClass::Input -- Handles input for sending messages * + * * + * INPUT: * + * input key value to process * + * * + * OUTPUT: * + * 1 = caller should redraw the message list (no need to complete * + * refresh, though) * + * 2 = caller should completely refresh the display. * + * 3 = caller should send the edit message. * + * (sets 'input' to 0 if it processes it.) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/05/1995 BRR : Created. * + *=========================================================================*/ +int MessageListClass::Input(KeyNumType &input) +{ + KeyASCIIType ascii; + int retcode = 0; + + /*------------------------------------------------------------------------ + Do nothing if nothing to do. + ------------------------------------------------------------------------*/ + if (input == KN_NONE) + return(0); + + /*------------------------------------------------------------------------ + Leave mouse events alone. + ------------------------------------------------------------------------*/ + if ( (input & (~KN_RLSE_BIT))==KN_LMOUSE || + (input & (~KN_RLSE_BIT))==KN_RMOUSE) + return(0); + + /*------------------------------------------------------------------------ + If we're in 'edit mode', handle keys + ------------------------------------------------------------------------*/ + if (EditLabel) { + ascii = (KeyASCIIType)(Keyboard::To_ASCII(input) & 0x00ff); + + /* + ** Allow numeric keypad presses to map to ascii numbers + */ + if ((input & WWKEY_VK_BIT) && ascii >='0' && ascii <= '9') { + + input = (KeyNumType)(input & ~WWKEY_VK_BIT); + + } else { + /* + ** Filter out all special keys except return, escape and backspace + */ + if ((!(input & WWKEY_VK_BIT) && !(input & KN_BUTTON) + && ascii >= ' ' && ascii <= 127) + || (input & 0xff)== (KN_RETURN & 0xff) + || (input & 0xff)== (KN_BACKSPACE & 0xff) + || (input & 0xff)== (KN_ESC & 0xff) ) { + + //ascii = (KeyASCIIType)(Keyboard->To_ASCII(input)); + } else { + input = KN_NONE; + return (0); + } + } + + + switch (ascii) { + /*------------------------------------------------------------------ + ESC = abort message + ------------------------------------------------------------------*/ + case KA_ESC & 0xff: + EditLabel->UserData = 1; // force it to be removed + input = KN_NONE; + break; + + /*------------------------------------------------------------------ + RETURN = send the message + ------------------------------------------------------------------*/ + case KA_RETURN & 0xff: + EditLabel->UserData = 1; // force it to be removed + retcode = 3; + input = KN_NONE; + break; + + /*------------------------------------------------------------------ + BACKSPACE = remove a character + ------------------------------------------------------------------*/ + case KA_BACKSPACE & 0xff: + if (EditCurPos > EditInitPos) { + EditCurPos--; + EditBuf[EditCurPos] = 0; + retcode = 2; + } + input = KN_NONE; + break; + + /*------------------------------------------------------------------ + default: add a character. Reserve the last buffer position for null. + (EditCurPos - EditInitPos) is the buffer index # of the next + character, after the "To:" prefix. + ------------------------------------------------------------------*/ + default: + if ( (EditCurPos - EditInitPos) < (MaxChars - 1) ) { + if (!(input & WWKEY_VK_BIT) && ascii >= ' ' && ascii <= 127) { + EditBuf[EditCurPos] = ascii; + EditCurPos++; + retcode = 1; + + /* + ** Verify that the additional character would not overrun the on screen edit box. + */ + Fancy_Text_Print(TXT_NONE, 0, 0, EditLabel->Color, TBLACK, EditLabel->Style); + int width = String_Pixel_Width(EditBuf); + if (width >= Width){ + EditBuf[EditCurPos--] = 0; + retcode = 0; + } + + } + } + input = KN_NONE; + break; + } + } + + return(retcode); +} + + +/*************************************************************************** + * MessageListClass::Draw -- draws messages * + * * + * INPUT: * + * none * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/22/1995 BRR : Created. * + *=========================================================================*/ +void MessageListClass::Draw(void) +{ + if (MessageList) { + Hide_Mouse(); + MessageList->Draw_All(); + Show_Mouse(); + } +} + + +/*************************************************************************** + * MessageListClass::Num_Messages -- returns # messages in the list * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # of messages * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/26/1995 BRR : Created. * + *=========================================================================*/ +int MessageListClass::Num_Messages(void) +{ + GadgetClass *gadg; + int num; + + num = 0; + + if (MessageList) { + gadg = MessageList; + while (gadg) { + num++; + gadg = gadg->Get_Next(); + } + } + + return (num); +} + + +/*************************************************************************** + * MessageListClass::Set_Width -- sets allowable width of messages * + * * + * INPUT: * + * width pixel width * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/26/1995 BRR : Created. * + *=========================================================================*/ +void MessageListClass::Set_Width(int width) +{ + GadgetClass *gadg; + + if (MessageList) { + gadg = MessageList; + while (gadg) { + ((TextLabelClass *)gadg)->PixWidth = width; + gadg = gadg->Get_Next(); + } + } +} + diff --git a/MSGLIST.H b/MSGLIST.H new file mode 100644 index 0000000..6755765 --- /dev/null +++ b/MSGLIST.H @@ -0,0 +1,109 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MSGLIST.H * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 05/22/95 * + * * + * Last Update : May 22, 1995 [BRR] * + * * + * How the messages work: * + * - MPlayerMessageList is a gadget list of all current messages * + * - MPlayerMessageX & Y are the upper left corner of the 1st message * + * - MPlayerMaxMessages is the max # of messages allowed, including * + * the editable message; 0 = no limit. * + * - EditLabel points to the textmessage gadget for the current editable * + * field. EditBuf points to the char buffer being edited. EditInitPos * + * & EditCurPos define buffer index positions. * + * - EditSendAddress is the IPX Address to send the message to when RETURN * + * is pressed. * + * * + * The UserData field in the TextLabelClass tells what the timeout for * + * each message is (0 = none). * + * When a message's timeout expires, it's deleted. When a new message * + * is added, the top message is deleted if MPlayerMaxMessages is exceeded. * + * * + * The Edit-able message is never deleted until ESC or RETURN is pressed. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MSGLIST_H +#define MSGLIST_H + +/* +** Class declaration +*/ +class MessageListClass { + public: + /* + ** Constructor/Destructor + */ + MessageListClass (void); + ~MessageListClass (); + + /* + ** Initialization + */ + void Init (int x, int y, int max_msg, int maxchars, int height); + TextLabelClass * Add_Message (char *txt, int color, TextPrintType style, int timeout, + unsigned short magic_number, unsigned short crc); + + /* + ** Message-editing routines + */ + TextLabelClass * Add_Edit (int color, TextPrintType style, char *to, int width); + char * Get_Edit_Buf (void); + + /* + ** Maintenance routines + */ + int Manage (void); + int Input (KeyNumType &input); + void Draw(void); + int Num_Messages(void); + void Set_Width(int width); + + private: + TextLabelClass * MessageList; // list of messages + int MessageX; // x-coord of upper-left + int MessageY; // y-coord of upper-left + int MaxMessages; // max messages allowed + int MaxChars; // max allowed chars per message + int Height; // height in pixels + TextLabelClass *EditLabel; // ptr to current edit label + char *EditBuf; // ptr to current edit buffer + int EditCurPos; // current edit position + int EditInitPos; // initial edit position + int Width; // Maximum width in pixels of editable string + + /* + ** Static buffers provided for messages. They must be long enough for + ** both the message, and for the "To" prefix on edited messages, or + ** the "From:" prefix on received messages. + */ + static char MessageBuffers[MAX_NUM_MESSAGES][MAX_MESSAGE_LENGTH + 30]; + static char BufferAvail[MAX_NUM_MESSAGES]; +}; + +#endif diff --git a/NETDLG.CPP b/NETDLG.CPP new file mode 100644 index 0000000..d0122c5 --- /dev/null +++ b/NETDLG.CPP @@ -0,0 +1,5431 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\netdlg.cpv 2.17 16 Oct 1995 16:52:26 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NETDLG.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : January 23, 1995 * + * * + * Last Update : July 8, 1995 [BRR] * + * * + *---------------------------------------------------------------------------------------------* + * * + * These routines establish & maintain peer-to-peer connections between this system * + * and all others in the game. Each system finds out the IPX address of the others, * + * and forms a direct connection (IPXConnectionClass) to that system. Systems are * + * found out via broadcast queries. Every system broadcasts its queries, and every * + * system replies to queries it receives. At the point when the game owner signals * + * 'OK', every system must know about all the other systems in the game. * + * * + * How Bridges are handled: * + * Currently, bridges are handled by specifying the destination IPX address of the * + * "server" (game owner's system) on the command-line. This address is used to * + * derive a broadcast address to that destination network, and this system's queries * + * are broadcast over its network & the server's network; replies to the queries come * + * with each system's IPX address attached, so once we have the address, we can form * + * a connection with any system on the bridged net. * + * * + * The flaw in this plan is that we can only cross one bridge. If there are 3 nets * + * bridged (A, B, & C), and the server is on net B, and we're on net A, our broadcasts * + * will reach nets A & B, but not C. The way to circumvent this (if it becomes a problem) * + * would be to have the server tell us what other systems are in its game, not each * + * individual player's system. Thus, each system would find out about all the other systems * + * by interacting with the game's owner system (this would be more involved than what * + * I'm doing here). * + * * + * Here's a list of all the different packets sent over the Global Channel: * + * * + * NET_QUERY_GAME * + * (no other data) * + * NET_ANSWER_GAME * + * Name: game owner's name * + * GameInfo: game's version & open state * + * NET_QUERY_PLAYER * + * Name: name of game we want players to respond for * + * NET_ANSWER_PLAYER * + * Name: player's name * + * PlayerInfo: info about player * + * NET_QUERY_JOIN * + * Name: name of player wanting to join * + * PlayerInfo: player's requested house & color * + * NET_CONFIRM_JOIN * + * PlayerInfo: approves player's house & color * + * NET_REJECT_JOIN * + * (no other data) * + * NET_GAME_OPTIONS * + * ScenarioInfo: info about scenario * + * NET_SIGN_OFF * + * Name: name of player signing off * + * NET_PING * + * (no other data) * + * NET_GO * + * Delay: value of one-way response time, in frames * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Clear_Game_List -- Clears the game-name listbox & 'Games' Vector * + * Clear_Player_List -- Clears the player-name listbox & Vector * + * Destroy_Connection -- destroys the given connection * + * Get_Join_Responses -- sends queries for the Join Dialog * + * Get_NewGame_Responses -- processes packets for New Game dialog * + * Init_Network -- initializes network stuff * + * Net_Join_Dialog -- lets user join an existing game, or start a new one * + * Net_New_Dialog -- lets user start a new game * + * Process_Global_Packet -- responds to remote queries * + * Remote_Connect -- handles connecting this user to others * + * Request_To_Join -- Sends a JOIN request packet to game owner * + * Send_Join_Queries -- sends queries for the Join Dialog * + * Shutdown_Network -- shuts down network stuff * + * Compute_Name_CRC -- computes CRC from char string * + * Net_Reconnect_Dialog -- Draws/updates the network reconnect dialog * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include +#include "tcpip.h" +#include "ccdde.h" +#define SHOW_MONO 0 + + +#ifndef DEMO + +/*--------------------------------------------------------------------------- +The possible states of the join-game dialog +---------------------------------------------------------------------------*/ +typedef enum { + JOIN_REJECTED = -1, // we've been rejected + JOIN_NOTHING, // we're not trying to join a game + JOIN_WAIT_CONFIRM, // we're asking to join, & waiting for confirmation + JOIN_CONFIRMED, // we've been confirmed + JOIN_GAME_START, // the game we've joined is starting +} JoinStateType; + +/*--------------------------------------------------------------------------- +The possible return codes from Get_Join_Responses() +---------------------------------------------------------------------------*/ +typedef enum { + EV_NONE, // nothing happened + EV_STATE_CHANGE, // Join dialog is in a new state + EV_NEW_GAME, // a new game was detected + EV_NEW_PLAYER, // a new player was detected + EV_PLAYER_SIGNOFF, // a player has signed off + EV_GAME_SIGNOFF, // a gamed owner has signed off + EV_GAME_OPTIONS, // a game options packet was received + EV_MESSAGE, // a message was received +} JoinEventType; + + +/* +******************************** Prototypes ********************************* +*/ +static int Net_Join_Dialog(void); +static void Clear_Game_List (ListClass *gamelist); +static void Clear_Player_List (ListClass *playerlist); +static int Request_To_Join (char *playername, int join_index, ListClass *playerlist, + HousesType house, int color); +static void Send_Join_Queries(int curgame, int gamenow, int playernow); +static JoinEventType Get_Join_Responses(JoinStateType *joinstate, ListClass *gamelist, + ColorListClass *playerlist, int join_index); +static int Net_New_Dialog(void); +static JoinEventType Get_NewGame_Responses(ColorListClass *playerlist); +static int Net_Fake_New_Dialog(void); +static int Net_Fake_Join_Dialog(void); + + +/*********************************************************************************************** + * Init_Network -- initializes network stuff * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = Initialization OK, false = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +bool Init_Network (void) +{ + NetNumType net; + NetNodeType node; + + /*------------------------------------------------------------------------ + This call allocates all necessary queue buffers, allocates Real-mode + memory, and commands IPX to start listening on the Global Channel. + ------------------------------------------------------------------------*/ + if (!Ipx.Init()) + return(false); + + /*------------------------------------------------------------------------ + Allocate our "meta-packet" buffer + ------------------------------------------------------------------------*/ + if (!MetaPacket) { + MetaPacket = new char [sizeof (EventClass) * MAX_EVENTS]; + } + + /*------------------------------------------------------------------------ + Set up the IPX manager to cross a bridge + ------------------------------------------------------------------------*/ + if (!(GameToPlay == GAME_INTERNET)){ + if (IsBridge) { + BridgeNet.Get_Address(net,node); + Ipx.Set_Bridge(net); + } + } + + return(true); + +} /* end of Init_Network */ + + +/*********************************************************************************************** + * Shutdown_Network -- shuts down network stuff * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +void Shutdown_Network (void) +{ + +// +// Note: The thought behind this section of code was that if the program +// terminates early, without an EventClass::EXIT event, it still needs to +// tell the other systems that it's gone, so it would send a SIGN_OFF packet. +// BUT, this causes a sync bug if the systems are running slow and this system +// is running ahead of the others; it will send the NET_SIGN_OFF >>before<< +// the other system execute their EventClass::EXIT event, and the other systems +// will kill the connection at some random Frame # & turn my stuff over to +// the computer possibly at different times. +// BRR, 10/29/96 +// +#if (0) + /*------------------------------------------------------------------------ + Broadcast a sign-off packet, by sending the packet over the Global Channel, + telling the IPX Manager that no ACK is required, and specifying a NULL + destination address. + ------------------------------------------------------------------------*/ + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_SIGN_OFF; + strcpy (GPacket.Name, MPlayerName); + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), 0, NULL); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), 0, NULL); + + if (IsBridge && !Winsock.Get_Connected()) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, &BridgeNet); + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, &BridgeNet); + } + + /*------------------------------------------------------------------------ + Wait for the packets to finish going out (or the Global Channel times out) + ------------------------------------------------------------------------*/ + for (;;) { + if (Ipx.Global_Num_Send()==0) { + break; + } + Ipx.Service(); + } + +#endif //(0) + + /*------------------------------------------------------------------------ + Delete our "meta-packet" + ------------------------------------------------------------------------*/ + delete [] MetaPacket; + MetaPacket = 0; + + /*------------------------------------------------------------------------ + If I was in a game, I'm not now, so clear the game name + ------------------------------------------------------------------------*/ + MPlayerGameName[0] = 0; +} + + +/*********************************************************************************************** + * Process_Global_Packet -- responds to remote queries * + * * + * The only commands from other systems this routine responds to are NET_QUERY_GAME * + * and NET_QUERY_PLAYER. The other commands are too context-specific to be able * + * to handle here, such as joining the game or signing off; but this routine handles * + * the majority of the program's needs. * + * * + * INPUT: * + * packet ptr to packet to process * + * address source address of sender * + * * + * OUTPUT: * + * true = packet was processed, false = wasn't * + * * + * WARNINGS: * + * MPlayerName & MPlayerGameName must have been filled in before this function * + * can be called. * + * * + * HISTORY: * + * 02/15/1995 BR : Created. * + *=============================================================================================*/ +bool Process_Global_Packet(GlobalPacketType *packet, IPXAddressClass *address) +{ + GlobalPacketType mypacket; + + /* + ---------------- Another system asking what game this is ----------------- + */ + if (packet->Command==NET_QUERY_GAME && NetStealth==0) { + /*..................................................................... + If the game is closed, let every player respond, and let the sender of + the query sort it all out. This way, if the game's host exits the game, + the game still shows up on other players' dialogs. + If the game is open, only the game owner may respond. + .....................................................................*/ + if (strlen(MPlayerName) > 0 && strlen(MPlayerGameName) > 0 && + ((!NetOpen) || (NetOpen && !strcmp(MPlayerName,MPlayerGameName)))) { + memset (packet, 0, sizeof(GlobalPacketType)); + + mypacket.Command = NET_ANSWER_GAME; + strcpy(mypacket.Name, MPlayerGameName); +#ifdef PATCH + if (IsV107) { + mypacket.GameInfo.Version = 1; + } else { + mypacket.GameInfo.Version = 2; + } +#else + mypacket.GameInfo.Version = Version_Number(); +#endif + mypacket.GameInfo.IsOpen = NetOpen; + + Ipx.Send_Global_Message (&mypacket, sizeof(GlobalPacketType), 1, + address); + } + return(true); + } else { + + /* + ----------------- Another system asking what player I am ----------------- + */ + if (packet->Command==NET_QUERY_PLAYER && + !strcmp (packet->Name, MPlayerGameName) && + (strlen(MPlayerGameName) > 0) && NetStealth==0) { + memset (packet, 0, sizeof(GlobalPacketType)); + + mypacket.Command = NET_ANSWER_PLAYER; + strcpy(mypacket.Name, MPlayerName); + mypacket.PlayerInfo.House = MPlayerHouse; + mypacket.PlayerInfo.Color = MPlayerColorIdx; + mypacket.PlayerInfo.NameCRC = Compute_Name_CRC(MPlayerGameName); + + Ipx.Send_Global_Message (&mypacket, sizeof(GlobalPacketType), 1, address); + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * Destroy_Connection -- destroys the given connection * + * * + * Call this routine when a connection goes bad, or another player signs off. * + * * + * INPUT: * + * id connection ID to destroy * + * error 0 = user signed off; 1 = connection error; otherwise, no error is shown. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/22/1995 BR : Created. * + *=============================================================================================*/ +void Destroy_Connection(int id, int error) +{ + int i,j; + HousesType house; + HouseClass *housep; + char txt[80]; + + /*------------------------------------------------------------------------ + Create a message to display to the user + ------------------------------------------------------------------------*/ + txt[0] = '\0'; + if (error==1) { + sprintf(txt,Text_String(TXT_CONNECTION_LOST),Ipx.Connection_Name(id)); + } else if (error==0) { + sprintf(txt,Text_String(TXT_LEFT_GAME),Ipx.Connection_Name(id)); + } + + if (strlen(txt)) { + Messages.Add_Message (txt, + MPlayerTColors[MPlayerID_To_ColorIndex((unsigned char)id)], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, 0, 0); + Map.Flag_To_Redraw(false); + } + + /*------------------------------------------------------------------------ + Delete the IPX connection, shift the MPlayerID's & MPlayerHouses' back one. + ------------------------------------------------------------------------*/ + Ipx.Delete_Connection(id); + + for (i = 0; i < MPlayerCount; i++) { + if (MPlayerID[i] == (unsigned char)id) { + /*.................................................................. + Turn the player's house over to the computer's AI + ..................................................................*/ + house = MPlayerHouses[i]; + housep = HouseClass::As_Pointer (house); + housep->IsHuman = false; + housep->IsStarted = true; + + /*.................................................................. + Move arrays back by one + ..................................................................*/ + for (j = i; j < MPlayerCount - 1; j++) { + MPlayerID[j] = MPlayerID[j + 1]; + MPlayerHouses[j] = MPlayerHouses[j + 1]; + strcpy (MPlayerNames[j], MPlayerNames[j+1]); + TheirProcessTime[j] = TheirProcessTime[j+1]; + } + } + } + + MPlayerCount--; + + /*------------------------------------------------------------------------ + If we're the last player left, tell the user. + ------------------------------------------------------------------------*/ + if (MPlayerCount == 1) { + sprintf(txt,"%s",Text_String(TXT_JUST_YOU_AND_ME)); + Messages.Add_Message (txt, + MPlayerTColors[MPlayerID_To_ColorIndex((unsigned char)id)], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, 0, 0); + Map.Flag_To_Redraw(false); + } + +} /* end of Destroy_Connection */ + + +/*********************************************************************************************** + * Remote_Connect -- handles connecting this user to others * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = connections established; false = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +bool Remote_Connect(void) +{ + int rc; + int stealth; // original state of NetStealth flag + + /*------------------------------------------------------------------------ + Init network timing parameters; these values should work for both a "real" + network, and a simulated modem network (ie Kali) + ------------------------------------------------------------------------*/ + Ipx.Set_Timing ( 30, // retry 2 times per second + -1, // ignore max retries + 600); // give up after 10 seconds + + /*------------------------------------------------------------------------ + Save the original value of the NetStealth flag, so we can turn stealth + off for now (during this portion of the dialogs, we must show ourselves) + ------------------------------------------------------------------------*/ + stealth = NetStealth; + NetStealth = 0; + + /*------------------------------------------------------------------------ + Init my game name to 0-length, since I haven't joined any game yet. + ------------------------------------------------------------------------*/ + MPlayerGameName[0] = 0; + + /*------------------------------------------------------------------------ + The game is now "open" for joining. Close it as soon as we exit this + routine. + ------------------------------------------------------------------------*/ + NetOpen = 1; + + /*------------------------------------------------------------------------ + Read the default values from the INI file + ------------------------------------------------------------------------*/ + Read_MultiPlayer_Settings (); + + /*------------------------------------------------------------------------ + Keep looping until something useful happens. + ------------------------------------------------------------------------*/ + while (1) { + /*--------------------------------------------------------------------- + Pop up the network Join/New dialog + ---------------------------------------------------------------------*/ + rc = Net_Join_Dialog(); + + /*--------------------------------------------------------------------- + -1 = user selected Cancel + ---------------------------------------------------------------------*/ + if (rc==-1) { + NetStealth = stealth; + NetOpen = 0; + return(false); + } else { + + /*--------------------------------------------------------------------- + 0 = user has joined an existing game; save values & return + ---------------------------------------------------------------------*/ + if (rc==0) { + Write_MultiPlayer_Settings (); + NetStealth = stealth; + NetOpen = 0; + + return(true); + } else { + + /*--------------------------------------------------------------------- + 1 = user requests New Network Game + ---------------------------------------------------------------------*/ + if (rc==1) { + /*.................................................................. + Pop up the New Network Game dialog; if user selects OK, return + 'true'; otherwise, return to the Join Dialog. + ..................................................................*/ + if (Net_New_Dialog()) { + Write_MultiPlayer_Settings (); + NetOpen = 0; + NetStealth = stealth; + NetOpen = 0; + + return(true); + } else { + continue; + } + } + } + } + } +} + + + +/*********************************************************************************************** + * Remote_Connect -- handles connecting this host to the server in an internet game * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = connections established; false = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +bool Server_Remote_Connect(void) +{ + int stealth; // original state of NetStealth flag + + /*------------------------------------------------------------------------ + Init network timing parameters; these values should work for both a "real" + network, and a simulated modem network (ie Kali) + ------------------------------------------------------------------------*/ + Ipx.Set_Timing ( 30, // retry 2 times per second + -1, // ignore max retries + 600); // give up after 10 seconds + + /*------------------------------------------------------------------------ + Save the original value of the NetStealth flag, so we can turn stealth + off for now (during this portion of the dialogs, we must show ourselves) + ------------------------------------------------------------------------*/ + stealth = NetStealth; + NetStealth = 0; + + /*------------------------------------------------------------------------ + The game is now "open" for joining. Close it as soon as we exit this + routine. + ------------------------------------------------------------------------*/ + NetOpen = 1; + + /*------------------------------------------------------------------------ + Read the default values from the INI file + ------------------------------------------------------------------------*/ + Read_MultiPlayer_Settings (); + + if (!Net_Fake_New_Dialog()){ + Write_MultiPlayer_Settings (); + return (false); + } + + NetOpen = 0; + NetStealth = stealth; + Write_MultiPlayer_Settings (); + return (true); +} + + +/*********************************************************************************************** + * Client_Remote_Connect -- handles connecting this client to the server in an internet game * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = connections established; false = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 ST : Created. * + *=============================================================================================*/ +bool Client_Remote_Connect(void) +{ + int rc; + int stealth; // original state of NetStealth flag + + /*------------------------------------------------------------------------ + Init network timing parameters; these values should work for both a "real" + network, and a simulated modem network (ie Kali) + ------------------------------------------------------------------------*/ + Ipx.Set_Timing ( 30, // retry 2 times per second + -1, // ignore max retries + 600); // give up after 10 seconds + + /*------------------------------------------------------------------------ + Save the original value of the NetStealth flag, so we can turn stealth + off for now (during this portion of the dialogs, we must show ourselves) + ------------------------------------------------------------------------*/ + stealth = NetStealth; + NetStealth = 0; + + /*------------------------------------------------------------------------ + The game is now "open" for joining. Close it as soon as we exit this + routine. + ------------------------------------------------------------------------*/ + NetOpen = 1; + + /*------------------------------------------------------------------------ + Read the default values from the INI file + ------------------------------------------------------------------------*/ + Read_MultiPlayer_Settings (); + + /*--------------------------------------------------------------------- + Pop up the network Join/New dialog + ---------------------------------------------------------------------*/ + rc = Net_Fake_Join_Dialog(); + Write_MultiPlayer_Settings (); + + NetStealth = stealth; + NetOpen = 0; + + if (rc == -1) { + return(false); + } else { + return(true); + } +} + + + +/*********************************************************************************************** + * Net_Join_Dialog -- lets user join an existing game or start a new one * + * * + * This dialog displays an edit field for the player's name, and a list of all non-stealth- * + * mode games. Clicking once on a game name displays a list of who's in that game. Clicking * + * "New" takes the user to the Net_New dialog, where he waits for other users to join his * + * game. All other input is done through this dialog. * + * * + * The dialog has several "states": * + * * + * 1) Initially, it waits for the user to fill in his/her name and then to select Join or New; * + * if New is selected, this dialog is exited. * + * * + * 2) If Join is selected, the Join & New buttons are removed, but the Cancel button remains. * + * The join request is transmitted to the game's owner, and the message "Waiting for * + * Confirmation" is displayed, until a confirmation or denial is received from the game's * + * owner. The user may click Cancel at this point to cancel the join request. * + * (Once Join is selected, the name editing field is disabled, and becomes a display-only * + * field. If cancel is selected, it reappears as an edit field.) The user can still click * + * around & see who's in which games. * + * * + * 3) If the join request is denied, the dialog re-initializes to its pre-join state; the * + * Join & New buttons reappear, & the Name field is available again. * + * * + * 4) If join confirmation is obtained, the message just changes to "Confirmed. Waiting for * + * Entry Signal." or some such nonsense. The user can still click around & see who's * + * in which games. * + * * + * Any game running in Stealth mode won't show up on this dialog. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Network Games ³ * + * ³ ³ * + * ³ Your Name: ____________ ³ * + * ³ House: [GDI] [NOD] ³ * + * ³ Desired Color: [ ][ ][ ][ ] ³ * + * ³ ³ * + * ³ Games Players ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³(Bill's Game )³³ ³ Peter Parker GDI ³³ ³ * + * ³ ³ Peter Parker's Game ÃÄ´ ³ Mary Jane GDI ÃÄ´ ³ * + * ³ ³(Magnum PI's Game )³ ³ ³ JJ Jameson NOD ³ ³ ³ * + * ³ ³ ÃÄ´ ³ ÃÄ´ ³ * + * ³ ³ ³³ ³ ³³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ Scenario: Big Long Description ³ * + * ³ Starting Credits: xxxx ³ * + * ³ Count: --- Level: --- ³ * + * ³ Bases: ON Crates: ON ³ * + * ³ Tiberium: ON AI Players: ON ³ * + * ³ ³ * + * ³ [Join] [Cancel] [New] ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ [Send Message] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * -1 = cancel, 0 = OK, 1 = New net game requested * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +static int Net_Join_Dialog(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ +/* ###Change collision detected! C:\PROJECTS\CODE\NETDLG.CPP... */ + int d_dialog_w = 287 *factor; // dialog width + int d_dialog_h = 198*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6*factor+1; // ht of 6-pt text + int d_margin1 = 5*factor; // large margin + int d_margin2 = 2*factor; // small margin + + int d_name_w = 70*factor; + int d_name_h = 9*factor; + int d_name_x = d_dialog_cx - 10*factor; + int d_name_y = d_dialog_y + d_margin1 + d_txt6_h + d_txt6_h; + + int d_gdi_w = 30*factor; + int d_gdi_h = 9*factor; + int d_gdi_x = d_dialog_cx - 10*factor; + int d_gdi_y = d_name_y + d_name_h + d_margin2; + + int d_nod_w = 30*factor; + int d_nod_h = 9*factor; + int d_nod_x = d_gdi_x + d_gdi_w; + int d_nod_y = d_name_y + d_name_h + d_margin2; + + int d_color_w = 10*factor; + int d_color_h = 9*factor; + int d_color_y = d_nod_y + d_nod_h + d_margin2; + + int d_gamelist_w = 160*factor; + int d_gamelist_h = 27*factor; + int d_gamelist_x = d_dialog_x + d_margin1; + int d_gamelist_y = d_color_y + d_color_h + d_margin1 + d_txt6_h; + + int d_playerlist_w = 106*factor; + int d_playerlist_h = 27*factor; + int d_playerlist_x = d_dialog_x + d_dialog_w - d_margin1 - d_playerlist_w; + int d_playerlist_y = d_color_y + d_color_h + d_margin1 + d_txt6_h; + + int d_msg1_y = d_gamelist_y + d_gamelist_h + d_margin1; + int d_msg2_y = d_msg1_y + d_txt6_h; + int d_msg3_y = d_msg2_y + d_txt6_h; + int d_msg4_y = d_msg3_y + d_txt6_h; + int d_msg5_y = d_msg4_y + d_txt6_h; + + int d_join_w = 40*factor; + int d_join_h = 9*factor; + int d_join_x = d_dialog_x + (d_dialog_w / 6) - (d_join_w / 2); + int d_join_y = d_msg5_y + d_txt6_h + d_margin1; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*factor; +#else + int d_cancel_w = 40*factor; +#endif + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_msg5_y + d_txt6_h + d_margin1; + + int d_new_w = 40*factor; + int d_new_h = 9*factor; + int d_new_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_new_w / 2); + int d_new_y = d_msg5_y + d_txt6_h + d_margin1; + + int d_message_w = d_dialog_w - (d_margin1 * 2); + int d_message_h = 34*factor; + int d_message_x = d_dialog_x + d_margin1; + int d_message_y = d_cancel_y + d_cancel_h + d_margin1; + + int d_send_w = 80*factor; + int d_send_h = 9*factor; + int d_send_x = d_dialog_cx - (d_send_w / 2); + int d_send_y = d_message_y + d_message_h + d_margin2; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_NAME = 100, + BUTTON_GDI, + BUTTON_NOD, + BUTTON_GAMELIST, + BUTTON_PLAYERLIST, + BUTTON_JOIN, + BUTTON_CANCEL, + BUTTON_NEW, + BUTTON_SEND, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + int cbox_x[] = { d_gdi_x, + d_gdi_x + d_color_w, + d_gdi_x + (d_color_w * 2), + d_gdi_x + (d_color_w * 3), + d_gdi_x + (d_color_w * 4), + d_gdi_x + (d_color_w * 5)}; + + JoinStateType joinstate = JOIN_NOTHING; // current "state" of this dialog + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int tabs[] = {77*factor}; // tabs for player list box + int game_index = -1; // index of currently-selected game + int join_index = -1; // index of game we're joining + int rc = 0; // -1 = user cancelled, 1 = New + JoinEventType event; // event from incoming packet + int i,j; // loop counter + char txt[80]; + char const *p; + int parms_received; // 1 = game options received + int found; + + unsigned char tmp_id[MAX_PLAYERS]; // temp storage for sorting player ID's + int min_index; // for sorting player ID's + unsigned char min_id; // for sorting player ID's + unsigned char id; // connection ID + char * item; + unsigned long starttime; + + NodeNameType *who; + + int message_length; + int sent_so_far; + unsigned short magic_number; + unsigned short crc; + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + EditClass name_edt (BUTTON_NAME, + namebuf, MPLAYER_NAME_MAX, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_name_x, d_name_y, d_name_w, d_name_h, EditClass::ALPHANUMERIC); + + TextButtonClass gdibtn(BUTTON_GDI, TXT_G_D_I, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_gdi_x, d_gdi_y, d_gdi_w, d_gdi_h); + + TextButtonClass nodbtn(BUTTON_NOD, TXT_N_O_D, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_nod_x, d_nod_y, d_nod_w, d_nod_h); + + ListClass gamelist(BUTTON_GAMELIST, + d_gamelist_x, d_gamelist_y, d_gamelist_w, d_gamelist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + ColorListClass playerlist(BUTTON_PLAYERLIST, + d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + TextButtonClass joinbtn(BUTTON_JOIN, TXT_JOIN, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#ifdef FRENCH + d_join_x, d_join_y); +#else + d_join_x, d_join_y, d_join_w, d_join_h); +#endif + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + TextButtonClass newbtn(BUTTON_NEW, TXT_NEW, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_new_x, d_new_y, d_new_w, d_new_h); + + TextButtonClass sendbtn(BUTTON_SEND, TXT_SEND_MESSAGE, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_send_x, d_send_y); +//#else + d_send_x, d_send_y, d_send_w, d_send_h); +//#endif + + playerlist.Set_Tabs(tabs); + + /* + ----------------------------- Various Inits ------------------------------ + */ + MPlayerColorIdx = MPlayerPrefColor; // init my preferred color + strcpy (namebuf, MPlayerName); // set my name + name_edt.Set_Text(namebuf,MPLAYER_NAME_MAX); + name_edt.Set_Color (MPlayerTColors[MPlayerColorIdx]); + + playerlist.Set_Selected_Style(ColorListClass::SELECT_NONE); + + if (MPlayerHouse==HOUSE_GOOD) { + gdibtn.Turn_On(); + } else { + nodbtn.Turn_On(); + } + + Fancy_Text_Print("", 0, 0, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Messages.Init (d_message_x + 2, d_message_y + 2, 4, + MAX_MESSAGE_LENGTH, d_txt6_h); + + /* + --------------------------- Send network query --------------------------- + */ + Send_Join_Queries (game_index, 1, 0); + + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + + /* + ---------------------------- Init Mono Output ---------------------------- + */ + #if(SHOW_MONO) + Ipx.Configure_Debug(-1, sizeof (GlobalHeaderType), + sizeof(NetCommandType), GlobalPacketNames, 11); + Ipx.Mono_Debug_Print(-1,1); + #endif + while (Get_Mouse_State() > 0) Show_Mouse(); + + /* + ---------------------------- Processing loop ----------------------------- + */ + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + #if(SHOW_MONO) + Ipx.Mono_Debug_Print(-1,0); + #endif + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_JOIN_NETWORK_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(TXT_YOUR_NAME, + d_name_x - 5, d_name_y + 1, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_SIDE_COLON, + d_gdi_x - 5, d_gdi_y + 1, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_COLOR_COLON, + cbox_x[0] - 5, d_color_y + 1, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_GAMES, + d_gamelist_x + (d_gamelist_w / 2), d_gamelist_y - d_txt6_h, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_PLAYERS, + d_playerlist_x + (d_playerlist_w / 2), d_playerlist_y - d_txt6_h, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................... + Join-state-specific labels: + ...............................................................*/ + if (joinstate > JOIN_NOTHING) { + Fancy_Text_Print(namebuf, d_name_x, d_name_y + 1, CC_GREEN, + TBLACK, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + if (MPlayerHouse==HOUSE_GOOD) { + Fancy_Text_Print(TXT_G_D_I, d_gdi_x, d_gdi_y + 1, CC_GREEN, + TBLACK, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } else { + Fancy_Text_Print(TXT_N_O_D, d_gdi_x, d_gdi_y + 1, CC_GREEN, + TBLACK, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + } + + /* + .................... Rebuild the button list .................... + */ + cancelbtn.Zap(); + gamelist.Zap(); + playerlist.Zap(); + gdibtn.Zap(); + nodbtn.Zap(); + name_edt.Zap(); + joinbtn.Zap(); + newbtn.Zap(); + sendbtn.Zap(); + + commands = &cancelbtn; + gamelist.Add_Tail(*commands); + playerlist.Add_Tail(*commands); + /*............................................................... + Only add the name edit field, the House, Join & New buttons if + we're doing nothing, or we've just been rejected. + ...............................................................*/ + if (joinstate <= JOIN_NOTHING) { + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); + name_edt.Add_Tail(*commands); + joinbtn.Add_Tail(*commands); + newbtn.Add_Tail(*commands); + } + if (joinstate == JOIN_CONFIRMED) + sendbtn.Add_Tail(*commands); + } + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + + /*.................................................................. + Draw the color boxes + ..................................................................*/ + if (display >= REDRAW_COLORS) { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + LogicPage->Fill_Rect (cbox_x[i] + 1, d_color_y + 1, + cbox_x[i] + 1 + d_color_w - 2, d_color_y + 1 + d_color_h - 2, + MPlayerGColors[i]); + + if (i == MPlayerColorIdx) { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, + BOXSTYLE_GREEN_DOWN, false); + } else { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, + BOXSTYLE_GREEN_RAISED, false); + } + } + } + + /*.................................................................. + Draw the message: + - Erase an old message first + - If we're in a game, print the game options (if they've been + received) + - If we've been rejected from a game, print that message + ..................................................................*/ + if (display >= REDRAW_MESSAGE) { + Draw_Box(d_message_x, d_message_y, d_message_w, d_message_h, + BOXSTYLE_GREEN_BORDER, true); + Messages.Draw(); + + LogicPage->Fill_Rect( d_dialog_x + 2, + d_msg1_y, + d_dialog_x + d_dialog_w - 4, + d_msg5_y + d_txt6_h, + BLACK); + + if (joinstate==JOIN_CONFIRMED && parms_received) { + /*............................................................ + Scenario title + ............................................................*/ + p = Text_String(TXT_SCENARIO_COLON); + if (ScenarioIdx != -1) { + sprintf(txt,"%s %s",p, MPlayerScenarios[ScenarioIdx]); + + Fancy_Text_Print (txt, d_dialog_cx, + d_msg1_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW | TPF_CENTER); + } else { + sprintf(txt,"%s %s",p,Text_String(TXT_NOT_FOUND)); + + Fancy_Text_Print (txt, d_dialog_cx, + d_msg1_y, CC_NOD_COLOR, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW | TPF_CENTER); + } + + /*............................................................ + # of credits + ............................................................*/ + p = Text_String(TXT_START_CREDITS_COLON); + sprintf(txt, "%s %d", p, MPlayerCredits); + Fancy_Text_Print (txt, d_dialog_cx, + d_msg2_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW | TPF_CENTER); + + /*............................................................ + Count & Level values + ............................................................*/ + p = Text_String(TXT_COUNT); + sprintf(txt,"%s %d",p,MPlayerUnitCount); + Fancy_Text_Print (txt, + d_dialog_x + (d_dialog_w / 4) - String_Pixel_Width(p), + d_msg3_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + p = Text_String(TXT_LEVEL); + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(txt, "%s %d", p, BuildLevel); + } else { + sprintf(txt, "%s **", p); + } + Fancy_Text_Print (txt, + d_dialog_x + d_dialog_w - (d_dialog_w / 4) - String_Pixel_Width(p), + d_msg3_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Bases + ............................................................*/ + p = Text_String(TXT_BASES_COLON); + if (MPlayerBases) { + sprintf(txt,"%s %s",p,Text_String(TXT_ON)); + } else { + sprintf(txt,"%s %s",p,Text_String(TXT_OFF)); + } + Fancy_Text_Print (txt, + d_dialog_x + (d_dialog_w / 4) - String_Pixel_Width(p), + d_msg4_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Tiberium + ............................................................*/ + p = Text_String(TXT_TIBERIUM_COLON); + if (MPlayerTiberium) { + sprintf(txt,"%s %s",p,Text_String(TXT_ON)); + } else { + sprintf(txt,"%s %s",p,Text_String(TXT_OFF)); + } + + Fancy_Text_Print (txt, + d_dialog_x + (d_dialog_w / 4) - String_Pixel_Width(p), + d_msg5_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Goody boxes + ............................................................*/ + p = Text_String(TXT_CRATES_COLON); + if (MPlayerGoodies) { + sprintf(txt,"%s %s",p,Text_String(TXT_ON)); + } else { + sprintf(txt,"%s %s",p,Text_String(TXT_OFF)); + } + + Fancy_Text_Print (txt, + d_dialog_x + d_dialog_w - (d_dialog_w / 4) - String_Pixel_Width(p), + d_msg4_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Computer AI players + ............................................................*/ + if (Special.IsCaptureTheFlag) { + p = Text_String(TXT_CAPTURE_THE_FLAG_COLON); + sprintf(txt,"%s %s",p,Text_String(TXT_ON)); + } else { + p = Text_String(TXT_AI_PLAYERS_COLON); + if (MPlayerGhosts) { + sprintf(txt,"%s %s",p,Text_String(TXT_ON)); + } else { + sprintf(txt,"%s %s",p,Text_String(TXT_OFF)); + } + } + Fancy_Text_Print (txt, + d_dialog_x + d_dialog_w - (d_dialog_w / 4) - String_Pixel_Width(p), + d_msg5_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + } else { + + /*............................................................... + Rejection notice + ...............................................................*/ + if (joinstate==JOIN_REJECTED) { + Fancy_Text_Print(TXT_REQUEST_DENIED, + d_dialog_cx, d_msg3_y, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + } + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + /*------------------------------------------------------------------ + User clicks on a color button: + - If we've joined a game, don't allow a new color selection + - otherwise, select that color + ------------------------------------------------------------------*/ + case KN_LMOUSE: + if (joinstate > JOIN_NOTHING) + break; + if (_Kbd->MouseQX > cbox_x[0] && + _Kbd->MouseQX < (cbox_x[MAX_MPLAYER_COLORS - 1] + d_color_w) && + _Kbd->MouseQY > d_color_y && + _Kbd->MouseQY < (d_color_y + d_color_h)) { + MPlayerPrefColor = (_Kbd->MouseQX - cbox_x[0]) / d_color_w; + MPlayerColorIdx = MPlayerPrefColor; + + name_edt.Set_Color (MPlayerTColors[MPlayerColorIdx]); + name_edt.Flag_To_Redraw(); + + display = REDRAW_COLORS; + } + break; + + /*------------------------------------------------------------------ + User clicks on the game list: + - If we've joined a game, don't allow the selected item to change; + otherwise: + - Clear the player list + - Send an immediate player query + ------------------------------------------------------------------*/ + case (BUTTON_GAMELIST | KN_BUTTON): + if (joinstate==JOIN_CONFIRMED) { + gamelist.Set_Selected_Index(game_index); + } else { + if (gamelist.Current_Index() != game_index) { + Clear_Player_List (&playerlist); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, 0, 1); + } + } + break; + + /*------------------------------------------------------------------ + House Buttons: set the player's desired House + ------------------------------------------------------------------*/ + case (BUTTON_GDI | KN_BUTTON): + MPlayerHouse = HOUSE_GOOD; + gdibtn.Turn_On(); + nodbtn.Turn_Off(); + break; + + case (BUTTON_NOD | KN_BUTTON): + MPlayerHouse = HOUSE_BAD; + gdibtn.Turn_Off(); + nodbtn.Turn_On(); + break; + + /*------------------------------------------------------------------ + JOIN: send a join request packet & switch to waiting-for-confirmation + mode. (Request_To_Join fills in MPlayerName with my namebuf.) + ------------------------------------------------------------------*/ + case (BUTTON_JOIN | KN_BUTTON): + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + + join_index = gamelist.Current_Index(); + parms_received = 0; + if (Request_To_Join (namebuf, join_index, &playerlist, MPlayerHouse, + MPlayerColorIdx)) { + joinstate = JOIN_WAIT_CONFIRM; + } else { + display = REDRAW_ALL; + } + break; + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF + - If we're part of a game, stay in this dialog; otherwise, exit + ------------------------------------------------------------------*/ + case (KN_ESC): + if (Messages.Get_Edit_Buf() != NULL) { + Messages.Input(input); + display = REDRAW_MESSAGE; + break; + } + case (BUTTON_CANCEL | KN_BUTTON): + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_SIGN_OFF; + strcpy(GPacket.Name,MPlayerName); + + /*............................................................... + If we're joined to a game, make extra sure the other players in + that game know I'm exiting; send my SIGN_OFF as an ack-required + packet. Do not send this packet to myself (index 0). + ...............................................................*/ + if (joinstate == JOIN_CONFIRMED) { + // + // Remove myself from the player list box + // + if (playerlist.Count()) { // added: BRR 6/14/96 + item = (char *)(playerlist.Get_Item(0)); + playerlist.Remove_Item(item); + delete [] item; + playerlist.Flag_To_Redraw(); + } + + // + // Remove myself from the Players list + // + if (Players.Count()) { // added: BRR 6/14/96 + who = Players[0]; + Players.Delete(0); + delete who; + } + + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 1, + &(Players[i]->Address)); + Ipx.Service(); + } + } + + /*............................................................... + Now broadcast my SIGN_OFF so other players looking at this game + know I'm leaving. + ...............................................................*/ + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + + if (IsBridge) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + if (joinstate != JOIN_CONFIRMED) { + process = false; + rc = -1; + } else { + MPlayerGameName[0] = 0; + joinstate = JOIN_NOTHING; + display = REDRAW_ALL; + } + break; + + /*------------------------------------------------------------------ + NEW: bail out with return code 1 + ------------------------------------------------------------------*/ + case (BUTTON_NEW | KN_BUTTON): + /* + .................. Force user to enter a name ................... + */ + if (strlen(namebuf)==0) { + CCMessageBox().Process(TXT_NAME_ERROR); + display = REDRAW_ALL; + break; + } + /* + ..................... Ensure name is unique ..................... + */ + found = 0; + for (i = 0; i < Games.Count(); i++) { + if (!stricmp(Games[i]->Name, namebuf)) { + found = 1; + CCMessageBox().Process (TXT_GAMENAME_MUSTBE_UNIQUE); + display = REDRAW_ALL; + break; + } + } + if (found) + break; + /* + .................... Save player & game name .................... + */ + strcpy(MPlayerName,namebuf); + strcpy(MPlayerGameName,namebuf); + + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + + rc = 1; + process = false; + break; + + /*------------------------------------------------------------------ + Default: manage the inter-player messages + ------------------------------------------------------------------*/ + default: + /*............................................................... + F4/SEND/'M' = edit a message + ...............................................................*/ + if (Messages.Get_Edit_Buf()==NULL) { + if ( (input == KN_M && joinstate==JOIN_CONFIRMED) || + input==(BUTTON_SEND | KN_BUTTON) || input == KN_F4) { + memset (txt, 0, 80); + + strcpy(txt,Text_String(TXT_TO_ALL)); // "To All:" + + Messages.Add_Edit (MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, d_message_w-70*factor); + + if (joinstate <= JOIN_NOTHING) { + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + } + + display = REDRAW_MESSAGE; + + break; + } + } else + + /*............................................................... + If we're already editing a message and the user clicks on + 'Send', translate our input to a Return so Messages.Input() will + work properly. + ...............................................................*/ + if (input==(BUTTON_SEND | KN_BUTTON)) { + input = KN_RETURN; + } + + /*............................................................... + Manage the message system (get rid of old messages) + ...............................................................*/ + if (Messages.Manage()) { + display = REDRAW_MESSAGE; + } + + /*............................................................... + Service keyboard input for any message being edited. + ...............................................................*/ + i = Messages.Input(input); + + /*............................................................... + If 'Input' returned 1, it means refresh the message display. + ...............................................................*/ + if (i==1) { + Messages.Draw(); + } else { + + /*............................................................... + If 'Input' returned 2, it means redraw the message display. + ...............................................................*/ + if (i==2) { + display = REDRAW_MESSAGE; + } else { + + /*............................................................... + If 'input' returned 3, it means send the current message. + ...............................................................*/ + if (i==3) { + long actual_message_size; + char *the_string; + + sent_so_far = 0; + magic_number = MESSAGE_HEAD_MAGIC_NUMBER; + message_length = strlen(Messages.Get_Edit_Buf()); + crc = (unsigned short) + (Calculate_CRC(Messages.Get_Edit_Buf(),message_length) &0xffff); + + while ( sent_so_far < message_length ){ + GPacket.Command = NET_MESSAGE; + strcpy (GPacket.Name, namebuf); + memcpy (GPacket.Message.Buf, Messages.Get_Edit_Buf()+sent_so_far, COMPAT_MESSAGE_LENGTH-5); + + /* + ** Steve I's stuff for splitting message on word boundries + */ + actual_message_size = COMPAT_MESSAGE_LENGTH - 5; + + /* Start at the end of the message and find a space with 10 chars. */ + the_string = GPacket.Message.Buf; + while ( (COMPAT_MESSAGE_LENGTH -5) -actual_message_size < 10 && + the_string[actual_message_size] != ' '){ + --actual_message_size; + } + if ( the_string[actual_message_size] == ' ' ){ + + /* Now delete the extra characters after the space (they musnt print) */ + for ( int i=0 ; i< (COMPAT_MESSAGE_LENGTH-5) - actual_message_size; i++ ){ + the_string[i + actual_message_size] = 0xff; + } + }else{ + actual_message_size = COMPAT_MESSAGE_LENGTH - 5; + } + + + *(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-5) = 0; + *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-4)) = magic_number; + *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-2)) = crc; + GPacket.Message.ID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + GPacket.Message.NameCRC = Compute_Name_CRC(MPlayerGameName); + + /*.................................................................. + Send the message to every player in our player list. The local + system will also receive this message, since it's in the Player list. + ..................................................................*/ + if (joinstate == JOIN_CONFIRMED) { + for (i = 1; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, + sizeof(GlobalPacketType), 1, &(Players[i]->Address)); + Ipx.Service(); + } + + sprintf(txt,Text_String (TXT_FROM), MPlayerName, GPacket.Message.Buf); + Messages.Add_Message (txt, + MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, magic_number, crc); + } + else { + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, + sizeof(GlobalPacketType), 1, &(Players[i]->Address)); + Ipx.Service(); + } + } + magic_number++; + sent_so_far += actual_message_size; //COMPAT_MESSAGE_LENGTH-5; + } + } + } + } + break; + } + + /*--------------------------------------------------------------------- + Resend our query packets + ---------------------------------------------------------------------*/ + Send_Join_Queries(game_index, 0, 0); + + /*--------------------------------------------------------------------- + Process incoming packets + ---------------------------------------------------------------------*/ + event = Get_Join_Responses(&joinstate, &gamelist, &playerlist, + join_index); + /*..................................................................... + If we've changed state, redraw everything; if we're starting the game, + break out of the loop. If we've just joined, send out a player query + so I'll get added to the list instantly. + .....................................................................*/ + if (event == EV_STATE_CHANGE) { + display = REDRAW_ALL; + if (joinstate==JOIN_GAME_START) { + rc = 0; + process = false; + } else { + + /*.................................................................. + If we're newly-confirmed, immediately send out a player query + ..................................................................*/ + if (joinstate==JOIN_CONFIRMED) { + + Clear_Player_List(&playerlist); + + item = new char [MPLAYER_NAME_MAX + 4]; + if (MPlayerHouse==HOUSE_GOOD) { + sprintf(item,"%s\t%s",MPlayerName,Text_String(TXT_G_D_I)); + } else { + sprintf(item,"%s\t%s",MPlayerName,Text_String(TXT_N_O_D)); + } + playerlist.Add_Item (item, MPlayerTColors[MPlayerColorIdx]); + + who = new NodeNameType; + strcpy(who->Name, MPlayerName); + who->Address = IPXAddressClass(); + who->Player.House = MPlayerHouse; + who->Player.Color = MPlayerColorIdx; + Players.Add (who); + + Send_Join_Queries (game_index, 0, 1); + } else { + + /*.................................................................. + If we've been rejected, clear any messages we may have been typing. + ..................................................................*/ + if (joinstate==JOIN_REJECTED) { + + // + // Remove myself from the player list box + // + if (playerlist.Count()) { // added: BRR 6/14/96 + item = (char *)(playerlist.Get_Item(0)); + if (item){ + playerlist.Remove_Item(item); + delete [] item; + playerlist.Flag_To_Redraw(); + } + } + + // + // Remove myself from the Players list + // + if (Players.Count()){ + who = Players[0]; + Players.Delete(0); + delete who; + } + + Messages.Init (d_message_x + 2, d_message_y + 2, 4, + MAX_MESSAGE_LENGTH, d_txt6_h); + } + } + } + } else + + /*..................................................................... + If a new game is detected, and it's the first game on our list, + automatically send out a player query for that game. + .....................................................................*/ + if (event == EV_NEW_GAME && gamelist.Count()==1) { + gamelist.Set_Selected_Index(0); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, 0, 1); + } else + + /*..................................................................... + If the game options have changed, print them. + .....................................................................*/ + if (event == EV_GAME_OPTIONS) { + parms_received = 1; + display = REDRAW_MESSAGE; + } else + + /*..................................................................... + Draw an incoming message + .....................................................................*/ + if (event == EV_MESSAGE) { + display = REDRAW_MESSAGE; + } else + + /*..................................................................... + A game before the one I've selected is gone, so we have a new index now. + 'game_index' must be kept set to the currently-selected list item, so + we send out queries for the currently-selected game. It's therefore + imperative that we detect any changes to the game list. + If we're joined in a game, we must decrement our game_index to keep + it aligned with the game we're joined to. + .....................................................................*/ + if (event == EV_GAME_SIGNOFF) { + if (joinstate==JOIN_CONFIRMED) { + game_index--; + join_index--; + gamelist.Set_Selected_Index(join_index); + } else { + gamelist.Flag_To_Redraw(); + Clear_Player_List(&playerlist); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, 0, 1); + } + } + + /*--------------------------------------------------------------------- + Service the Ipx connections + ---------------------------------------------------------------------*/ + Ipx.Service(); + + /*--------------------------------------------------------------------- + Clean out the Game List; if an old entry is found: + - Remove it + - Clear the player list + - Send queries for the new selected game, if there is one + ---------------------------------------------------------------------*/ + for (i = 0; i < Games.Count(); i++) { + if (TickCount.Time() - Games[i]->Game.LastTime > 400) { + Games.Delete(Games[i]); + item = (char *)(gamelist.Get_Item (i)); + gamelist.Remove_Item (item); + delete [] item; + if (i <= game_index) { + gamelist.Flag_To_Redraw(); + Clear_Player_List(&playerlist); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, 0, 1); + } + } + } + + /*--------------------------------------------------------------------- + Service the sounds & score; GameActive must be false at this point, + so Call_Back() doesn't intercept global messages from me! + ---------------------------------------------------------------------*/ + Call_Back(); + } + + /*------------------------------------------------------------------------ + Establish connections with all other players. + ------------------------------------------------------------------------*/ + if (rc == 0) { + /*..................................................................... + If the other guys are playing a scenario I don't have (sniff), I can't + play. Try to bail gracefully. + .....................................................................*/ + if (ScenarioIdx==-1) { + CCMessageBox().Process (TXT_UNABLE_PLAY_WAAUGH); + + // + // Remove myself from the player list box + // + if (playerlist.Count()) { // added: BRR 6/14/96 + item = (char *)(playerlist.Get_Item(0)); + playerlist.Remove_Item(item); + delete [] item; + playerlist.Flag_To_Redraw(); + } + + // + // Remove myself from the Players list + // + if (Players.Count()) { // added: BRR 6/14/96 + who = Players[0]; + Players.Delete(0); + delete who; + } + + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_SIGN_OFF; + strcpy (GPacket.Name, MPlayerName); + + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 1, + &(Players[i]->Address)); + Ipx.Service(); + } + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + + if (IsBridge) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + rc = -1; + + } else { + + /*.................................................................. + Set the number of players in this game, and my ID + ..................................................................*/ + MPlayerCount = Players.Count(); + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + /*.................................................................. + Get the scenario number + ..................................................................*/ + Scenario = MPlayerFilenum[ScenarioIdx]; + + /*.................................................................. + Form connections with all other players. Form the IPX Connection ID + from the player's Color and House. This will let us extract any + player's color & house at any time. Fill in 'tmp_id' while we're + doing this. + ..................................................................*/ + for (i = 0; i < Players.Count(); i++) { + + /*............................................................... + Only create the connection if it's not myself! + ...............................................................*/ + if (strcmp (MPlayerName, Players[i]->Name)) { + id = Build_MPlayerID(Players[i]->Player.Color, + Players[i]->Player.House); + + tmp_id[i] = id; + + Ipx.Create_Connection((int)id, Players[i]->Name, &(Players[i]->Address) ); + } else { + tmp_id[i] = MPlayerLocalID; + } + } + + /*.................................................................. + Store every player's ID in the MPlayerID[] array. This array will + determine the order of event execution, so the ID's must be stored + in the same order on all systems. + ..................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + min_index = 0; + min_id = 0xff; + for (j = 0; j < MPlayerCount; j++) { + if (tmp_id[j] < min_id) { + min_id = tmp_id[j]; + min_index = j; + } + } + MPlayerID[i] = tmp_id[min_index]; + tmp_id[min_index] = 0xff; + } + /*.................................................................. + Fill in the array of player names, including my own. + ..................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + if (MPlayerID[i] == MPlayerLocalID) { + strcpy (MPlayerNames[i], MPlayerName); + } else { + strcpy (MPlayerNames[i], Ipx.Connection_Name(MPlayerID[i])); + } + } + } + /*--------------------------------------------------------------------- + Wait a while, polling the IPX service routines, to give our ACK + a chance to get to the other system. If he doesn't get our ACK, he'll + be waiting the whole time we load MIX files. + ---------------------------------------------------------------------*/ + i = MAX(Ipx.Global_Response_Time() * 2, 60); + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < i) { + Ipx.Service(); + } + } + + /*------------------------------------------------------------------------ + Init network timing values, using previous response times as a measure + of what our retry delta & timeout should be. + ------------------------------------------------------------------------*/ + Ipx.Set_Timing (Ipx.Global_Response_Time() + 2, -1, + Ipx.Global_Response_Time() * 4); + + /*------------------------------------------------------------------------ + Clear all lists + ------------------------------------------------------------------------*/ + Clear_Game_List(&gamelist); + Clear_Player_List(&playerlist); + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Show_Mouse(); + + return(rc); +} + + +/*************************************************************************** + * Clear_Game_List -- Clears the game-name listbox & 'Games' Vector * + * * + * Assumes each entry in 'Games' & the list box have been allocated * + * separately. * + * * + * INPUT: * + * gamelist ptr to list box * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + *=========================================================================*/ +static void Clear_Game_List (ListClass *gamelist) +{ + char * item; + int i; + + /*------------------------------------------------------------------------ + Clear the list box + ------------------------------------------------------------------------*/ + while (gamelist->Count()) { + item = (char *)(gamelist->Get_Item (0)); + gamelist->Remove_Item(item); + delete [] item; + } + gamelist->Flag_To_Redraw(); + + /*------------------------------------------------------------------------ + Clear the 'Games' Vector + ------------------------------------------------------------------------*/ + for (i = 0; i < Games.Count(); i++) + delete Games[i]; + + Games.Clear(); + +} /* end of Clear_Game_List */ + + +/*************************************************************************** + * Clear_Player_List -- Clears the player-name listbox & Vector * + * * + * Assumes each entry in 'Players' & the list box have been allocated * + * separately. * + * * + * INPUT: * + * playerlist ptr to list box * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + *=========================================================================*/ +static void Clear_Player_List (ListClass *playerlist) +{ + char * item; + int i; + + /*------------------------------------------------------------------------ + Clear the list box + ------------------------------------------------------------------------*/ + while (playerlist->Count()) { + item = (char *)(playerlist->Get_Item(0)); + playerlist->Remove_Item(item); + delete [] item; + } + playerlist->Flag_To_Redraw(); + + /*------------------------------------------------------------------------ + Clear the 'Players' Vector + ------------------------------------------------------------------------*/ + for (i = 0; i < Players.Count(); i++) + delete Players[i]; + + Players.Clear(); + +} /* end of Clear_Player_List */ + + +/*************************************************************************** + * Request_To_Join -- Sends a JOIN request packet to game owner * + * * + * Regardless of the return code, the Join Dialog will need to be redrawn * + * after calling this routine. * + * * + * INPUT: * + * playername player's name * + * join_index index of game we're joining * + * playerlist listbox containing other players' names * + * house requested house * + * color requested color * + * * + * OUTPUT: * + * 1 = Packet sent, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + *=========================================================================*/ +static int Request_To_Join (char *playername, int join_index, ListClass *playerlist, + HousesType house, int color) +{ + int i; + + playerlist = playerlist; // shaddup, Mr stupid compiler! + + /* + --------------------------- Validate join_index -------------------------- + */ + if ( (Games.Count()==0) || join_index > Games.Count() || join_index < 0) { + CCMessageBox().Process (TXT_NOTHING_TO_JOIN); + return(false); + } + + /* + ----------------------- Force user to enter a name ----------------------- + */ + if (strlen(playername)==0) { + CCMessageBox().Process (TXT_NAME_ERROR); + return(false); + } + + /* + ------------------------- The game must be open -------------------------- + */ + if (!Games[join_index]->Game.IsOpen) { + CCMessageBox().Process(TXT_GAME_IS_CLOSED); + return (false); + } + + /* + ------------------------ Make sure name is unique ------------------------ + */ + for (i = 0; i < Players.Count(); i++) { + if (!stricmp(playername, Players[i]->Name)) { + CCMessageBox().Process (TXT_NAME_MUSTBE_UNIQUE); + return(false); + } + } + + /* + ----------------------------- Check version #'s -------------------------- + */ + int v; +#ifdef PATCH + if (IsV107) { + v = 1; + } else { + v = 2; + } +#else + v = Version_Number(); +#endif + if (Games[join_index]->Game.Version > v) { + CCMessageBox().Process (TXT_YOURGAME_OUTDATED); + return(false); + } else { + if (Games[join_index]->Game.Version < v) { + CCMessageBox().Process (TXT_DESTGAME_OUTDATED); + return(false); + } + } + + /* + ----------------------------- Save game name ----------------------------- + */ + strcpy (MPlayerName,playername); + + /* + ----------------------- Send packet to game's owner ---------------------- + */ + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_QUERY_JOIN; + strcpy (GPacket.Name, MPlayerName); + GPacket.PlayerInfo.House = house; + GPacket.PlayerInfo.Color = color; + + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 1, + &(Games[join_index]->Address)); + + return(true); +} + + +/*********************************************************************************************** + * Send_Join_Queries -- sends queries for the Join Dialog * + * * + * This routine [re]sends the queries related to the Join Dialog: * + * - NET_QUERY_GAME * + * - NET_QUERY_PLAYER for the game currently selected (if there is one) * + * * + * The queries are "staggered" in time so they aren't all sent at once; otherwise, we'd * + * be inundated with reply packets & we'd miss some (even though the replies will require * + * ACK's). * + * * + * INPUT: * + * curgame index of currently-selected game; -1 = none * + * gamenow if 1, will immediately send the game query * + * playernow if 1, will immediately send the player query for currently-selected game * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 04/15/1995 BRR : Created. * + *=============================================================================================*/ +static void Send_Join_Queries(int curgame, int gamenow, int playernow) +{ + static int lasttime1 = 0; // time since last Game query sent out + static int lasttime2 = 0; // time since last Player query sent out + + /*------------------------------------------------------------------------ + Send the game-name query if the time has expired, or we're told to do + it right now + ------------------------------------------------------------------------*/ + if ( (TickCount.Time() - lasttime1 > 120) || gamenow) { + lasttime1 = TickCount.Time(); + + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_QUERY_GAME; + + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, NULL); + + /*..................................................................... + If the user specified a remote server address, broadcast over that + network, too. + .....................................................................*/ + if (IsBridge) + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + } + + /*------------------------------------------------------------------------ + Send the player query for the game currently clicked on, if the time has + expired and there is a currently-selected game, or we're told to do it + right now + ------------------------------------------------------------------------*/ + if ( (curgame != -1) && curgame < Games.Count() && + ((TickCount.Time() - lasttime2 > 35) || playernow) ) { + lasttime2 = TickCount.Time(); + + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_QUERY_PLAYER; + strcpy (GPacket.Name, Games[curgame]->Name); + + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, NULL); + + /*..................................................................... + If the user specified a remote server address, broadcast over that + network, too. + .....................................................................*/ + if (IsBridge) + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + } + +} /* end of Send_Join_Queries */ + + +/*********************************************************************************************** + * Get_Join_Responses -- sends queries for the Join Dialog * + * * + * This routine polls the Global Channel to see if there are any incoming packets; * + * if so, it processes them. This routine can change the state of the Join Dialog, or * + * the contents of the list boxes, based on what the packet is. * + * * + * The list boxes are passed in as pointers; they can't be made globals, because they * + * can't be constructed, because they require shape pointers to the arrow buttons, and * + * the mix files won't have been initialized when the global variables' constructors are * + * called. * + * * + * This routine sets the globals * + * MPlayerHouse (from NET_CONFIRM_JOIN) * + * MPlayerColorIdx (from NET_CONFIRM_JOIN) * + * MPlayerBases (from NET_GAME_OPTIONS) * + * MPlayerTiberium (from NET_GAME_OPTIONS) * + * MPlayerGoodies (from NET_GAME_OPTIONS) * + * MPlayerGhosts (from NET_GAME_OPTIONS) * + * ScenarioIdx (from NET_GAME_OPTIONS; -1 = scenario not found) * + * * + * INPUT: * + * joinstate current state of Join Dialog * + * gamelist list box containing game names * + * playerlist list box containing player names for the currently-selected game * + * join_index index of the game we've joined or are asking to join * + * * + * OUTPUT: * + * Event that occurred * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 04/15/1995 BRR : Created. * + *=============================================================================================*/ +static JoinEventType Get_Join_Responses(JoinStateType *joinstate, ListClass *gamelist, + ColorListClass *playerlist, int join_index) +{ + int rc; + char * item; // general-purpose string + NodeNameType *who; // node to add to Games or Players + int i; + int found; + JoinEventType retcode = EV_NONE; + char txt[80]; + int color; + unsigned short magic_number; + unsigned short crc; + + /*------------------------------------------------------------------------ + If there is no incoming packet, just return + ------------------------------------------------------------------------*/ + rc = Ipx.Get_Global_Message (&GPacket, &GPacketlen, &GAddress, &GProductID); + if (!rc || GProductID != IPXGlobalConnClass::COMMAND_AND_CONQUER) + return(EV_NONE); + + /*------------------------------------------------------------------------ + If we're joined in a game, handle the packet in a standard way; otherwise, + don't answer standard queries. + ------------------------------------------------------------------------*/ + if ( (*joinstate)==JOIN_CONFIRMED && + Process_Global_Packet(&GPacket,&GAddress)!=0) + return(EV_NONE); + + /*------------------------------------------------------------------------ + NET_ANSWER_GAME: Another system is answering our GAME query, so add that + system to our list box if it's new. + ------------------------------------------------------------------------*/ + if (GPacket.Command==NET_ANSWER_GAME) { + /*..................................................................... + See if this name is unique + .....................................................................*/ + retcode = EV_NONE; + found = 0; + for (i = 0; i < Games.Count(); i++) { + if (!strcmp(Games[i]->Name, GPacket.Name)) { + found = 1; + /*............................................................... + If name was found, update the node's time stamp & IsOpen flag. + ...............................................................*/ + Games[i]->Game.LastTime = TickCount.Time(); + if (Games[i]->Game.IsOpen != GPacket.GameInfo.IsOpen) { + item = (char *)gamelist->Get_Item(i); + if (GPacket.GameInfo.IsOpen) { + sprintf(item,Text_String(TXT_THATGUYS_GAME),GPacket.Name); + } else { + sprintf(item,Text_String(TXT_THATGUYS_GAME_BRACKET),GPacket.Name); + } + Games[i]->Game.IsOpen = GPacket.GameInfo.IsOpen; + gamelist->Flag_To_Redraw(); + /*............................................................ + If this game has gone from closed to open, copy the responder's + address into our Game slot, since the guy responding to this + must be game owner. + ............................................................*/ + if (Games[i]->Game.IsOpen) + Games[i]->Address = GAddress; + } + break; + } + } + /*..................................................................... + name not found (or addresses are different); add it to 'Games' + .....................................................................*/ + if (found==0) { + /*.................................................................. + Create a new node structure, fill it in, add it to 'Games' + ..................................................................*/ + who = new NodeNameType; + strcpy(who->Name, GPacket.Name); + who->Address = GAddress; + who->Game.Version = GPacket.GameInfo.Version; + who->Game.IsOpen = GPacket.GameInfo.IsOpen; + who->Game.LastTime = TickCount.Time(); + Games.Add (who); + + /*.................................................................. + Create a string for "xxx's Game", leaving room for brackets around + the string if it's a closed game + ..................................................................*/ + item = new char [MPLAYER_NAME_MAX + 9]; + if (GPacket.GameInfo.IsOpen) { + sprintf(item,Text_String(TXT_THATGUYS_GAME),GPacket.Name); + } else { + sprintf(item,Text_String(TXT_THATGUYS_GAME_BRACKET),GPacket.Name); + } + gamelist->Add_Item(item); + + retcode = EV_NEW_GAME; + } + } + + /*------------------------------------------------------------------------ + NET_ANSWER_PLAYER: Another system is answering our PLAYER query, so add it + to our player list box & the Player Vector if it's new + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_ANSWER_PLAYER) { + /*..................................................................... + See if this name is unique + .....................................................................*/ + retcode = EV_NONE; + found = 0; + for (i = 0; i < Players.Count(); i++) { + /*.................................................................. + If the address is already present, re-copy their name, color & + house into the existing entry, in case they've changed it without + our knowledge; set the 'found' flag so we won't create a new entry. + ..................................................................*/ + if (Players[i]->Address==GAddress) { + strcpy(Players[i]->Name, GPacket.Name); + Players[i]->Player.House = GPacket.PlayerInfo.House; + Players[i]->Player.Color = GPacket.PlayerInfo.Color; + playerlist->Colors[i] = MPlayerTColors[GPacket.PlayerInfo.Color]; + found = 1; + break; + } + } + /*..................................................................... + Don't add this player if he's not part of the game that's selected. + .....................................................................*/ + i = gamelist->Current_Index(); + if (Games.Count() && GPacket.PlayerInfo.NameCRC != Compute_Name_CRC(Games[i]->Name)) + found = 1; + + /* + ** Dont add this player if its really me! (hack, hack) + */ + if (!strcmp(GPacket.Name, MPlayerName)){ + found = 1; + } + + + /*..................................................................... + name not found (or address didn't match); add to player list box & Vector + .....................................................................*/ + if (found==0) { + /*.................................................................. + Create & add a node to the Vector + ..................................................................*/ + who = new NodeNameType; + strcpy(who->Name, GPacket.Name); + who->Address = GAddress; + who->Player.House = GPacket.PlayerInfo.House; + who->Player.Color = GPacket.PlayerInfo.Color; + Players.Add (who); + + /*.................................................................. + Create & add a string to the list box + ..................................................................*/ + item = new char [MPLAYER_NAME_MAX + 4]; + if (GPacket.PlayerInfo.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",GPacket.Name,Text_String(TXT_G_D_I)); + } else { + sprintf(item,"%s\t%s",GPacket.Name,Text_String(TXT_N_O_D)); + } + playerlist->Add_Item(item, MPlayerTColors[who->Player.Color]); + + retcode = EV_NEW_PLAYER; + } + } + + /*------------------------------------------------------------------------ + NET_CONFIRM_JOIN: The game owner has confirmed our JOIN query; mark us as + being confirmed, and start answering queries from other systems + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_CONFIRM_JOIN) { + if ( (*joinstate) != JOIN_CONFIRMED) { + strcpy (MPlayerGameName, GPacket.Name); + MPlayerHouse = GPacket.PlayerInfo.House; + MPlayerColorIdx = GPacket.PlayerInfo.Color; + + (*joinstate) = JOIN_CONFIRMED; + retcode = EV_STATE_CHANGE; + } + } + + /*------------------------------------------------------------------------ + NET_REJECT_JOIN: The game owner has turned down our JOIN query; restore + the dialog state to its first pop-up state. Broadcast a sign-off to + tell all other systems that I'm no longer a part of any game; this way, + I'll be properly removed from their dialogs. + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_REJECT_JOIN) { + if ( (*joinstate) != JOIN_REJECTED) { + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_SIGN_OFF; + strcpy (GPacket.Name,MPlayerName); + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), 0, NULL); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), 0, NULL); + + if (IsBridge) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + MPlayerGameName[0] = 0; + + (*joinstate) = JOIN_REJECTED; + retcode = EV_STATE_CHANGE; + } + } + + /*------------------------------------------------------------------------ + NET_GAME_OPTIONS: The game owner has changed the game options & is sending + us the new values. + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_GAME_OPTIONS) { + if ( (*joinstate)==JOIN_CONFIRMED) { + MPlayerCredits = GPacket.ScenarioInfo.Credits; + MPlayerBases = GPacket.ScenarioInfo.IsBases; + MPlayerTiberium = GPacket.ScenarioInfo.IsTiberium; + MPlayerGoodies = GPacket.ScenarioInfo.IsGoodies; + MPlayerGhosts = GPacket.ScenarioInfo.IsGhosties; + BuildLevel = GPacket.ScenarioInfo.BuildLevel; + MPlayerUnitCount = GPacket.ScenarioInfo.UnitCount; + Seed = GPacket.ScenarioInfo.Seed; + Special = GPacket.ScenarioInfo.Special; + Options.GameSpeed = GPacket.ScenarioInfo.GameSpeed; + + if (MPlayerTiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + + if (Winsock.Get_Connected()){ + ScenarioIdx = GPacket.ScenarioInfo.Scenario; + }else{ + + ScenarioIdx = -1; + for (i = 0; i < MPlayerFilenum.Count(); i++) { + if (GPacket.ScenarioInfo.Scenario == MPlayerFilenum[i]) + ScenarioIdx = i; + } + } + + retcode = EV_GAME_OPTIONS; + } + } + + /*------------------------------------------------------------------------ + NET_SIGN_OFF: Another system is signing off: search for that system in + both the game list & player list, & remove it if found + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_SIGN_OFF) { + /*..................................................................... + Remove this name from the list of games + .....................................................................*/ + for (i = 0; i < Games.Count(); i++) { + if (!strcmp(Games[i]->Name, GPacket.Name) && + Games[i]->Address==GAddress) { + /*............................................................... + If the system signing off is the currently-selected list + item, clear the player list since that game is no longer + forming. + ...............................................................*/ + if (i==gamelist->Current_Index()) { + Clear_Player_List (playerlist); + } + + /*............................................................... + If the system signing off was the owner of our game, mark + ourselves as rejected + ...............................................................*/ + if ( (*joinstate) > JOIN_NOTHING && i==join_index) { + (*joinstate) = JOIN_REJECTED; + retcode = EV_STATE_CHANGE; + } + + /* + ....................... Set my return code ...................... + */ + if (retcode == EV_NONE) { + if (i <= gamelist->Current_Index()) { + retcode = EV_GAME_SIGNOFF; + } else { + retcode = EV_PLAYER_SIGNOFF; + } + } + + /* + ................. Remove game name from game list ............... + */ + Games.Delete(Games[i]); + item = (char *)(gamelist->Get_Item (i)); + gamelist->Remove_Item (item); + delete [] item; + gamelist->Flag_To_Redraw(); + + } + } + /*..................................................................... + Remove this name from the list of players + .....................................................................*/ + for (i = 0; i < Players.Count(); i++) { + /* + ..................... Name found; remove it ..................... + */ + if (Players[i]->Address==GAddress) { + item = (char *)(playerlist->Get_Item(i)); + playerlist->Remove_Item(item); + delete [] item; + Players.Delete(Players[i]); + playerlist->Flag_To_Redraw(); + + if (retcode == EV_NONE) + retcode = EV_PLAYER_SIGNOFF; + } + } + } + + /*------------------------------------------------------------------------ + NET_GO: The game's owner is signalling us to start playing. + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_GO) { + if ( (*joinstate)==JOIN_CONFIRMED) { + MPlayerMaxAhead = GPacket.ResponseTime.OneWay; + (*joinstate) = JOIN_GAME_START; + retcode = EV_STATE_CHANGE; +CCDebugString ("C&C95 - Received the 'GO' packet\n"); + } + } + + /*------------------------------------------------------------------------ + NET_MESSAGE: Someone is sending us a message + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_MESSAGE) { + sprintf(txt,Text_String (TXT_FROM), GPacket.Name, GPacket.Message.Buf); + magic_number = *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-4)); + crc = *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-2)); + color = MPlayerID_To_ColorIndex(GPacket.Message.ID); + Messages.Add_Message (txt, MPlayerTColors[color], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, magic_number, crc); + retcode = EV_MESSAGE; + } + + /*------------------------------------------------------------------------ + NET_PING: Someone is pinging me to get a response time measure (will only + happen after I've joined a game). Do nothing; the IPX Manager will handle + sending an ACK, and updating the response time measurements. + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_PING) { + retcode = EV_NONE; + } + + /*------------------------------------------------------------------------ + Default case: nothing happened. (This case will be hit every time I + receive my own NET_QUERY_GAME or NET_QUERY_PLAYER packets.) + ------------------------------------------------------------------------*/ + else { + retcode = EV_NONE; + } + + return(retcode); +} + + +/*********************************************************************************************** + * Net_New_Dialog -- lets user start a new game * + * * + * This dialog shows a list of who's requesting to join this game, and lets * + * the game initiator selectively approve each user. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ New Network Game ³ * + * ³ ³ * + * ³ Players Scenario ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³ Boffo ³³ ³ Hell's Kitchen ³³ ³ * + * ³ ³ Bozo ÃÄ´ ³ Heaven's Gate ÃÄ´ ³ * + * ³ ³ Bonzo ³ ³ ³ ... ³ ³ ³ * + * ³ ³ ÃÄ´ ³ ÃÄ´ ³ * + * ³ ³ ³³ ³ ³³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ [Reject] Count:--- ## ³ * + * ³ Level:--- ## ³ * + * ³ ³ * + * ³ Credits: _____ ³ * + * ³ [ Bases ] [ Crates ] ³ * + * ³ [ Tiberium ] [ AI Players ] ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ [Send Message] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = success, false = cancel * + * * + * WARNINGS: * + * MPlayerName & MPlayerGameName must contain this player's name. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +static int Net_New_Dialog(void) +{ +/* ###Change collision detected! C:\PROJECTS\CODE\NETDLG.CPP... */ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + //D_DIALOG_W = 281; // dialog width + int d_dialog_w = 287*factor; // dialog width + int d_dialog_h = 177*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6*factor+1; // ht of 6-pt text + int d_margin1 = 5*factor; // margin width/height + int d_margin2 = 2*factor; // margin width/height + + //d_playerlist_w = 100; + int d_playerlist_w = 106*factor; + int d_playerlist_h = 27*factor; + int d_playerlist_x = d_dialog_x + d_margin1; + int d_playerlist_y = d_dialog_y + d_margin1 + (d_txt6_h * 3); + + int d_scenariolist_w = 162*factor; + int d_scenariolist_h = 27*factor; + int d_scenariolist_x = d_dialog_x + d_dialog_w - d_margin1 - d_scenariolist_w; + int d_scenariolist_y = d_dialog_y + d_margin1 + (d_txt6_h * 3); + +#if (GERMAN | FRENCH) + int d_reject_w = 55*factor; +#else + int d_reject_w = 45*factor; +#endif + int d_reject_h = 9*factor; + int d_reject_x = d_playerlist_x + (d_playerlist_w / 2) - (d_reject_w / 2); + int d_reject_y = d_playerlist_y + d_playerlist_h + d_margin2; + + int d_count_w = 25*factor; + int d_count_h = d_txt6_h; + int d_count_x = d_scenariolist_x + (d_scenariolist_w / 2); + int d_count_y = d_scenariolist_y + d_scenariolist_h + d_margin2; + + int d_level_w = 25*factor; + int d_level_h = d_txt6_h; + int d_level_x = d_scenariolist_x + (d_scenariolist_w / 2); + int d_level_y = d_count_y + d_count_h; + + int d_credits_w = ((CREDITSBUF_MAX - 1) * 7*factor) + 4*factor; + //int d_credits_w = ((CREDITSBUF_MAX - 1) * 6*factor) + 3*factor; + int d_credits_h = 9*factor; + int d_credits_x = d_dialog_cx + 2*factor; + int d_credits_y = d_level_y + d_level_h + d_margin1; + +#if (GERMAN | FRENCH) + int d_bases_w = 120*factor;//bga:100; +#else + int d_bases_w = 100*factor; +#endif + int d_bases_h = 9*factor; + int d_bases_x = d_dialog_cx - d_bases_w - d_margin2; + int d_bases_y = d_credits_y + d_credits_h + d_margin2; + +#if (GERMAN | FRENCH) + int d_tiberium_w = 120*factor; +#else + int d_tiberium_w = 100*factor; +#endif + int d_tiberium_h = 9*factor; + int d_tiberium_x = d_dialog_cx - d_bases_w - d_margin2; + int d_tiberium_y = d_bases_y + d_bases_h + d_margin2; + +#if (GERMAN | FRENCH) + int d_goodies_w = 120*factor; +#else + int d_goodies_w = 100*factor; +#endif + int d_goodies_h = 9*factor; + int d_goodies_x = d_dialog_cx + d_margin2; + int d_goodies_y = d_credits_y + d_credits_h + d_margin2; + +#if (GERMAN | FRENCH) + int d_ghosts_w = 120*factor; +#else + int d_ghosts_w = 100*factor; +#endif + int d_ghosts_h = 9*factor; + int d_ghosts_x = d_dialog_cx + d_margin2; + int d_ghosts_y = d_goodies_y + d_goodies_h + d_margin2; + + int d_ok_w = 45*factor; + int d_ok_h = 9*factor; + int d_ok_x = d_dialog_cx - d_margin2 - (d_bases_w / 2) - (d_ok_w / 2); + int d_ok_y = d_ghosts_y + d_ghosts_h + d_margin1; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*factor; +#else + int d_cancel_w = 45*factor; +#endif + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx + d_margin2 + (d_goodies_w / 2) - (d_cancel_w / 2); + int d_cancel_y = d_ghosts_y + d_ghosts_h + d_margin1; + + int d_message_w = d_dialog_w - (d_margin1 * 2); + int d_message_h = 34*factor; + int d_message_x = d_dialog_x + d_margin1; + int d_message_y = d_cancel_y + d_cancel_h + d_margin1; + + int d_send_w = 80*factor; + int d_send_h = 9*factor; + int d_send_x = d_dialog_cx - (d_send_w / 2); + int d_send_y = d_message_y + d_message_h + d_margin2; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_PLAYERLIST = 100, + BUTTON_SCENARIOLIST, + BUTTON_REJECT, + BUTTON_COUNT, + BUTTON_LEVEL, + BUTTON_CREDITS, + BUTTON_BASES, + BUTTON_TIBERIUM, + BUTTON_GOODIES, + BUTTON_GHOSTS, + BUTTON_OK, + BUTTON_CANCEL, + BUTTON_SEND, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_UNIT_COUNT, + REDRAW_MESSAGE, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + char credbuf[CREDITSBUF_MAX]; // for credit edit box + int old_cred; // old value in credits buffer + int transmit; // 1 = re-transmit new game options + + long ok_timer = 0; // for timing OK button + int index; // index for rejecting a player + int rc; + int i,j; + char *item; + int tabs[] = {77*factor}; // tabs for player list box + + long ping_timer = 0; // for sending Ping packets + + unsigned char tmp_id[MAX_PLAYERS]; // temp storage for sorting player ID's + int min_index; // for sorting player ID's + unsigned char min_id; // for sorting player ID's + unsigned char id; // connection ID + char txt[80]; + JoinEventType whahoppa; // event generated by received packets + static int first_time = 1; // 1 = 1st time this dialog is run + + int message_length; + int sent_so_far; + unsigned short magic_number; + unsigned short crc; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + + ColorListClass playerlist(BUTTON_PLAYERLIST, + d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + ListClass scenariolist(BUTTON_SCENARIOLIST, + d_scenariolist_x, d_scenariolist_y, d_scenariolist_w, d_scenariolist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + EditClass credit_edt (BUTTON_CREDITS, + credbuf, CREDITSBUF_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_credits_x, d_credits_y, d_credits_w, d_credits_h, EditClass::ALPHANUMERIC); + + TextButtonClass rejectbtn(BUTTON_REJECT, TXT_REJECT, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_reject_x, d_reject_y); +//#else + d_reject_x, d_reject_y, d_reject_w, d_reject_h); +//#endif + + GaugeClass countgauge (BUTTON_COUNT, + d_count_x, d_count_y, d_count_w, d_count_h); + + GaugeClass levelgauge (BUTTON_LEVEL, + d_level_x, d_level_y, d_level_w, d_level_h); + + TextButtonClass basesbtn(BUTTON_BASES, TXT_BASES_OFF, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_bases_x, d_bases_y, d_bases_w, d_bases_h); + + TextButtonClass tiberiumbtn(BUTTON_TIBERIUM, TXT_TIBERIUM_OFF, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_tiberium_x, d_tiberium_y, d_tiberium_w, d_tiberium_h); + + TextButtonClass goodiesbtn(BUTTON_GOODIES, TXT_CRATES_OFF, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_goodies_x, d_goodies_y, d_goodies_w, d_goodies_h); + + TextButtonClass ghostsbtn(BUTTON_GHOSTS, TXT_AI_PLAYERS_OFF, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_ghosts_x, d_ghosts_y, d_ghosts_w, d_ghosts_h); + + TextButtonClass okbtn(BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_ok_x, d_ok_y, d_ok_w, d_ok_h); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + TextButtonClass sendbtn(BUTTON_SEND, TXT_SEND_MESSAGE, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_send_x, d_send_y); +//#else + d_send_x, d_send_y, d_send_w, d_send_h); +//#endif + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &playerlist; + scenariolist.Add_Tail(*commands); + credit_edt.Add_Tail(*commands); + rejectbtn.Add_Tail(*commands); + countgauge.Add_Tail(*commands); + levelgauge.Add_Tail(*commands); + basesbtn.Add_Tail(*commands); + tiberiumbtn.Add_Tail(*commands); + goodiesbtn.Add_Tail(*commands); + ghostsbtn.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + sendbtn.Add_Tail(*commands); + + playerlist.Set_Tabs(tabs); + + /* + ----------------------------- Various Inits ------------------------------ + */ + /*........................................................................ + Init dialog values, only the first time through + ........................................................................*/ + if (first_time) { + MPlayerCredits = 3000; // init credits & credit buffer + MPlayerBases = 1; // init scenario parameters + MPlayerTiberium = 0; + MPlayerGoodies = 0; + MPlayerGhosts = 0; + Special.IsCaptureTheFlag = 0; + MPlayerUnitCount = (MPlayerCountMax[MPlayerBases] + MPlayerCountMin[MPlayerBases]) / 2; + first_time = 0; + } + + /*........................................................................ + Init button states + ........................................................................*/ + if (MPlayerBases) { + basesbtn.Turn_On(); + basesbtn.Set_Text(TXT_BASES_ON); + } + if (MPlayerTiberium) { + tiberiumbtn.Turn_On(); + tiberiumbtn.Set_Text(TXT_TIBERIUM_ON); + } + if (MPlayerGoodies) { + goodiesbtn.Turn_On(); + goodiesbtn.Set_Text(TXT_CRATES_ON); + } + if (MPlayerGhosts) { + ghostsbtn.Turn_On(); + ghostsbtn.Set_Text(TXT_AI_PLAYERS_ON); + } + if (Special.IsCaptureTheFlag) { + MPlayerGhosts = 0; + ghostsbtn.Turn_On(); + ghostsbtn.Set_Text(TXT_CAPTURE_THE_FLAG); + } + + sprintf(credbuf, "%d", MPlayerCredits); + credit_edt.Set_Text(credbuf, CREDITSBUF_MAX); + old_cred = MPlayerCredits; + + levelgauge.Set_Maximum(MPLAYER_BUILD_LEVEL_MAX - 1); + levelgauge.Set_Value(BuildLevel - 1); + + countgauge.Set_Maximum(MPlayerCountMax[MPlayerBases] - MPlayerCountMin[MPlayerBases]); + countgauge.Set_Value(MPlayerUnitCount - MPlayerCountMin[MPlayerBases]); + + /*........................................................................ + Init other scenario parameters + ........................................................................*/ + Special.IsTGrowth = MPlayerTiberium; + Special.IsTSpread = MPlayerTiberium; + transmit = 0; + + /*........................................................................ + Init scenario description list box + ........................................................................*/ + for (i = 0; i < MPlayerScenarios.Count(); i++) { + scenariolist.Add_Item (strupr(MPlayerScenarios[i])); + } + ScenarioIdx = 0; // 1st scenario is selected + + /*........................................................................ + Init player color-used flags + ........................................................................*/ + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + ColorUsed[i] = 0; // init all colors to available + } + ColorUsed[MPlayerColorIdx] = 1; // set my color to used + playerlist.Set_Selected_Style(ColorListClass::SELECT_BAR, CC_GREEN_SHADOW); + + /*........................................................................ + Init random-number generator, & create a seed to be used for all random + numbers from here on out + ........................................................................*/ + randomize(); + Seed = rand(); + + /*........................................................................ + Init the message display system + ........................................................................*/ + Messages.Init (d_message_x + 2*factor, d_message_y + 2*factor, 4, MAX_MESSAGE_LENGTH, + d_txt6_h); + + /*------------------------------------------------------------------------ + Add myself to the list. Note that since I'm not in the Players Vector, + the Vector & listbox are now 1 out of sync. + ------------------------------------------------------------------------*/ + item = new char [MPLAYER_NAME_MAX + 4]; + if (MPlayerHouse==HOUSE_GOOD) { + sprintf(item,"%s\t%s",MPlayerName,Text_String(TXT_G_D_I)); + } else { + sprintf(item,"%s\t%s",MPlayerName,Text_String(TXT_N_O_D)); + } + playerlist.Add_Item(item, MPlayerTColors[MPlayerColorIdx]); + + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + while (Get_Mouse_State() > 0) Show_Mouse(); + + /* + ---------------------------- Processing loop ----------------------------- + */ + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + #if(SHOW_MONO) + Ipx.Mono_Debug_Print(-1,0); + #endif + /* + ...................... Refresh display if needed ...................... + */ + if (display == REDRAW_UNIT_COUNT){ + /* + ** Wipe the background behind the unit count then reprint it + */ + LogicPage->Fill_Rect(d_count_x + d_count_w + 2*factor, + d_count_y, + d_count_x + d_count_w + 2*factor + 20, + d_count_y + 12, + 0 ); + sprintf(txt,"%d",MPlayerUnitCount); + Fancy_Text_Print (txt, d_count_x + d_count_w + 2*factor, d_count_y, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL); + display = REDRAW_NONE; + } + + + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + + /* + ** Reload and draw the title page + */ + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_NETGAME_SETUP, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(TXT_PLAYERS, + d_playerlist_x + (d_playerlist_w / 2), d_playerlist_y - d_txt6_h, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_CENTER); + + Fancy_Text_Print(TXT_SCENARIOS, + d_scenariolist_x + (d_scenariolist_w / 2), + d_scenariolist_y - d_txt6_h, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_CENTER); + + Fancy_Text_Print (TXT_COUNT, d_count_x - 2*factor, d_count_y, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_RIGHT); + + sprintf(txt,"%d",MPlayerUnitCount); + Fancy_Text_Print (txt, d_count_x + d_count_w + 2*factor, d_count_y, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL); + + Fancy_Text_Print (TXT_LEVEL, d_level_x - 2*factor, d_level_y, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_RIGHT); + + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(txt,"%d",BuildLevel); + } else { + sprintf(txt, "**"); + } + Fancy_Text_Print (txt, d_level_x + d_level_w + 2*factor, d_level_y, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL); + + Fancy_Text_Print (TXT_START_CREDITS_COLON, d_credits_x - 5*factor, + d_credits_y + 1*factor, CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_RIGHT); + + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + + /*.................................................................. + Draw the messages: + - Erase an old message first + - If we're in a game, print the game options (if they've been + received) + - If we've been rejected from a game, print that message + ..................................................................*/ + if (display >= REDRAW_MESSAGE) { + Draw_Box(d_message_x, d_message_y, d_message_w, d_message_h, + BOXSTYLE_GREEN_BORDER, true); + Messages.Draw(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + /*------------------------------------------------------------------ + New Scenario selected. + ------------------------------------------------------------------*/ + case (BUTTON_SCENARIOLIST | KN_BUTTON): + if (scenariolist.Current_Index() != ScenarioIdx) { + ScenarioIdx = scenariolist.Current_Index(); + MPlayerCredits = atoi(credbuf); + transmit = 1; + } + break; + + /*------------------------------------------------------------------ + Reject the currently-selected player (don't allow rejecting myself, + who will be the first entry in the list) + ------------------------------------------------------------------*/ + case (BUTTON_REJECT | KN_BUTTON): + index = playerlist.Current_Index(); + if (index == 0) { + CCMessageBox().Process (TXT_CANT_REJECT_SELF, TXT_OOPS); + display = REDRAW_ALL; + break; + } else { + if (index < 0 || index >= playerlist.Count()) { + CCMessageBox().Process (TXT_SELECT_PLAYER_REJECT,TXT_OOPS); + display = REDRAW_ALL; + break; + } + } + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_REJECT_JOIN; + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), 1, + &(Players[index - 1]->Address)); + break; + + /*------------------------------------------------------------------ + User adjusts max # units + ------------------------------------------------------------------*/ + case (BUTTON_COUNT | KN_BUTTON): + MPlayerUnitCount = countgauge.Get_Value() + MPlayerCountMin[MPlayerBases]; + + Hide_Mouse(); + LogicPage->Fill_Rect (d_count_x + d_count_w + 2*factor, d_count_y, + d_count_x + d_count_w + 14*factor, d_count_y + 6*factor, BLACK); + + sprintf(txt,"%d",MPlayerUnitCount); + Fancy_Text_Print (txt, d_count_x + d_count_w + 2*factor, d_count_y, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL); + Show_Mouse(); + + transmit = 1; + break; + + /*------------------------------------------------------------------ + User adjusts build level + ------------------------------------------------------------------*/ + case (BUTTON_LEVEL | KN_BUTTON): + BuildLevel = levelgauge.Get_Value() + 1; + if (BuildLevel > MPLAYER_BUILD_LEVEL_MAX) // if it's pegged, max it out + BuildLevel = MPLAYER_BUILD_LEVEL_MAX; + + Hide_Mouse(); + LogicPage->Fill_Rect (d_level_x + d_level_w + 2*factor, d_level_y, + d_level_x + d_level_w + 14*factor, d_level_y + 6*factor, BLACK); + + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(txt,"%d",BuildLevel); + } else { + sprintf(txt, "**"); + } + Fancy_Text_Print (txt, d_level_x + d_level_w + 2*factor, d_level_y, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL); + Show_Mouse(); + + transmit = 1; + break; + + /*------------------------------------------------------------------ + User edits the credits value; retransmit new game options + ------------------------------------------------------------------*/ + case (BUTTON_CREDITS | KN_BUTTON): + MPlayerCredits = atoi(credbuf); + transmit = 1; + break; + + /*------------------------------------------------------------------ + Toggle bases: + - Clear scenario list & rebuild it with new names + - toggle bases button, change its text + - adjust the MPlayerUnitCount to reflect the new allowed range, + using the current gauge setting + - Change the unit count gauge limit & value + ------------------------------------------------------------------*/ + case (BUTTON_BASES | KN_BUTTON): + if (MPlayerBases) { + MPlayerBases = 0; + basesbtn.Turn_Off(); + basesbtn.Set_Text(TXT_BASES_OFF); + MPlayerUnitCount = Fixed_To_Cardinal (MPlayerCountMax[0]-MPlayerCountMin[0], + Cardinal_To_Fixed(MPlayerCountMax[1]-MPlayerCountMin[1], + MPlayerUnitCount-MPlayerCountMin[1])) + MPlayerCountMin[0]; + } else { + MPlayerBases = 1; + basesbtn.Turn_On(); + basesbtn.Set_Text(TXT_BASES_ON); + MPlayerUnitCount = Fixed_To_Cardinal (MPlayerCountMax[1]-MPlayerCountMin[1], + Cardinal_To_Fixed(MPlayerCountMax[0]-MPlayerCountMin[0], + MPlayerUnitCount-MPlayerCountMin[0])) + MPlayerCountMin[1]; + } + MPlayerCredits = atoi(credbuf); + countgauge.Set_Maximum(MPlayerCountMax[MPlayerBases] - MPlayerCountMin[MPlayerBases]); + countgauge.Set_Value(MPlayerUnitCount - MPlayerCountMin[MPlayerBases]); + transmit = 1; + countgauge.Flag_To_Redraw(); + display = REDRAW_UNIT_COUNT; + break; + + /*------------------------------------------------------------------ + Toggle tiberium + ------------------------------------------------------------------*/ + case (BUTTON_TIBERIUM | KN_BUTTON): + if (MPlayerTiberium) { + MPlayerTiberium = 0; + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + tiberiumbtn.Turn_Off(); + tiberiumbtn.Set_Text(TXT_TIBERIUM_OFF); + } else { + MPlayerTiberium = 1; + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + tiberiumbtn.Turn_On(); + tiberiumbtn.Set_Text(TXT_TIBERIUM_ON); + } + MPlayerCredits = atoi(credbuf); + transmit = 1; + break; + + /*------------------------------------------------------------------ + Toggle goodies + ------------------------------------------------------------------*/ + case (BUTTON_GOODIES | KN_BUTTON): + if (MPlayerGoodies) { + MPlayerGoodies = 0; + goodiesbtn.Turn_Off(); + goodiesbtn.Set_Text(TXT_CRATES_OFF); + } else { + MPlayerGoodies = 1; + goodiesbtn.Turn_On(); + goodiesbtn.Set_Text(TXT_CRATES_ON); + } + MPlayerCredits = atoi(credbuf); + transmit = 1; + break; + + /*------------------------------------------------------------------ + Toggle ghosts/capture-the-flag + ------------------------------------------------------------------*/ + case (BUTTON_GHOSTS | KN_BUTTON): + if (!MPlayerGhosts && !Special.IsCaptureTheFlag) { // ghosts OFF => ghosts ON + MPlayerGhosts = 1; + Special.IsCaptureTheFlag = 0; + ghostsbtn.Turn_On(); + ghostsbtn.Set_Text(TXT_AI_PLAYERS_ON); + } else { + if (MPlayerGhosts) { // ghosts ON => capture-flag + MPlayerGhosts = 0; + Special.IsCaptureTheFlag = 1; + ghostsbtn.Turn_On(); + ghostsbtn.Set_Text(TXT_CAPTURE_THE_FLAG); + } else { + if (Special.IsCaptureTheFlag) { // capture-flag => AI OFF + MPlayerGhosts = 0; + Special.IsCaptureTheFlag = 0; + ghostsbtn.Turn_Off(); + ghostsbtn.Set_Text(TXT_AI_PLAYERS_OFF); + } + } + } + + MPlayerCredits = atoi(credbuf); + transmit = 1; + break; + + /*------------------------------------------------------------------ + OK: exit loop with TRUE status + ------------------------------------------------------------------*/ + case (BUTTON_OK | KN_BUTTON): + /*............................................................... + If a new player has joined in the last second, don't allow + an OK; force a wait longer than 1 second (to give all players + a chance to know about this new guy) + ...............................................................*/ + i = MAX(Ipx.Global_Response_Time() * 2, 60); + while (TickCount.Time() - ok_timer < i) + Ipx.Service(); + + /*............................................................... + If there are at least 2 players, go ahead & play; error otherwise + ...............................................................*/ + if (MPlayerSolo || Players.Count() > 0) { + rc = TRUE; + process = FALSE; + } else { + CCMessageBox().Process (TXT_ONLY_ONE,TXT_OOPS,NULL); + display = REDRAW_ALL; + } + break; + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF, bail out with error code + ------------------------------------------------------------------*/ + case (KN_ESC): + if (Messages.Get_Edit_Buf() != NULL) { + Messages.Input(input); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + break; + } + case (BUTTON_CANCEL | KN_BUTTON): + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_SIGN_OFF; + strcpy (GPacket.Name, MPlayerName); + + /*............................................................... + Broadcast my sign-off over my network + ...............................................................*/ + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + /*............................................................... + Broadcast my sign-off over a bridged network if there is one + ...............................................................*/ + if (IsBridge) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + } + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + /*............................................................... + And now, just be absolutely sure, send my sign-off to each + player in my game. (If there's a bridge between us, the other + player will have specified my address, so he can cross the + bridge; but I may not have specified a bridge address, so the + only way I have of crossing the bridge is to send a packet + directly to him.) + ...............................................................*/ + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 1, + &(Players[i]->Address)); + Ipx.Service(); + } + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + MPlayerGameName[0] = 0; + process = false; + rc = false; + break; + + /*------------------------------------------------------------------ + Default: manage the inter-player messages + ------------------------------------------------------------------*/ + default: + /*............................................................... + F4/SEND/'M' = send a message + ...............................................................*/ + if (Messages.Get_Edit_Buf()==NULL) { + if (input == KN_M || input==(BUTTON_SEND | KN_BUTTON) || + input == KN_F4) { + memset (txt, 0, 80); + + strcpy(txt,Text_String(TXT_TO_ALL)); // "To All:" + + Messages.Add_Edit (MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, d_message_w-70*factor); + + credit_edt.Clear_Focus(); + credit_edt.Flag_To_Redraw(); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + + break; + } + } else { + + /*............................................................... + If we're already editing a message and the user clicks on + 'Send', translate our input to a Return so Messages.Input() will + work properly. + ...............................................................*/ + if (input==(BUTTON_SEND | KN_BUTTON)) { + input = KN_RETURN; + } + } + + /*............................................................... + Manage the message system (get rid of old messages) + ...............................................................*/ + if (Messages.Manage()) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } + + /*............................................................... + Re-draw the messages & service keyboard input for any message + being edited. + ...............................................................*/ + i = Messages.Input(input); + + /*............................................................... + If 'Input' returned 1, it means refresh the message display. + ...............................................................*/ + if (i==1) { + Messages.Draw(); + } + + /*............................................................... + If 'Input' returned 2, it means redraw the message display. + ...............................................................*/ + else if (i==2) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } + + /*............................................................... + If 'input' returned 3, it means send the current message. + ...............................................................*/ + else if (i==3) { + long actual_message_size; + char *the_string; + + sent_so_far = 0; + magic_number = MESSAGE_HEAD_MAGIC_NUMBER; + message_length = strlen(Messages.Get_Edit_Buf()); + crc = (unsigned short) + (Calculate_CRC(Messages.Get_Edit_Buf(), message_length) &0xffff); + while ( sent_so_far < message_length ){ + memset (&GPacket, 0, sizeof(GlobalPacketType)); + GPacket.Command = NET_MESSAGE; + strcpy (GPacket.Name, MPlayerName); + memcpy (GPacket.Message.Buf, Messages.Get_Edit_Buf()+sent_so_far, COMPAT_MESSAGE_LENGTH-5); + + /* + ** Steve I's stuff for splitting message on word boundries + */ + actual_message_size = COMPAT_MESSAGE_LENGTH - 5; + + /* Start at the end of the message and find a space with 10 chars. */ + the_string = GPacket.Message.Buf; + while ( (COMPAT_MESSAGE_LENGTH -5) -actual_message_size < 10 && + the_string[actual_message_size] != ' '){ + --actual_message_size; + } + if ( the_string[actual_message_size] == ' ' ){ + + /* Now delete the extra characters after the space (they musnt print) */ + for ( int i=0 ; i< (COMPAT_MESSAGE_LENGTH-5) - actual_message_size; i++ ){ + the_string[i + actual_message_size] = 0xff; + } + }else{ + actual_message_size = COMPAT_MESSAGE_LENGTH - 5; + } + + *(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-5) = 0; + *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-4)) = magic_number; + *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-2)) = crc; + GPacket.Message.ID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + GPacket.Message.NameCRC = Compute_Name_CRC(MPlayerGameName); + + /*.................................................................. + Send the message to every player in our player list. + ..................................................................*/ + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 1, + &(Players[i]->Address)); + Ipx.Service(); + } + /*.................................................................. + Add the message to our own list, since we're not in the player list + on this dialog. + ..................................................................*/ + sprintf(txt,Text_String (TXT_FROM), MPlayerName, GPacket.Message.Buf); + Messages.Add_Message (txt, MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, magic_number, crc); + + magic_number++; + sent_so_far += actual_message_size; //COMPAT_MESSAGE_LENGTH-5; + + } + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } + } + + /*--------------------------------------------------------------------- + Detect editing of the credits buffer, transmit new values to players + ---------------------------------------------------------------------*/ + if (atoi(credbuf) != old_cred) { + old_cred = Bound(atoi(credbuf), 0, 9999); + MPlayerCredits = old_cred; + transmit = 1; + sprintf(credbuf, "%d", MPlayerCredits); + credit_edt.Set_Text(credbuf, CREDITSBUF_MAX); + } + + /*--------------------------------------------------------------------- + Process incoming packets + ---------------------------------------------------------------------*/ + whahoppa = Get_NewGame_Responses(&playerlist); + if (whahoppa == EV_NEW_PLAYER) { + ok_timer = TickCount.Time(); + transmit = 1; + } else { + if (whahoppa == EV_MESSAGE) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } + } + + /*--------------------------------------------------------------------- + If our Transmit flag is set, we need to send out a game option packet + ---------------------------------------------------------------------*/ + if (transmit) { + for (i = 0; i < Players.Count(); i++) { + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_GAME_OPTIONS; + GPacket.ScenarioInfo.Scenario = MPlayerFilenum[ScenarioIdx]; + GPacket.ScenarioInfo.Credits = MPlayerCredits; + GPacket.ScenarioInfo.IsBases = MPlayerBases; + GPacket.ScenarioInfo.IsTiberium = MPlayerTiberium; + GPacket.ScenarioInfo.IsGoodies = MPlayerGoodies; + GPacket.ScenarioInfo.IsGhosties = MPlayerGhosts; + GPacket.ScenarioInfo.BuildLevel = BuildLevel; + GPacket.ScenarioInfo.UnitCount = MPlayerUnitCount; + GPacket.ScenarioInfo.Seed = Seed; + GPacket.ScenarioInfo.Special = Special; + GPacket.ScenarioInfo.GameSpeed = Options.GameSpeed; + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 1, &(Players[i]->Address) ); + } + transmit = 0; + } + + /*--------------------------------------------------------------------- + Ping every player in my game, to force the Global Channel to measure + the connection response time. + ---------------------------------------------------------------------*/ + if (TickCount.Time() - ping_timer > 15) { + memset (&GPacket, 0, sizeof(GlobalPacketType)); + GPacket.Command = NET_PING; + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 1, &(Players[i]->Address) ); + } + ping_timer = TickCount.Time(); + } + + /*--------------------------------------------------------------------- + Service the Ipx connections + ---------------------------------------------------------------------*/ + Ipx.Service(); + + /*--------------------------------------------------------------------- + Service the sounds & score; GameActive must be false at this point, + so Call_Back() doesn't intercept global messages from me! + ---------------------------------------------------------------------*/ + Call_Back(); + + } /* end of while */ + + /*------------------------------------------------------------------------ + Establish connections with all other players. + ------------------------------------------------------------------------*/ + if (rc) { + /*..................................................................... + Set the number of players in this game, and my ID + .....................................................................*/ + MPlayerCount = Players.Count() + 1; + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + /*..................................................................... + Get the scenario filename + .....................................................................*/ + Scenario = MPlayerFilenum[ScenarioIdx]; + + /*..................................................................... + Compute frame delay value for packet transmissions: + - Divide global channel's response time by 8 (2 to convert to 1-way + value, 4 more to convert from ticks to frames) + .....................................................................*/ + MPlayerMaxAhead = MAX( (Ipx.Global_Response_Time() / 8), 2); + + /*..................................................................... + Send all players the NET_GO packet. Wait until all ACK's have been + received. + .....................................................................*/ + memset (&GPacket, 0, sizeof(GlobalPacketType)); + GPacket.Command = NET_GO; + GPacket.ResponseTime.OneWay = MPlayerMaxAhead; + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 1, &(Players[i]->Address) ); + /*.................................................................. + Wait for all the ACK's to come in. + ..................................................................*/ + while (Ipx.Global_Num_Send() > 0) + Ipx.Service(); + } + + /*..................................................................... + Form connections with all other players. Form the IPX Connection ID + from the player's Color (high byte) and House (low byte). This + will let us extract any player's color & house at any time. + Fill in 'tmp_id' while we're doing this. + .....................................................................*/ + for (i = 0; i < Players.Count(); i++) { + id = Build_MPlayerID (Players[i]->Player.Color, + Players[i]->Player.House); + + tmp_id[i] = id; + + Ipx.Create_Connection(id, Players[i]->Name, &(Players[i]->Address) ); + } + tmp_id[i] = MPlayerLocalID; + + /*..................................................................... + Store every player's ID in the MPlayerID[] array. This array will + determine the order of event execution, so the ID's must be stored + in the same order on all systems. + .....................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + min_index = 0; + min_id = 0xff; + for (j = 0; j < MPlayerCount; j++) { + if (tmp_id[j] < min_id) { + min_id = tmp_id[j]; + min_index = j; + } + } + MPlayerID[i] = tmp_id[min_index]; + tmp_id[min_index] = 0xff; + } + /*..................................................................... + Fill in the array of player names, including my own. + .....................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + if (MPlayerID[i] == MPlayerLocalID) { + strcpy (MPlayerNames[i], MPlayerName); + } else { + strcpy (MPlayerNames[i], Ipx.Connection_Name(MPlayerID[i])); + } + } + } + + /*------------------------------------------------------------------------ + Init network timing values, using previous response times as a measure + of what our retry delta & timeout should be. + ------------------------------------------------------------------------*/ + Ipx.Set_Timing (Ipx.Global_Response_Time() + 2, -1, + Ipx.Global_Response_Time() * 4); + + /*------------------------------------------------------------------------ + Clear all lists + ------------------------------------------------------------------------*/ + while (scenariolist.Count()) { + scenariolist.Remove_Item(scenariolist.Get_Item(0)); + } + Clear_Player_List(&playerlist); + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Show_Mouse(); + + return(rc); +} + + +/*************************************************************************** + * Get_NewGame_Responses -- processes packets for New Game dialog * + * * + * This routine can modify the contents of the given list box, as well * + * as the contents of the Players Vector global. * + * * + * INPUT: * + * playerlist list of players in this game * + * * + * OUTPUT: * + * EV_NONE = nothing happened * + * EV_NEW_PLAYER = a new player has joined; false otherwise * + * EV_MESSAGE = a message was received * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/18/1995 BRR : Created. * + *=========================================================================*/ +static JoinEventType Get_NewGame_Responses(ColorListClass *playerlist) +{ + int rc; + char * item; // general-purpose string + NodeNameType *who; // node to add to Players Vector + int i; + int found; + JoinEventType retval = EV_NONE; + int resend; + char txt[80]; + int color; + unsigned short magic_number; + unsigned short crc; + + /*------------------------------------------------------------------------ + If there is no incoming packet, just return + ------------------------------------------------------------------------*/ + rc = Ipx.Get_Global_Message (&GPacket, &GPacketlen, &GAddress, &GProductID); + if (!rc || GProductID != IPXGlobalConnClass::COMMAND_AND_CONQUER) { + return(EV_NONE); + } + + /*------------------------------------------------------------------------ + Try to handle the packet in a standard way + ------------------------------------------------------------------------*/ + if (Process_Global_Packet(&GPacket,&GAddress) != 0) { + return(EV_NONE); + } else + + /*------------------------------------------------------------------------ + NET_QUERY_JOIN: + ------------------------------------------------------------------------*/ + if (GPacket.Command==NET_QUERY_JOIN) { + /*..................................................................... + See if this name is unique: + - If the name matches, but the address is different, reject this player + - If the name & address match, this packet must be a re-send of a + prevous request; in this case, do nothing. The other player must have + received my CONFIRM_JOIN packet (since it was sent with an ACK + required), so we can ignore this resend. + .....................................................................*/ + found = 0; + resend = 0; + for (i = 0; i < Players.Count(); i++) { + if (!strcmp(Players[i]->Name,GPacket.Name)) { + if (Players[i]->Address != GAddress) { + found = 1; + } + else { + resend = 1; + } + break; + } + } + if (!strcmp (MPlayerName, GPacket.Name)) { + found = 1; + } + + /*..................................................................... + Reject if name is a duplicate, or if there are too many players: + .....................................................................*/ + if (found || (Players.Count() >= (MPlayerMax - 1) && !resend) ) { + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_REJECT_JOIN; + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 1, &GAddress); + } + + /*..................................................................... + If this packet is NOT a resend, accept the player. Grant him the + requested color if possible. + .....................................................................*/ + else if (!resend) { + /*.................................................................. + Add node to the Vector list + ..................................................................*/ + who = new NodeNameType; + strcpy(who->Name, GPacket.Name); + who->Address = GAddress; + who->Player.House = GPacket.PlayerInfo.House; + Players.Add (who); + + /*.................................................................. + Set player's color; if requested color isn't used, give it to him; + otherwise, give him the 1st available color. Mark the color we + give him as used. + ..................................................................*/ + if (ColorUsed[GPacket.PlayerInfo.Color] == 0) { + who->Player.Color = GPacket.PlayerInfo.Color; + } else { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + if (ColorUsed[i]==0) { + who->Player.Color = i; + break; + } + } + } + ColorUsed[who->Player.Color] = 1; + + /*.................................................................. + Add player name to the list box + ..................................................................*/ + item = new char [MPLAYER_NAME_MAX + 4]; + if (GPacket.PlayerInfo.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",GPacket.Name,Text_String(TXT_G_D_I)); + } else { + sprintf(item,"%s\t%s",GPacket.Name,Text_String(TXT_N_O_D)); + } + playerlist->Add_Item (item, MPlayerTColors[who->Player.Color]); + + /*.................................................................. + Send a confirmation packet + ..................................................................*/ + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_CONFIRM_JOIN; + strcpy(GPacket.Name,MPlayerName); + GPacket.PlayerInfo.House = who->Player.House; + GPacket.PlayerInfo.Color = who->Player.Color; + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 1, &GAddress); + + retval = EV_NEW_PLAYER; + } + } + + /*------------------------------------------------------------------------ + NET_SIGN_OFF: Another system is signing off: search for that system in + the player list, & remove it if found + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_SIGN_OFF) { + for (i = 0; i < Players.Count(); i++) { + /* + ....................... Name found; remove it ...................... + */ + if (!strcmp (Players[i]->Name, GPacket.Name) && + Players[i]->Address==GAddress) { + /*............................................................... + Remove from the list box + ...............................................................*/ + item = (char *)(playerlist->Get_Item(i + 1)); + playerlist->Remove_Item(item); + playerlist->Flag_To_Redraw(); + delete [] item; + /*............................................................... + Mark his color as available + ...............................................................*/ + ColorUsed[Players[i]->Player.Color] = 0; + /*............................................................... + Delete from the Vector list + ...............................................................*/ + Players.Delete(Players[i]); + break; + } + } + } + + /*------------------------------------------------------------------------ + NET_MESSAGE: Someone is sending us a message + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_MESSAGE) { + sprintf(txt,Text_String (TXT_FROM), GPacket.Name, GPacket.Message.Buf); + magic_number = *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-4)); + crc = *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-2)); + color = MPlayerID_To_ColorIndex(GPacket.Message.ID); + Messages.Add_Message (txt, MPlayerTColors[color], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, magic_number, crc); + retval = EV_MESSAGE; + } + + return(retval); +} + + + +/*************************************************************************** + * Compute_Name_CRC -- computes CRC from char string * + * * + * INPUT: * + * name string to create CRC for * + * * + * OUTPUT: * + * CRC * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/29/1995 BRR : Created. * + *=========================================================================*/ +unsigned long Compute_Name_CRC(char *name) +{ + char buf[80]; + unsigned long crc = 0L; + int i; + + strcpy (buf, name); + strupr (buf); + + for (i = 0; i < strlen(buf); i++) { + Add_CRC (&crc, (unsigned long)buf[i]); + } + + return (crc); +} + +/*************************************************************************** + * Net_Reconnect_Dialog -- Draws/updates the network reconnect dialog * + * * + * INPUT: * + * reconn 1 = reconnect, 0 = waiting for first-time connection * + * fresh 1 = draw from scratch, 0 = only update time counter * + * oldest_index IPX connection index of oldest connection * + * (only used for reconnection) * + * timeval value to print in the countdown field * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/08/1995 BRR : Created. * + *=========================================================================*/ +void Net_Reconnect_Dialog(int reconn, int fresh, int oldest_index, + unsigned long timeval) +{ + static int x,y,w,h; + int id; + char buf1[40] = {0}; + char buf2[40] = {0}; + char const *buf3 = ""; + + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + int d_txt6_h = 6*factor+1; + int d_margin = 5*factor; + + + /*------------------------------------------------------------------------ + Draw the dialog from scratch + ------------------------------------------------------------------------*/ + if (fresh) { + Fancy_Text_Print ("", 0, 0, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + if (reconn) { + id = Ipx.Connection_ID(oldest_index); + sprintf(buf1,Text_String(TXT_RECONNECTING_TO), + Ipx.Connection_Name(id)); + } else { + sprintf(buf1,Text_String(TXT_WAITING_FOR_CONNECTIONS)); + } + sprintf(buf2,Text_String(TXT_TIME_ALLOWED), timeval + 1); + buf3 = Text_String(TXT_PRESS_ESC); + + w = MAX(String_Pixel_Width(buf1),String_Pixel_Width(buf2)); + w = MAX(String_Pixel_Width(buf3), w); + w += (d_margin * 4); + h = (d_txt6_h * 3) + (d_margin * 6); + x = 160*factor - (w / 2); + y = 100*factor - (h / 2); + + Hide_Mouse(); + Set_Logic_Page(SeenBuff); + Dialog_Box(x, y, w, h); + + Fancy_Text_Print (buf1, 160*factor, y + (d_margin * 2), CC_GREEN, BLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print (buf2, 160*factor, y + (d_margin * 2) + d_txt6_h + d_margin, + CC_GREEN, BLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print (buf3, 160*factor, y + (d_margin * 2) + (d_txt6_h + d_margin) * 2, + CC_GREEN, BLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Show_Mouse(); + + } else { + + /*------------------------------------------------------------------------ + Just update the timeout value on the dialog + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Set_Logic_Page(SeenBuff); + + sprintf(buf2,Text_String(TXT_TIME_ALLOWED), timeval + 1); + int pixwidth = String_Pixel_Width (buf2); + LogicPage->Fill_Rect (160*factor - (pixwidth/2) - 12, y+(d_margin*2) + d_txt6_h + d_margin, + 160*factor + (pixwidth/2) + 12, y+(d_margin*2) + d_txt6_h*2 + d_margin, + TBLACK); + Fancy_Text_Print (buf2, 160*factor, y + (d_margin * 2) + d_txt6_h + d_margin, + CC_GREEN, BLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Show_Mouse(); + } +} + + + + + + +/*********************************************************************************************** + * Wait_For_Focus -- Wait for game to be in focus before proceeding * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/6/97 3:23PM ST : Created * + *=============================================================================================*/ +void Wait_For_Focus (void) +{ + CountDownTimerClass focus_timer; + focus_timer.Set(5*60); + ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + + /* + ** Process the message loop until we are in focus. + */ + if (!GameInFocus){ + CCDebugString ("C&C95 - Waiting for game to come into focus."); + do { + CCDebugString ("."); + Keyboard::Check(); + if (!focus_timer.Time()){ + CCDebugString ("C&C95 - Calling SetForgroundWindow.\n"); + SetForegroundWindow ( MainWindow ); + CCDebugString ("C&C95 - Calling ShowWindow.\n"); + ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + focus_timer.Set(5*60); + } + + }while (!GameInFocus); + CCDebugString ("\n"); + AllSurfaces.SurfacesRestored=FALSE; + } +} + + + + + +extern bool Spawn_WChat(bool can_launch); + +/*********************************************************************************************** + * Net_Fake_New_Dialog -- Just like Net_New_Dialog but without the Dialog. For internet play * + * * + * This 'dialog' does all the non-dialog game set up stuff that is done in the normal * + * network game set up dialog. The only visible button is 'cancel' * + * * + * INPUT: Nothing * + * * + * OUTPUT: true if successfully connected * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/24/96 10:34AM ST : Created * + *=============================================================================================*/ +static int Net_Fake_New_Dialog(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + int d_dialog_w = 120*factor; // dialog width + int d_dialog_h = 80*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + //d_playerlist_w = 100; + int d_playerlist_w = 106*factor; + int d_playerlist_h = 27*factor; + //int d_playerlist_x = 10 * factor; //off screen + int d_playerlist_x = 500*factor; //10 * factor; //off screen + int d_playerlist_y = d_dialog_y + 20; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*factor; +#else + int d_cancel_w = 45*factor; +#endif + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx - (d_cancel_w / 2); + int d_cancel_y = d_dialog_y + d_dialog_h - 20*factor; + +#if (GERMAN | FRENCH) + int width=160*factor; + int height=80*factor; +#else + int width=120*factor; + int height=80*factor; +#endif //GERMAN | FRENCH + + bool player_joined = false; + CountDownTimerClass join_timer; + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String((char*)Text_String(TXT_CONNECTING), SeenBuff.Get_Height(), width, height); + +#if (GERMAN | FRENCH) + d_dialog_w = width + 25*factor; + d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + d_cancel_x = d_dialog_cx - (d_cancel_w / 2); +#endif + + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_CANCEL = 100, + BUTTON_PLAYERLIST, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_MESSAGE, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + char credbuf[CREDITSBUF_MAX]; // for credit edit box + int old_cred; // old value in credits buffer + int transmit; // 1 = re-transmit new game options + + long ok_timer = 0; // for timing OK button + int rc; + int i,j; + char *item; + int tabs[] = {77*factor}; // tabs for player list box + + long ping_timer = 0; // for sending Ping packets + + unsigned char tmp_id[MAX_PLAYERS]; // temp storage for sorting player ID's + int min_index; // for sorting player ID's + unsigned char min_id; // for sorting player ID's + unsigned char id; // connection ID + JoinEventType whahoppa; // event generated by received packets + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + ColorListClass playerlist(BUTTON_PLAYERLIST, + d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + + CCDebugString ("C&C95 - In new game dialog - initialising lists.\n"); + /* + ------------------------- Build the button list -------------------------- + */ + commands = &playerlist; + cancelbtn.Add_Tail(*commands); + + playerlist.Set_Tabs(tabs); + + /* + ----------------------------- Various Inits ------------------------------ + */ + + sprintf(credbuf, "%d", MPlayerCredits); + old_cred = MPlayerCredits; + + /*........................................................................ + Init other scenario parameters + ........................................................................*/ + Special.IsTGrowth = MPlayerTiberium; + Special.IsTSpread = MPlayerTiberium; + transmit = 0; + + /*........................................................................ + Init player color-used flags + ........................................................................*/ + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + ColorUsed[i] = 0; // init all colors to available + } + ColorUsed[MPlayerColorIdx] = 1; // set my color to used + playerlist.Set_Selected_Style(ColorListClass::SELECT_BAR, CC_GREEN_SHADOW); + + /*........................................................................ + Init random-number generator, & create a seed to be used for all random + numbers from here on out + ........................................................................*/ + randomize(); + Seed = rand(); + + /*------------------------------------------------------------------------ + Add myself to the list. Note that since I'm not in the Players Vector, + the Vector & listbox are now 1 out of sync. + ------------------------------------------------------------------------*/ + item = new char [MPLAYER_NAME_MAX + 4]; + if (MPlayerHouse==HOUSE_GOOD) { + sprintf(item,"%s\t%s",MPlayerName,Text_String(TXT_G_D_I)); + } else { + sprintf(item,"%s\t%s",MPlayerName,Text_String(TXT_N_O_D)); + } + playerlist.Add_Item(item, MPlayerTColors[MPlayerColorIdx]); + + Wait_For_Focus(); + + CCDebugString ("C&C95 - About to uncompress title page.\n"); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + CCDebugString ("C&C95 - About to set the palette.\n"); + Set_Palette(Palette); + CCDebugString ("C&C95 - Palette was set OK.\n"); + + if (LogicPage != &SeenBuff && LogicPage!= &HidPage){ + CCDebugString ("C&C95 - Logic page invalid"); + Set_Logic_Page (SeenBuff); + } + + char a_buffer [128]; + sprintf (a_buffer, "Number of players:%d", Players.Count()); + CCDebugString (a_buffer); + + +#ifdef VIRTUAL_SUBNET_SERVER + /* + ** Send a bogus packet to wake up the VSS + */ + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = (NetCommandType)50; //Invalid command + strcpy (GPacket.Name, MPlayerName); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), 0, NULL); +#endif //VIRTUAL_SUBNET_SERVER + + + CCDebugString ("C&C95 - About to reveal mouse\n"); + while (Get_Mouse_State() > 0) Show_Mouse(); + + /* + ---------------------------- Processing loop ----------------------------- + */ + CCDebugString ("C&C95 - Entering join dialogue loop\n"); + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_NONE, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(TXT_CONNECTING, d_dialog_cx-width/2, d_dialog_y + 25*factor, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF, bail out with error code + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_SIGN_OFF; + strcpy (GPacket.Name, MPlayerName); + + /*............................................................... + Broadcast my sign-off over my network + ...............................................................*/ + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + /*............................................................... + Broadcast my sign-off over a bridged network if there is one + ...............................................................*/ + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + /*............................................................... + And now, just be absolutely sure, send my sign-off to each + player in my game. (If there's a bridge between us, the other + player will have specified my address, so he can cross the + bridge; but I may not have specified a bridge address, so the + only way I have of crossing the bridge is to send a packet + directly to him.) + ...............................................................*/ + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 1, + &(Players[i]->Address)); + Ipx.Service(); + } + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + MPlayerGameName[0] = 0; + process = false; + rc = false; + Send_Data_To_DDE_Server ("Hello", strlen("Hello"), DDEServerClass::DDE_CONNECTION_FAILED); + GameStatisticsPacketSent = false; + Spawn_WChat(false); + break; + + /*------------------------------------------------------------------ + default: exit loop with TRUE status + ------------------------------------------------------------------*/ + default: +#ifdef VIRTUAL_SUBNET_SERVER + if (Players.Count() == InternetMaxPlayers-1){ + + +#else //VIRTUAL_SUBNET_SERVER + if (Players.Count() > 0){ +#endif // VIRTUAL_SUBNET_SERVER + +//char ddkks[128]; +//sprintf (ddkks, "C&C95 - Players.Count() = %d\n", Players.Count()); +//CCDebugString (ddkks); + + /* + ** Wait for several secs after receiving request to join before sending + ** start game packet + */ + if (!player_joined){ + player_joined = true; + join_timer.Set (3*60, true); + break; + }else{ + if (join_timer.Time()) break; + } + +CCDebugString ("C&C95 - Join timer expired\n"); + + /*............................................................... + If a new player has joined in the last second, don't allow + an OK; force a wait longer than 2 seconds (to give all players + a chance to know about this new guy) + ...............................................................*/ + i = MAX(Ipx.Global_Response_Time() * 2, 120); + while (TickCount.Time() - ok_timer < i) + Ipx.Service(); + + /*............................................................... + If there are at least 2 players, go ahead & play; error otherwise + ...............................................................*/ + if (MPlayerSolo || Players.Count() > 0) { + rc = TRUE; + process = FALSE; + } else { + CCMessageBox().Process (TXT_ONLY_ONE,TXT_OOPS,NULL); + display = REDRAW_ALL; + } + } + break; + } + + /*--------------------------------------------------------------------- + Process incoming packets + ---------------------------------------------------------------------*/ + whahoppa = Get_NewGame_Responses(&playerlist); + if (whahoppa == EV_NEW_PLAYER) { + ok_timer = TickCount.Time(); + transmit = 1; + } else { + if (whahoppa == EV_MESSAGE) { + display = REDRAW_MESSAGE; + } + } + + /*--------------------------------------------------------------------- + If our Transmit flag is set, we need to send out a game option packet + ---------------------------------------------------------------------*/ + if (transmit) { + for (i = 0; i < Players.Count(); i++) { + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_GAME_OPTIONS; + GPacket.ScenarioInfo.Scenario = ScenarioIdx; //MPlayerFilenum[ScenarioIdx]; + GPacket.ScenarioInfo.Credits = MPlayerCredits; + GPacket.ScenarioInfo.IsBases = MPlayerBases; + GPacket.ScenarioInfo.IsTiberium = MPlayerTiberium; + GPacket.ScenarioInfo.IsGoodies = MPlayerGoodies; + GPacket.ScenarioInfo.IsGhosties = MPlayerGhosts; + GPacket.ScenarioInfo.BuildLevel = BuildLevel; + GPacket.ScenarioInfo.UnitCount = MPlayerUnitCount; + GPacket.ScenarioInfo.Seed = Seed; + GPacket.ScenarioInfo.Special = Special; + GPacket.ScenarioInfo.GameSpeed = Options.GameSpeed; + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 1, &(Players[i]->Address) ); + } + transmit = 0; + } + + /*--------------------------------------------------------------------- + Ping every player in my game, to force the Global Channel to measure + the connection response time. + ---------------------------------------------------------------------*/ + if (TickCount.Time() - ping_timer > 15) { + memset (&GPacket, 0, sizeof(GlobalPacketType)); + GPacket.Command = NET_PING; + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 1, &(Players[i]->Address) ); + } + ping_timer = TickCount.Time(); + } + + /*--------------------------------------------------------------------- + Service the Ipx connections + ---------------------------------------------------------------------*/ + Ipx.Service(); + + /*--------------------------------------------------------------------- + Service the sounds & score; GameActive must be false at this point, + so Call_Back() doesn't intercept global messages from me! + ---------------------------------------------------------------------*/ + Call_Back(); + + } /* end of while */ + +CCDebugString ("C&C95 - Exited process loop\n"); + + /*------------------------------------------------------------------------ + Establish connections with all other players. + ------------------------------------------------------------------------*/ + if (rc) { + /*..................................................................... + Set the number of players in this game, and my ID + .....................................................................*/ + MPlayerCount = Players.Count() + 1; + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + /*..................................................................... + Get the scenario filename + .....................................................................*/ + Scenario = ScenarioIdx; //PlayerFilenum[ScenarioIdx]; We are passed actual number now from wchat not index from + //Scenario = MPlayerFilenum[ScenarioIdx]; + + /*..................................................................... + Compute frame delay value for packet transmissions: + - Divide global channel's response time by 8 (2 to convert to 1-way + value, 4 more to convert from ticks to frames) + .....................................................................*/ + MPlayerMaxAhead = MAX( (Ipx.Global_Response_Time() / 8), 2); + + /*..................................................................... + Send all players the NET_GO packet. Wait until all ACK's have been + received. + .....................................................................*/ +CCDebugString ("C&C95 - Sending the 'GO' packet\n"); + memset (&GPacket, 0, sizeof(GlobalPacketType)); + GPacket.Command = NET_GO; + GPacket.ResponseTime.OneWay = MPlayerMaxAhead; + for (i = 0; i < Players.Count(); i++) { +char flopbuf [128]; +sprintf (flopbuf, "Sending 'GO' packet to address %d\n", *((unsigned short*)&(Players[i]->Address))); +CCDebugString (flopbuf); + + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 1, &(Players[i]->Address) ); + /*.................................................................. + Wait for all the ACK's to come in. + ..................................................................*/ + while (Ipx.Global_Num_Send() > 0) + Ipx.Service(); + } + + /*..................................................................... + Form connections with all other players. Form the IPX Connection ID + from the player's Color (high byte) and House (low byte). This + will let us extract any player's color & house at any time. + Fill in 'tmp_id' while we're doing this. + .....................................................................*/ + for (i = 0; i < Players.Count(); i++) { + id = Build_MPlayerID (Players[i]->Player.Color, + Players[i]->Player.House); + + tmp_id[i] = id; + + Ipx.Create_Connection(id, Players[i]->Name, &(Players[i]->Address) ); + } + +#ifdef VIRTUAL_SUBNET_SERVER +CCDebugString ("C&C95 - Creating connection to the VSS\n"); + /* + ** Create an additional connection to the VSS + */ + if (UseVirtualSubnetServer){ + IPXAddressClass vss_global_address; + NetNodeType vss_node; + NetNumType vss_net; + memset (vss_net, 1, sizeof (vss_net)); + memset (vss_node, 0, sizeof (vss_node)); + vss_global_address.Set_Address(vss_net, vss_node); + Ipx.Create_Connection( VSS_ID, "VSS", &vss_global_address); + } +#endif //VIRTUAL_SUBNET_SERVER + tmp_id[i] = MPlayerLocalID; + + /*..................................................................... + Store every player's ID in the MPlayerID[] array. This array will + determine the order of event execution, so the ID's must be stored + in the same order on all systems. + .....................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + min_index = 0; + min_id = 0xff; + for (j = 0; j < MPlayerCount; j++) { + if (tmp_id[j] < min_id) { + min_id = tmp_id[j]; + min_index = j; + } + } + MPlayerID[i] = tmp_id[min_index]; + tmp_id[min_index] = 0xff; + } + /*..................................................................... + Fill in the array of player names, including my own. + .....................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + if (MPlayerID[i] == MPlayerLocalID) { + strcpy (MPlayerNames[i], MPlayerName); + } else { + strcpy (MPlayerNames[i], Ipx.Connection_Name(MPlayerID[i])); + } + } + } + + /*------------------------------------------------------------------------ + Init network timing values, using previous response times as a measure + of what our retry delta & timeout should be. + ------------------------------------------------------------------------*/ + Ipx.Set_Timing (Ipx.Global_Response_Time() + 2, -1, + Ipx.Global_Response_Time() * 4); + + /*------------------------------------------------------------------------ + Clear all lists + ------------------------------------------------------------------------*/ + Clear_Player_List(&playerlist); + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Show_Mouse(); + + if (rc){ + Wait_For_Focus(); + } + + return(rc); +} + + + + + + +/*********************************************************************************************** + * Net_Fake_Join_Dialog -- Like Net_Join_Dialog but with no dialogs. For Internet Play. * + * * + * This 'dialog' does all the non-dialog game set up stuff that is done in the normal * + * network game set up dialog. The only visible button is 'cancel' * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: 0 = good, -1 = bad * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/24/96 11:07AM ST : Created * + *=============================================================================================*/ + +static int Net_Fake_Join_Dialog(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ +/* ###Change collision detected! C:\PROJECTS\CODE\NETDLG.CPP... */ + int d_dialog_w = 120 *factor; // dialog width + int d_dialog_h = 80*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_margin1=10; + int d_txt6_h=15; + + int d_gamelist_w = 160*factor; + int d_gamelist_h = 27*factor; + int d_gamelist_x = 500*factor; //230*factor; //Off screen + //int d_gamelist_x = 230*factor; //Off screen + int d_gamelist_y = d_dialog_y + 20; + + int d_playerlist_w = 106*factor; + int d_playerlist_h = 27*factor; + //int d_playerlist_x = 10 * factor; //Off screen + int d_playerlist_x = 500*factor; //10 * factor; //Off screen + int d_playerlist_y = d_gamelist_y + 20; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*factor; +#else + int d_cancel_w = 45*factor; +#endif + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*factor; + + bool ready_to_go = false; + +#if (GERMAN | FRENCH) + int width=160*factor; + int height=80*factor; +#else + int width=120*factor; + int height=80*factor; +#endif //GERMAN | FRENCH + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String((char*)Text_String(TXT_CONNECTING), SeenBuff.Get_Height(), width, height); + +#if (GERMAN | FRENCH) + d_dialog_w = width + 25*factor; + d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + d_cancel_x = d_dialog_cx - (d_cancel_w / 2); +#endif + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_CANCEL = 100, + BUTTON_GAMELIST, + BUTTON_PLAYERLIST, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + JoinStateType joinstate = JOIN_NOTHING; // current "state" of this dialog + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int game_index = -1; // index of currently-selected game + int join_index = -1; // index of game we're joining + int rc = 0; // -1 = user cancelled, 1 = New + JoinEventType event; // event from incoming packet + int i,j; // loop counter + int parms_received; // 1 = game options received + + unsigned char tmp_id[MAX_PLAYERS]; // temp storage for sorting player ID's + int min_index; // for sorting player ID's + unsigned char min_id; // for sorting player ID's + unsigned char id; // connection ID + char * item; + unsigned long starttime; + + NodeNameType *who; + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + ColorListClass playerlist(BUTTON_PLAYERLIST, + d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + ListClass gamelist(BUTTON_GAMELIST, + d_gamelist_x, d_gamelist_y, d_gamelist_w, d_gamelist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + /* + ----------------------------- Various Inits ------------------------------ + */ + //MPlayerColorIdx = MPlayerPrefColor; // init my preferred color + + playerlist.Set_Selected_Style(ColorListClass::SELECT_NONE); + + Fancy_Text_Print("", 0, 0, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /* + --------------------------- Send network query --------------------------- + */ + CCDebugString ("C&C95 - About to call Send_Join_Queries.\n"); + Send_Join_Queries (game_index, 1, 0); + + Wait_For_Focus(); + + CCDebugString ("C&C95 - About to uncompress title page.\n"); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + CCDebugString ("C&C95 - About to set the palette.\n"); + Set_Palette(Palette); + CCDebugString ("C&C95 - Palette was set OK.\n"); + + if (LogicPage != &SeenBuff && LogicPage!= &HidPage){ + CCDebugString ("C&C95 - Logic page invalid\n"); + Set_Logic_Page (SeenBuff); + } + + + char a_buffer [128]; + sprintf (a_buffer, "C&C95 - Number of players:%d\n", Players.Count()); + CCDebugString (a_buffer); + + /* + ---------------------------- Init Mono Output ---------------------------- + */ + CCDebugString ("C&C95 - About to reveal mouse\n"); + while (Get_Mouse_State() > 0) Show_Mouse(); + + /* + ---------------------------- Processing loop ----------------------------- + */ + CCDebugString ("C&C95 - Entering join dialogue loop\n"); + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_NONE, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(TXT_CONNECTING, d_dialog_cx-width/2, d_dialog_y + 25*factor, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /* + .................... Rebuild the button list .................... + */ + cancelbtn.Zap(); + gamelist.Zap(); + playerlist.Zap(); + + commands = &cancelbtn; + gamelist.Add_Tail(*commands); + playerlist.Add_Tail(*commands); + } + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF + - If we're part of a game, stay in this dialog; otherwise, exit + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_SIGN_OFF; + strcpy(GPacket.Name,MPlayerName); + + /*............................................................... + If we're joined to a game, make extra sure the other players in + that game know I'm exiting; send my SIGN_OFF as an ack-required + packet. Do not send this packet to myself (index 0). + ...............................................................*/ + if (joinstate == JOIN_CONFIRMED) { + // + // Remove myself from the player list box + // + item = (char *)(playerlist.Get_Item(0)); + playerlist.Remove_Item(item); + delete [] item; + playerlist.Flag_To_Redraw(); + + // + // Remove myself from the Players list + // + who = Players[0]; + Players.Delete(0); + delete who; + + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 1, + &(Players[i]->Address)); + Ipx.Service(); + } + } + + /*............................................................... + Now broadcast my SIGN_OFF so other players looking at this game + know I'm leaving. + ...............................................................*/ + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + + if (IsBridge) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + Send_Data_To_DDE_Server ("Hello", strlen("Hello"), DDEServerClass::DDE_CONNECTION_FAILED); + GameStatisticsPacketSent = false; + Spawn_WChat(false); + process = false; + rc = -1; +#if (0) + if (joinstate != JOIN_CONFIRMED) { + process = false; + rc = -1; + } else { + MPlayerGameName[0] = 0; + joinstate = JOIN_NOTHING; + display = REDRAW_ALL; + } +#endif //(0) + break; + + /*------------------------------------------------------------------ + JOIN: send a join request packet & switch to waiting-for-confirmation + mode. (Request_To_Join fills in MPlayerName with my namebuf.) + ------------------------------------------------------------------*/ + default: + if (joinstate == JOIN_NOTHING && Games.Count()!=0){ + gamelist.Set_Selected_Index(0); + join_index = gamelist.Current_Index(); + parms_received = 0; + if (Request_To_Join (MPlayerName, join_index, &playerlist, MPlayerHouse, + MPlayerColorIdx)) { + joinstate = JOIN_WAIT_CONFIRM; + } else { + display = REDRAW_ALL; + } + } + break; + + } + + /*--------------------------------------------------------------------- + Resend our query packets + ---------------------------------------------------------------------*/ + Send_Join_Queries(game_index, 0, 0); + + /*--------------------------------------------------------------------- + Process incoming packets + ---------------------------------------------------------------------*/ + event = Get_Join_Responses(&joinstate, &gamelist, &playerlist, + join_index); + /*..................................................................... + If we've changed state, redraw everything; if we're starting the game, + break out of the loop. If we've just joined, send out a player query + so I'll get added to the list instantly. + .....................................................................*/ + if (event == EV_STATE_CHANGE) { + display = REDRAW_ALL; + if (joinstate==JOIN_GAME_START) { +CCDebugString ("C&C95 - Received 'GO' packet\n"); + + ready_to_go = true; + } else { + + /*.................................................................. + If we're newly-confirmed, immediately send out a player query + ..................................................................*/ + if (joinstate==JOIN_CONFIRMED) { + + Clear_Player_List(&playerlist); + + item = new char [MPLAYER_NAME_MAX + 4]; + if (MPlayerHouse==HOUSE_GOOD) { + sprintf(item,"%s\t%s",MPlayerName,Text_String(TXT_G_D_I)); + } else { + sprintf(item,"%s\t%s",MPlayerName,Text_String(TXT_N_O_D)); + } + playerlist.Add_Item (item, MPlayerTColors[MPlayerColorIdx]); + + who = new NodeNameType; + strcpy(who->Name, MPlayerName); + who->Address = IPXAddressClass(); + who->Player.House = MPlayerHouse; + who->Player.Color = MPlayerColorIdx; + Players.Add (who); + + Send_Join_Queries (game_index, 0, 1); + } else { + + /*.................................................................. + If we've been rejected, clear any messages we may have been typing. + ..................................................................*/ + if (joinstate==JOIN_REJECTED) { + + // + // Remove myself from the player list box + // + item = (char *)(playerlist.Get_Item(0)); + if (item){ + playerlist.Remove_Item(item); + delete [] item; + playerlist.Flag_To_Redraw(); + } + + // + // Remove myself from the Players list + // + if (Players.Count()){ + who = Players[0]; + Players.Delete(0); + delete who; + } + } + } + } + } else + + /*..................................................................... + If a new game is detected, and it's the first game on our list, + automatically send out a player query for that game. + .....................................................................*/ + if (event == EV_NEW_GAME && gamelist.Count()==1) { + gamelist.Set_Selected_Index(0); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, 0, 1); + } else + + /*..................................................................... + If the game options have changed, print them. + .....................................................................*/ + if (event == EV_GAME_OPTIONS) { + parms_received = 1; + display = REDRAW_MESSAGE; + } else + + /*..................................................................... + Draw an incoming message + .....................................................................*/ + if (event == EV_MESSAGE) { + display = REDRAW_MESSAGE; + } else + + /*..................................................................... + A game before the one I've selected is gone, so we have a new index now. + 'game_index' must be kept set to the currently-selected list item, so + we send out queries for the currently-selected game. It's therefore + imperative that we detect any changes to the game list. + If we're joined in a game, we must decrement our game_index to keep + it aligned with the game we're joined to. + .....................................................................*/ + if (event == EV_GAME_SIGNOFF) { + if (joinstate==JOIN_CONFIRMED) { + game_index--; + join_index--; + gamelist.Set_Selected_Index(join_index); + } else { + gamelist.Flag_To_Redraw(); + Clear_Player_List(&playerlist); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, 0, 1); + } + } + + /*--------------------------------------------------------------------- + Service the Ipx connections + ---------------------------------------------------------------------*/ + Ipx.Service(); + + /*--------------------------------------------------------------------- + Clean out the Game List; if an old entry is found: + - Remove it + - Clear the player list + - Send queries for the new selected game, if there is one + ---------------------------------------------------------------------*/ + for (i = 0; i < Games.Count(); i++) { + if (TickCount.Time() - Games[i]->Game.LastTime > 400) { + Games.Delete(Games[i]); + item = (char *)(gamelist.Get_Item (i)); + gamelist.Remove_Item (item); + delete [] item; + if (i <= game_index) { + gamelist.Flag_To_Redraw(); + Clear_Player_List(&playerlist); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, 0, 1); + } + } + } + + /* + ** If we were flagged to start the game and we recognise both players then quit the loop + */ + +//char ddkks[128]; +//sprintf (ddkks, "C&C95 - Players.Count() = %d\n", Players.Count()); +//CCDebugString (ddkks); + if (ready_to_go){ // && Players.Count() == InternetMaxPlayers){ + rc = 0; + process = false; + } + + /*--------------------------------------------------------------------- + Service the sounds & score; GameActive must be false at this point, + so Call_Back() doesn't intercept global messages from me! + ---------------------------------------------------------------------*/ + Call_Back(); + } + + /*------------------------------------------------------------------------ + Establish connections with all other players. + ------------------------------------------------------------------------*/ + if (rc == 0) { + /*..................................................................... + If the other guys are playing a scenario I don't have (sniff), I can't + play. Try to bail gracefully. + .....................................................................*/ + if (ScenarioIdx==-1) { + CCMessageBox().Process (TXT_UNABLE_PLAY_WAAUGH); + + // + // Remove myself from the player list box + // + item = (char *)(playerlist.Get_Item(0)); + playerlist.Remove_Item(item); + delete [] item; + playerlist.Flag_To_Redraw(); + + // + // Remove myself from the Players list + // + who = Players[0]; + Players.Delete(0); + delete who; + + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_SIGN_OFF; + strcpy (GPacket.Name, MPlayerName); + + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 1, + &(Players[i]->Address)); + Ipx.Service(); + } + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + + if (IsBridge) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + rc = -1; + + } else { + + /*.................................................................. + Set the number of players in this game, and my ID + ..................................................................*/ + MPlayerCount = Players.Count(); + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + /*.................................................................. + Get the scenario number + ..................................................................*/ + Scenario = ScenarioIdx; //PlayerFilenum[ScenarioIdx]; We are passed actual number now from wchat not index from + + /*.................................................................. + Form connections with all other players. Form the IPX Connection ID + from the player's Color and House. This will let us extract any + player's color & house at any time. Fill in 'tmp_id' while we're + doing this. + ..................................................................*/ + for (i = 0; i < Players.Count(); i++) { + + /*............................................................... + Only create the connection if it's not myself! + ...............................................................*/ + if (strcmp (MPlayerName, Players[i]->Name)) { + id = Build_MPlayerID(Players[i]->Player.Color, + Players[i]->Player.House); + + tmp_id[i] = id; + + Ipx.Create_Connection((int)id, Players[i]->Name, &(Players[i]->Address) ); + } else { + tmp_id[i] = MPlayerLocalID; + } + } + +#ifdef VIRTUAL_SUBNET_SERVER + /* + ** Create an additional connection to the VSS + */ + if (UseVirtualSubnetServer){ + IPXAddressClass vss_global_address; + NetNodeType vss_node; + NetNumType vss_net; + memset (vss_net, 1, sizeof (vss_net)); + memset (vss_node, 0, sizeof (vss_node)); + vss_global_address.Set_Address(vss_net, vss_node); + Ipx.Create_Connection( VSS_ID, "VSS", &vss_global_address); + } +#endif //VIRTUAL_SUBNET_SERVER + + /*.................................................................. + Store every player's ID in the MPlayerID[] array. This array will + determine the order of event execution, so the ID's must be stored + in the same order on all systems. + ..................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + min_index = 0; + min_id = 0xff; + for (j = 0; j < MPlayerCount; j++) { + if (tmp_id[j] < min_id) { + min_id = tmp_id[j]; + min_index = j; + } + } + MPlayerID[i] = tmp_id[min_index]; + tmp_id[min_index] = 0xff; + } + /*.................................................................. + Fill in the array of player names, including my own. + ..................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + if (MPlayerID[i] == MPlayerLocalID) { + strcpy (MPlayerNames[i], MPlayerName); + } else { + strcpy (MPlayerNames[i], Ipx.Connection_Name(MPlayerID[i])); + } + } + } + /*--------------------------------------------------------------------- + Wait a while, polling the IPX service routines, to give our ACK + a chance to get to the other system. If he doesn't get our ACK, he'll + be waiting the whole time we load MIX files. + ---------------------------------------------------------------------*/ + i = MAX(Ipx.Global_Response_Time() * 2, 120); + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < i) { + Ipx.Service(); + } + } + + /*------------------------------------------------------------------------ + Init network timing values, using previous response times as a measure + of what our retry delta & timeout should be. + ------------------------------------------------------------------------*/ + Ipx.Set_Timing (Ipx.Global_Response_Time() + 2, -1, + Ipx.Global_Response_Time() * 4); + + /*------------------------------------------------------------------------ + Clear all lists + ------------------------------------------------------------------------*/ + Clear_Game_List(&gamelist); + Clear_Player_List(&playerlist); + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Show_Mouse(); + + if (rc != -1){ + Wait_For_Focus(); + } + + return(rc); +} + + + +#endif diff --git a/NETDLG.CPP.BAK b/NETDLG.CPP.BAK new file mode 100644 index 0000000..9201b4f --- /dev/null +++ b/NETDLG.CPP.BAK @@ -0,0 +1,5295 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\netdlg.cpv 2.17 16 Oct 1995 16:52:26 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NETDLG.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : January 23, 1995 * + * * + * Last Update : July 8, 1995 [BRR] * + * * + *---------------------------------------------------------------------------------------------* + * * + * These routines establish & maintain peer-to-peer connections between this system * + * and all others in the game. Each system finds out the IPX address of the others, * + * and forms a direct connection (IPXConnectionClass) to that system. Systems are * + * found out via broadcast queries. Every system broadcasts its queries, and every * + * system replies to queries it receives. At the point when the game owner signals * + * 'OK', every system must know about all the other systems in the game. * + * * + * How Bridges are handled: * + * Currently, bridges are handled by specifying the destination IPX address of the * + * "server" (game owner's system) on the command-line. This address is used to * + * derive a broadcast address to that destination network, and this system's queries * + * are broadcast over its network & the server's network; replies to the queries come * + * with each system's IPX address attached, so once we have the address, we can form * + * a connection with any system on the bridged net. * + * * + * The flaw in this plan is that we can only cross one bridge. If there are 3 nets * + * bridged (A, B, & C), and the server is on net B, and we're on net A, our broadcasts * + * will reach nets A & B, but not C. The way to circumvent this (if it becomes a problem) * + * would be to have the server tell us what other systems are in its game, not each * + * individual player's system. Thus, each system would find out about all the other systems * + * by interacting with the game's owner system (this would be more involved than what * + * I'm doing here). * + * * + * Here's a list of all the different packets sent over the Global Channel: * + * * + * NET_QUERY_GAME * + * (no other data) * + * NET_ANSWER_GAME * + * Name: game owner's name * + * GameInfo: game's version & open state * + * NET_QUERY_PLAYER * + * Name: name of game we want players to respond for * + * NET_ANSWER_PLAYER * + * Name: player's name * + * PlayerInfo: info about player * + * NET_QUERY_JOIN * + * Name: name of player wanting to join * + * PlayerInfo: player's requested house & color * + * NET_CONFIRM_JOIN * + * PlayerInfo: approves player's house & color * + * NET_REJECT_JOIN * + * (no other data) * + * NET_GAME_OPTIONS * + * ScenarioInfo: info about scenario * + * NET_SIGN_OFF * + * Name: name of player signing off * + * NET_PING * + * (no other data) * + * NET_GO * + * Delay: value of one-way response time, in frames * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Clear_Game_List -- Clears the game-name listbox & 'Games' Vector * + * Clear_Player_List -- Clears the player-name listbox & Vector * + * Destroy_Connection -- destroys the given connection * + * Get_Join_Responses -- sends queries for the Join Dialog * + * Get_NewGame_Responses -- processes packets for New Game dialog * + * Init_Network -- initializes network stuff * + * Net_Join_Dialog -- lets user join an existing game, or start a new one * + * Net_New_Dialog -- lets user start a new game * + * Process_Global_Packet -- responds to remote queries * + * Remote_Connect -- handles connecting this user to others * + * Request_To_Join -- Sends a JOIN request packet to game owner * + * Send_Join_Queries -- sends queries for the Join Dialog * + * Shutdown_Network -- shuts down network stuff * + * Compute_Name_CRC -- computes CRC from char string * + * Net_Reconnect_Dialog -- Draws/updates the network reconnect dialog * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include +#include "tcpip.h" + +#define SHOW_MONO 0 + + +#ifndef DEMO + +/*--------------------------------------------------------------------------- +The possible states of the join-game dialog +---------------------------------------------------------------------------*/ +typedef enum { + JOIN_REJECTED = -1, // we've been rejected + JOIN_NOTHING, // we're not trying to join a game + JOIN_WAIT_CONFIRM, // we're asking to join, & waiting for confirmation + JOIN_CONFIRMED, // we've been confirmed + JOIN_GAME_START, // the game we've joined is starting +} JoinStateType; + +/*--------------------------------------------------------------------------- +The possible return codes from Get_Join_Responses() +---------------------------------------------------------------------------*/ +typedef enum { + EV_NONE, // nothing happened + EV_STATE_CHANGE, // Join dialog is in a new state + EV_NEW_GAME, // a new game was detected + EV_NEW_PLAYER, // a new player was detected + EV_PLAYER_SIGNOFF, // a player has signed off + EV_GAME_SIGNOFF, // a gamed owner has signed off + EV_GAME_OPTIONS, // a game options packet was received + EV_MESSAGE, // a message was received +} JoinEventType; + + +/* +******************************** Prototypes ********************************* +*/ +static int Net_Join_Dialog(void); +static void Clear_Game_List (ListClass *gamelist); +static void Clear_Player_List (ListClass *playerlist); +static int Request_To_Join (char *playername, int join_index, ListClass *playerlist, + HousesType house, int color); +static void Send_Join_Queries(int curgame, int gamenow, int playernow); +static JoinEventType Get_Join_Responses(JoinStateType *joinstate, ListClass *gamelist, + ColorListClass *playerlist, int join_index); +static int Net_New_Dialog(void); +static JoinEventType Get_NewGame_Responses(ColorListClass *playerlist); +static int Net_Fake_New_Dialog(void); +static int Net_Fake_Join_Dialog(void); + + +/*********************************************************************************************** + * Init_Network -- initializes network stuff * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = Initialization OK, false = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +bool Init_Network (void) +{ + NetNumType net; + NetNodeType node; + + /*------------------------------------------------------------------------ + This call allocates all necessary queue buffers, allocates Real-mode + memory, and commands IPX to start listening on the Global Channel. + ------------------------------------------------------------------------*/ + if (!Ipx.Init()) + return(false); + + /*------------------------------------------------------------------------ + Allocate our "meta-packet" buffer + ------------------------------------------------------------------------*/ + if (!MetaPacket) { + MetaPacket = new char [sizeof (EventClass) * MAX_EVENTS]; + } + + /*------------------------------------------------------------------------ + Set up the IPX manager to cross a bridge + ------------------------------------------------------------------------*/ + if (!(GameToPlay == GAME_INTERNET)){ + if (IsBridge) { + BridgeNet.Get_Address(net,node); + Ipx.Set_Bridge(net); + } + } + + return(true); + +} /* end of Init_Network */ + + +/*********************************************************************************************** + * Shutdown_Network -- shuts down network stuff * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +void Shutdown_Network (void) +{ + /*------------------------------------------------------------------------ + Broadcast a sign-off packet, by sending the packet over the Global Channel, + telling the IPX Manager that no ACK is required, and specifying a NULL + destination address. + ------------------------------------------------------------------------*/ + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_SIGN_OFF; + strcpy (GPacket.Name, MPlayerName); + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), 0, NULL); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), 0, NULL); + + if (IsBridge && !Winsock.Get_Connected()) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, &BridgeNet); + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, &BridgeNet); + } + + /*------------------------------------------------------------------------ + Wait for the packets to finish going out (or the Global Channel times out) + ------------------------------------------------------------------------*/ + for (;;) { + if (Ipx.Global_Num_Send()==0) { + break; + } + Ipx.Service(); + } + + /*------------------------------------------------------------------------ + Delete our "meta-packet" + ------------------------------------------------------------------------*/ + delete [] MetaPacket; + MetaPacket = 0; + + /*------------------------------------------------------------------------ + If I was in a game, I'm not now, so clear the game name + ------------------------------------------------------------------------*/ + MPlayerGameName[0] = 0; +} + + +/*********************************************************************************************** + * Process_Global_Packet -- responds to remote queries * + * * + * The only commands from other systems this routine responds to are NET_QUERY_GAME * + * and NET_QUERY_PLAYER. The other commands are too context-specific to be able * + * to handle here, such as joining the game or signing off; but this routine handles * + * the majority of the program's needs. * + * * + * INPUT: * + * packet ptr to packet to process * + * address source address of sender * + * * + * OUTPUT: * + * true = packet was processed, false = wasn't * + * * + * WARNINGS: * + * MPlayerName & MPlayerGameName must have been filled in before this function * + * can be called. * + * * + * HISTORY: * + * 02/15/1995 BR : Created. * + *=============================================================================================*/ +bool Process_Global_Packet(GlobalPacketType *packet, IPXAddressClass *address) +{ + GlobalPacketType mypacket; + + /* + ---------------- Another system asking what game this is ----------------- + */ + if (packet->Command==NET_QUERY_GAME && NetStealth==0) { + /*..................................................................... + If the game is closed, let every player respond, and let the sender of + the query sort it all out. This way, if the game's host exits the game, + the game still shows up on other players' dialogs. + If the game is open, only the game owner may respond. + .....................................................................*/ + if (strlen(MPlayerName) > 0 && strlen(MPlayerGameName) > 0 && + ((!NetOpen) || (NetOpen && !strcmp(MPlayerName,MPlayerGameName)))) { + memset (packet, 0, sizeof(GlobalPacketType)); + + mypacket.Command = NET_ANSWER_GAME; + strcpy(mypacket.Name, MPlayerGameName); +#ifdef PATCH + if (IsV107) { + mypacket.GameInfo.Version = 1; + } else { + mypacket.GameInfo.Version = 2; + } +#else + mypacket.GameInfo.Version = Version_Number(); +#endif + mypacket.GameInfo.IsOpen = NetOpen; + + Ipx.Send_Global_Message (&mypacket, sizeof(GlobalPacketType), 1, + address); + } + return(true); + } else { + + /* + ----------------- Another system asking what player I am ----------------- + */ + if (packet->Command==NET_QUERY_PLAYER && + !strcmp (packet->Name, MPlayerGameName) && + (strlen(MPlayerGameName) > 0) && NetStealth==0) { + memset (packet, 0, sizeof(GlobalPacketType)); + + mypacket.Command = NET_ANSWER_PLAYER; + strcpy(mypacket.Name, MPlayerName); + mypacket.PlayerInfo.House = MPlayerHouse; + mypacket.PlayerInfo.Color = MPlayerColorIdx; + mypacket.PlayerInfo.NameCRC = Compute_Name_CRC(MPlayerGameName); + + Ipx.Send_Global_Message (&mypacket, sizeof(GlobalPacketType), 1, address); + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * Destroy_Connection -- destroys the given connection * + * * + * Call this routine when a connection goes bad, or another player signs off. * + * * + * INPUT: * + * id connection ID to destroy * + * error 0 = user signed off; 1 = connection error; otherwise, no error is shown. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/22/1995 BR : Created. * + *=============================================================================================*/ +void Destroy_Connection(int id, int error) +{ + int i,j; + HousesType house; + HouseClass *housep; + char txt[80]; + + /*------------------------------------------------------------------------ + Create a message to display to the user + ------------------------------------------------------------------------*/ + txt[0] = '\0'; + if (error==1) { + sprintf(txt,Text_String(TXT_CONNECTION_LOST),Ipx.Connection_Name(id)); + } else if (error==0) { + sprintf(txt,Text_String(TXT_LEFT_GAME),Ipx.Connection_Name(id)); + } + + if (strlen(txt)) { + Messages.Add_Message (txt, + MPlayerTColors[MPlayerID_To_ColorIndex((unsigned char)id)], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, 0, 0); + Map.Flag_To_Redraw(false); + } + + /*------------------------------------------------------------------------ + Delete the IPX connection, shift the MPlayerID's & MPlayerHouses' back one. + ------------------------------------------------------------------------*/ + Ipx.Delete_Connection(id); + + for (i = 0; i < MPlayerCount; i++) { + if (MPlayerID[i] == (unsigned char)id) { + /*.................................................................. + Turn the player's house over to the computer's AI + ..................................................................*/ + house = MPlayerHouses[i]; + housep = HouseClass::As_Pointer (house); + housep->IsHuman = false; + housep->IsStarted = true; + + /*.................................................................. + Move arrays back by one + ..................................................................*/ + for (j = i; j < MPlayerCount - 1; j++) { + MPlayerID[j] = MPlayerID[j + 1]; + MPlayerHouses[j] = MPlayerHouses[j + 1]; + strcpy (MPlayerNames[j], MPlayerNames[j+1]); + TheirProcessTime[j] = TheirProcessTime[j+1]; + } + } + } + + MPlayerCount--; + + /*------------------------------------------------------------------------ + If we're the last player left, tell the user. + ------------------------------------------------------------------------*/ + if (MPlayerCount == 1) { + sprintf(txt,"%s",Text_String(TXT_JUST_YOU_AND_ME)); + Messages.Add_Message (txt, + MPlayerTColors[MPlayerID_To_ColorIndex((unsigned char)id)], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, 0, 0); + Map.Flag_To_Redraw(false); + } + +} /* end of Destroy_Connection */ + + +/*********************************************************************************************** + * Remote_Connect -- handles connecting this user to others * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = connections established; false = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +bool Remote_Connect(void) +{ + int rc; + int stealth; // original state of NetStealth flag + + /*------------------------------------------------------------------------ + Init network timing parameters; these values should work for both a "real" + network, and a simulated modem network (ie Kali) + ------------------------------------------------------------------------*/ + Ipx.Set_Timing ( 30, // retry 2 times per second + -1, // ignore max retries + 600); // give up after 10 seconds + + /*------------------------------------------------------------------------ + Save the original value of the NetStealth flag, so we can turn stealth + off for now (during this portion of the dialogs, we must show ourselves) + ------------------------------------------------------------------------*/ + stealth = NetStealth; + NetStealth = 0; + + /*------------------------------------------------------------------------ + Init my game name to 0-length, since I haven't joined any game yet. + ------------------------------------------------------------------------*/ + MPlayerGameName[0] = 0; + + /*------------------------------------------------------------------------ + The game is now "open" for joining. Close it as soon as we exit this + routine. + ------------------------------------------------------------------------*/ + NetOpen = 1; + + /*------------------------------------------------------------------------ + Read the default values from the INI file + ------------------------------------------------------------------------*/ + Read_MultiPlayer_Settings (); + + /*------------------------------------------------------------------------ + Keep looping until something useful happens. + ------------------------------------------------------------------------*/ + while (1) { + /*--------------------------------------------------------------------- + Pop up the network Join/New dialog + ---------------------------------------------------------------------*/ + rc = Net_Join_Dialog(); + + /*--------------------------------------------------------------------- + -1 = user selected Cancel + ---------------------------------------------------------------------*/ + if (rc==-1) { + NetStealth = stealth; + NetOpen = 0; + return(false); + } else { + + /*--------------------------------------------------------------------- + 0 = user has joined an existing game; save values & return + ---------------------------------------------------------------------*/ + if (rc==0) { + Write_MultiPlayer_Settings (); + NetStealth = stealth; + NetOpen = 0; + + return(true); + } else { + + /*--------------------------------------------------------------------- + 1 = user requests New Network Game + ---------------------------------------------------------------------*/ + if (rc==1) { + /*.................................................................. + Pop up the New Network Game dialog; if user selects OK, return + 'true'; otherwise, return to the Join Dialog. + ..................................................................*/ + if (Net_New_Dialog()) { + Write_MultiPlayer_Settings (); + NetOpen = 0; + NetStealth = stealth; + NetOpen = 0; + + return(true); + } else { + continue; + } + } + } + } + } +} + + + +/*********************************************************************************************** + * Remote_Connect -- handles connecting this host to the server in an internet game * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = connections established; false = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +bool Server_Remote_Connect(void) +{ + int stealth; // original state of NetStealth flag + + /*------------------------------------------------------------------------ + Init network timing parameters; these values should work for both a "real" + network, and a simulated modem network (ie Kali) + ------------------------------------------------------------------------*/ + Ipx.Set_Timing ( 30, // retry 2 times per second + -1, // ignore max retries + 600); // give up after 10 seconds + + /*------------------------------------------------------------------------ + Save the original value of the NetStealth flag, so we can turn stealth + off for now (during this portion of the dialogs, we must show ourselves) + ------------------------------------------------------------------------*/ + stealth = NetStealth; + NetStealth = 0; + + /*------------------------------------------------------------------------ + The game is now "open" for joining. Close it as soon as we exit this + routine. + ------------------------------------------------------------------------*/ + NetOpen = 1; + + /*------------------------------------------------------------------------ + Read the default values from the INI file + ------------------------------------------------------------------------*/ + Read_MultiPlayer_Settings (); + + if (!Net_Fake_New_Dialog()){ + Write_MultiPlayer_Settings (); + return (false); + } + + NetOpen = 0; + NetStealth = stealth; + Write_MultiPlayer_Settings (); + return (true); +} + + +/*********************************************************************************************** + * Client_Remote_Connect -- handles connecting this client to the server in an internet game * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = connections established; false = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 ST : Created. * + *=============================================================================================*/ +bool Client_Remote_Connect(void) +{ + int rc; + int stealth; // original state of NetStealth flag + + /*------------------------------------------------------------------------ + Init network timing parameters; these values should work for both a "real" + network, and a simulated modem network (ie Kali) + ------------------------------------------------------------------------*/ + Ipx.Set_Timing ( 30, // retry 2 times per second + -1, // ignore max retries + 600); // give up after 10 seconds + + /*------------------------------------------------------------------------ + Save the original value of the NetStealth flag, so we can turn stealth + off for now (during this portion of the dialogs, we must show ourselves) + ------------------------------------------------------------------------*/ + stealth = NetStealth; + NetStealth = 0; + + /*------------------------------------------------------------------------ + The game is now "open" for joining. Close it as soon as we exit this + routine. + ------------------------------------------------------------------------*/ + NetOpen = 1; + + /*------------------------------------------------------------------------ + Read the default values from the INI file + ------------------------------------------------------------------------*/ + Read_MultiPlayer_Settings (); + + /*--------------------------------------------------------------------- + Pop up the network Join/New dialog + ---------------------------------------------------------------------*/ + rc = Net_Fake_Join_Dialog(); + Write_MultiPlayer_Settings (); + + NetStealth = stealth; + NetOpen = 0; + + if (rc == -1) { + return(false); + } else { + return(true); + } +} + + + +/*********************************************************************************************** + * Net_Join_Dialog -- lets user join an existing game or start a new one * + * * + * This dialog displays an edit field for the player's name, and a list of all non-stealth- * + * mode games. Clicking once on a game name displays a list of who's in that game. Clicking * + * "New" takes the user to the Net_New dialog, where he waits for other users to join his * + * game. All other input is done through this dialog. * + * * + * The dialog has several "states": * + * * + * 1) Initially, it waits for the user to fill in his/her name and then to select Join or New; * + * if New is selected, this dialog is exited. * + * * + * 2) If Join is selected, the Join & New buttons are removed, but the Cancel button remains. * + * The join request is transmitted to the game's owner, and the message "Waiting for * + * Confirmation" is displayed, until a confirmation or denial is received from the game's * + * owner. The user may click Cancel at this point to cancel the join request. * + * (Once Join is selected, the name editing field is disabled, and becomes a display-only * + * field. If cancel is selected, it reappears as an edit field.) The user can still click * + * around & see who's in which games. * + * * + * 3) If the join request is denied, the dialog re-initializes to its pre-join state; the * + * Join & New buttons reappear, & the Name field is available again. * + * * + * 4) If join confirmation is obtained, the message just changes to "Confirmed. Waiting for * + * Entry Signal." or some such nonsense. The user can still click around & see who's * + * in which games. * + * * + * Any game running in Stealth mode won't show up on this dialog. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Network Games ³ * + * ³ ³ * + * ³ Your Name: ____________ ³ * + * ³ House: [GDI] [NOD] ³ * + * ³ Desired Color: [ ][ ][ ][ ] ³ * + * ³ ³ * + * ³ Games Players ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³(Bill's Game )³³ ³ Peter Parker GDI ³³ ³ * + * ³ ³ Peter Parker's Game ÃÄ´ ³ Mary Jane GDI ÃÄ´ ³ * + * ³ ³(Magnum PI's Game )³ ³ ³ JJ Jameson NOD ³ ³ ³ * + * ³ ³ ÃÄ´ ³ ÃÄ´ ³ * + * ³ ³ ³³ ³ ³³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ Scenario: Big Long Description ³ * + * ³ Starting Credits: xxxx ³ * + * ³ Count: --- Level: --- ³ * + * ³ Bases: ON Crates: ON ³ * + * ³ Tiberium: ON AI Players: ON ³ * + * ³ ³ * + * ³ [Join] [Cancel] [New] ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ [Send Message] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * -1 = cancel, 0 = OK, 1 = New net game requested * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +static int Net_Join_Dialog(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ +/* ###Change collision detected! C:\PROJECTS\CODE\NETDLG.CPP... */ + int d_dialog_w = 287 *factor; // dialog width + int d_dialog_h = 198*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6*factor+1; // ht of 6-pt text + int d_margin1 = 5*factor; // large margin + int d_margin2 = 2*factor; // small margin + + int d_name_w = 70*factor; + int d_name_h = 9*factor; + int d_name_x = d_dialog_cx - 10*factor; + int d_name_y = d_dialog_y + d_margin1 + d_txt6_h + d_txt6_h; + + int d_gdi_w = 30*factor; + int d_gdi_h = 9*factor; + int d_gdi_x = d_dialog_cx - 10*factor; + int d_gdi_y = d_name_y + d_name_h + d_margin2; + + int d_nod_w = 30*factor; + int d_nod_h = 9*factor; + int d_nod_x = d_gdi_x + d_gdi_w; + int d_nod_y = d_name_y + d_name_h + d_margin2; + + int d_color_w = 10*factor; + int d_color_h = 9*factor; + int d_color_y = d_nod_y + d_nod_h + d_margin2; + + int d_gamelist_w = 160*factor; + int d_gamelist_h = 27*factor; + int d_gamelist_x = d_dialog_x + d_margin1; + int d_gamelist_y = d_color_y + d_color_h + d_margin1 + d_txt6_h; + + int d_playerlist_w = 106*factor; + int d_playerlist_h = 27*factor; + int d_playerlist_x = d_dialog_x + d_dialog_w - d_margin1 - d_playerlist_w; + int d_playerlist_y = d_color_y + d_color_h + d_margin1 + d_txt6_h; + + int d_msg1_y = d_gamelist_y + d_gamelist_h + d_margin1; + int d_msg2_y = d_msg1_y + d_txt6_h; + int d_msg3_y = d_msg2_y + d_txt6_h; + int d_msg4_y = d_msg3_y + d_txt6_h; + int d_msg5_y = d_msg4_y + d_txt6_h; + + int d_join_w = 40*factor; + int d_join_h = 9*factor; + int d_join_x = d_dialog_x + (d_dialog_w / 6) - (d_join_w / 2); + int d_join_y = d_msg5_y + d_txt6_h + d_margin1; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*factor; +#else + int d_cancel_w = 40*factor; +#endif + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_msg5_y + d_txt6_h + d_margin1; + + int d_new_w = 40*factor; + int d_new_h = 9*factor; + int d_new_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_new_w / 2); + int d_new_y = d_msg5_y + d_txt6_h + d_margin1; + + int d_message_w = d_dialog_w - (d_margin1 * 2); + int d_message_h = 34*factor; + int d_message_x = d_dialog_x + d_margin1; + int d_message_y = d_cancel_y + d_cancel_h + d_margin1; + + int d_send_w = 80*factor; + int d_send_h = 9*factor; + int d_send_x = d_dialog_cx - (d_send_w / 2); + int d_send_y = d_message_y + d_message_h + d_margin2; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_NAME = 100, + BUTTON_GDI, + BUTTON_NOD, + BUTTON_GAMELIST, + BUTTON_PLAYERLIST, + BUTTON_JOIN, + BUTTON_CANCEL, + BUTTON_NEW, + BUTTON_SEND, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + int cbox_x[] = { d_gdi_x, + d_gdi_x + d_color_w, + d_gdi_x + (d_color_w * 2), + d_gdi_x + (d_color_w * 3), + d_gdi_x + (d_color_w * 4), + d_gdi_x + (d_color_w * 5)}; + + JoinStateType joinstate = JOIN_NOTHING; // current "state" of this dialog + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int tabs[] = {77*factor}; // tabs for player list box + int game_index = -1; // index of currently-selected game + int join_index = -1; // index of game we're joining + int rc = 0; // -1 = user cancelled, 1 = New + JoinEventType event; // event from incoming packet + int i,j; // loop counter + char txt[80]; + char const *p; + int parms_received; // 1 = game options received + int found; + + unsigned char tmp_id[MAX_PLAYERS]; // temp storage for sorting player ID's + int min_index; // for sorting player ID's + unsigned char min_id; // for sorting player ID's + unsigned char id; // connection ID + char * item; + unsigned long starttime; + + NodeNameType *who; + + int message_length; + int sent_so_far; + unsigned short magic_number; + unsigned short crc; + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + EditClass name_edt (BUTTON_NAME, + namebuf, MPLAYER_NAME_MAX, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_name_x, d_name_y, d_name_w, d_name_h, EditClass::ALPHANUMERIC); + + TextButtonClass gdibtn(BUTTON_GDI, TXT_G_D_I, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_gdi_x, d_gdi_y, d_gdi_w, d_gdi_h); + + TextButtonClass nodbtn(BUTTON_NOD, TXT_N_O_D, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_nod_x, d_nod_y, d_nod_w, d_nod_h); + + ListClass gamelist(BUTTON_GAMELIST, + d_gamelist_x, d_gamelist_y, d_gamelist_w, d_gamelist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + ColorListClass playerlist(BUTTON_PLAYERLIST, + d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + TextButtonClass joinbtn(BUTTON_JOIN, TXT_JOIN, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#ifdef FRENCH + d_join_x, d_join_y); +#else + d_join_x, d_join_y, d_join_w, d_join_h); +#endif + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + d_cancel_x, d_cancel_y); +#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +#endif + + TextButtonClass newbtn(BUTTON_NEW, TXT_NEW, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_new_x, d_new_y, d_new_w, d_new_h); + + TextButtonClass sendbtn(BUTTON_SEND, TXT_SEND_MESSAGE, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + d_send_x, d_send_y); +#else + d_send_x, d_send_y, d_send_w, d_send_h); +#endif + + playerlist.Set_Tabs(tabs); + + /* + ----------------------------- Various Inits ------------------------------ + */ + MPlayerColorIdx = MPlayerPrefColor; // init my preferred color + strcpy (namebuf, MPlayerName); // set my name + name_edt.Set_Text(namebuf,MPLAYER_NAME_MAX); + name_edt.Set_Color (MPlayerTColors[MPlayerColorIdx]); + + playerlist.Set_Selected_Style(ColorListClass::SELECT_NONE); + + if (MPlayerHouse==HOUSE_GOOD) { + gdibtn.Turn_On(); + } else { + nodbtn.Turn_On(); + } + + Fancy_Text_Print("", 0, 0, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Messages.Init (d_message_x + 2, d_message_y + 2, 4, + MAX_MESSAGE_LENGTH, d_txt6_h); + + /* + --------------------------- Send network query --------------------------- + */ + Send_Join_Queries (game_index, 1, 0); + + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + + /* + ---------------------------- Init Mono Output ---------------------------- + */ + #if(SHOW_MONO) + Ipx.Configure_Debug(-1, sizeof (GlobalHeaderType), + sizeof(NetCommandType), GlobalPacketNames, 11); + Ipx.Mono_Debug_Print(-1,1); + #endif + while (Get_Mouse_State() > 0) Show_Mouse(); + + /* + ---------------------------- Processing loop ----------------------------- + */ + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + #if(SHOW_MONO) + Ipx.Mono_Debug_Print(-1,0); + #endif + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_JOIN_NETWORK_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(TXT_YOUR_NAME, + d_name_x - 5, d_name_y + 1, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_SIDE_COLON, + d_gdi_x - 5, d_gdi_y + 1, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_COLOR_COLON, + cbox_x[0] - 5, d_color_y + 1, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_GAMES, + d_gamelist_x + (d_gamelist_w / 2), d_gamelist_y - d_txt6_h, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_PLAYERS, + d_playerlist_x + (d_playerlist_w / 2), d_playerlist_y - d_txt6_h, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................... + Join-state-specific labels: + ...............................................................*/ + if (joinstate > JOIN_NOTHING) { + Fancy_Text_Print(namebuf, d_name_x, d_name_y + 1, CC_GREEN, + TBLACK, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + if (MPlayerHouse==HOUSE_GOOD) { + Fancy_Text_Print(TXT_G_D_I, d_gdi_x, d_gdi_y + 1, CC_GREEN, + TBLACK, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } else { + Fancy_Text_Print(TXT_N_O_D, d_gdi_x, d_gdi_y + 1, CC_GREEN, + TBLACK, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + } + + /* + .................... Rebuild the button list .................... + */ + cancelbtn.Zap(); + gamelist.Zap(); + playerlist.Zap(); + gdibtn.Zap(); + nodbtn.Zap(); + name_edt.Zap(); + joinbtn.Zap(); + newbtn.Zap(); + sendbtn.Zap(); + + commands = &cancelbtn; + gamelist.Add_Tail(*commands); + playerlist.Add_Tail(*commands); + /*............................................................... + Only add the name edit field, the House, Join & New buttons if + we're doing nothing, or we've just been rejected. + ...............................................................*/ + if (joinstate <= JOIN_NOTHING) { + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); + name_edt.Add_Tail(*commands); + joinbtn.Add_Tail(*commands); + newbtn.Add_Tail(*commands); + } + if (joinstate == JOIN_CONFIRMED) + sendbtn.Add_Tail(*commands); + } + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + + /*.................................................................. + Draw the color boxes + ..................................................................*/ + if (display >= REDRAW_COLORS) { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + LogicPage->Fill_Rect (cbox_x[i] + 1, d_color_y + 1, + cbox_x[i] + 1 + d_color_w - 2, d_color_y + 1 + d_color_h - 2, + MPlayerGColors[i]); + + if (i == MPlayerColorIdx) { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, + BOXSTYLE_GREEN_DOWN, false); + } else { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, + BOXSTYLE_GREEN_RAISED, false); + } + } + } + + /*.................................................................. + Draw the message: + - Erase an old message first + - If we're in a game, print the game options (if they've been + received) + - If we've been rejected from a game, print that message + ..................................................................*/ + if (display >= REDRAW_MESSAGE) { + Draw_Box(d_message_x, d_message_y, d_message_w, d_message_h, + BOXSTYLE_GREEN_BORDER, true); + Messages.Draw(); + + LogicPage->Fill_Rect( d_dialog_x + 2, + d_msg1_y, + d_dialog_x + d_dialog_w - 4, + d_msg5_y + d_txt6_h, + BLACK); + + if (joinstate==JOIN_CONFIRMED && parms_received) { + /*............................................................ + Scenario title + ............................................................*/ + p = Text_String(TXT_SCENARIO_COLON); + if (ScenarioIdx != -1) { + sprintf(txt,"%s %s",p, MPlayerScenarios[ScenarioIdx]); + + Fancy_Text_Print (txt, d_dialog_cx, + d_msg1_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW | TPF_CENTER); + } else { + sprintf(txt,"%s %s",p,Text_String(TXT_NOT_FOUND)); + + Fancy_Text_Print (txt, d_dialog_cx, + d_msg1_y, CC_NOD_COLOR, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW | TPF_CENTER); + } + + /*............................................................ + # of credits + ............................................................*/ + p = Text_String(TXT_START_CREDITS_COLON); + sprintf(txt, "%s %d", p, MPlayerCredits); + Fancy_Text_Print (txt, d_dialog_cx, + d_msg2_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW | TPF_CENTER); + + /*............................................................ + Count & Level values + ............................................................*/ + p = Text_String(TXT_COUNT); + sprintf(txt,"%s %d",p,MPlayerUnitCount); + Fancy_Text_Print (txt, + d_dialog_x + (d_dialog_w / 4) - String_Pixel_Width(p), + d_msg3_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + p = Text_String(TXT_LEVEL); + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(txt, "%s %d", p, BuildLevel); + } else { + sprintf(txt, "%s **", p); + } + Fancy_Text_Print (txt, + d_dialog_x + d_dialog_w - (d_dialog_w / 4) - String_Pixel_Width(p), + d_msg3_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Bases + ............................................................*/ + p = Text_String(TXT_BASES_COLON); + if (MPlayerBases) { + sprintf(txt,"%s %s",p,Text_String(TXT_ON)); + } else { + sprintf(txt,"%s %s",p,Text_String(TXT_OFF)); + } + Fancy_Text_Print (txt, + d_dialog_x + (d_dialog_w / 4) - String_Pixel_Width(p), + d_msg4_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Tiberium + ............................................................*/ + p = Text_String(TXT_TIBERIUM_COLON); + if (MPlayerTiberium) { + sprintf(txt,"%s %s",p,Text_String(TXT_ON)); + } else { + sprintf(txt,"%s %s",p,Text_String(TXT_OFF)); + } + + Fancy_Text_Print (txt, + d_dialog_x + (d_dialog_w / 4) - String_Pixel_Width(p), + d_msg5_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Goody boxes + ............................................................*/ + p = Text_String(TXT_CRATES_COLON); + if (MPlayerGoodies) { + sprintf(txt,"%s %s",p,Text_String(TXT_ON)); + } else { + sprintf(txt,"%s %s",p,Text_String(TXT_OFF)); + } + + Fancy_Text_Print (txt, + d_dialog_x + d_dialog_w - (d_dialog_w / 4) - String_Pixel_Width(p), + d_msg4_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Computer AI players + ............................................................*/ + if (Special.IsCaptureTheFlag) { + p = Text_String(TXT_CAPTURE_THE_FLAG_COLON); + sprintf(txt,"%s %s",p,Text_String(TXT_ON)); + } else { + p = Text_String(TXT_AI_PLAYERS_COLON); + if (MPlayerGhosts) { + sprintf(txt,"%s %s",p,Text_String(TXT_ON)); + } else { + sprintf(txt,"%s %s",p,Text_String(TXT_OFF)); + } + } + Fancy_Text_Print (txt, + d_dialog_x + d_dialog_w - (d_dialog_w / 4) - String_Pixel_Width(p), + d_msg5_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + } else { + + /*............................................................... + Rejection notice + ...............................................................*/ + if (joinstate==JOIN_REJECTED) { + Fancy_Text_Print(TXT_REQUEST_DENIED, + d_dialog_cx, d_msg3_y, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + } + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + /*------------------------------------------------------------------ + User clicks on a color button: + - If we've joined a game, don't allow a new color selection + - otherwise, select that color + ------------------------------------------------------------------*/ + case KN_LMOUSE: + if (joinstate > JOIN_NOTHING) + break; + if (_Kbd->MouseQX > cbox_x[0] && + _Kbd->MouseQX < (cbox_x[MAX_MPLAYER_COLORS - 1] + d_color_w) && + _Kbd->MouseQY > d_color_y && + _Kbd->MouseQY < (d_color_y + d_color_h)) { + MPlayerPrefColor = (_Kbd->MouseQX - cbox_x[0]) / d_color_w; + MPlayerColorIdx = MPlayerPrefColor; + + name_edt.Set_Color (MPlayerTColors[MPlayerColorIdx]); + name_edt.Flag_To_Redraw(); + + display = REDRAW_COLORS; + } + break; + + /*------------------------------------------------------------------ + User clicks on the game list: + - If we've joined a game, don't allow the selected item to change; + otherwise: + - Clear the player list + - Send an immediate player query + ------------------------------------------------------------------*/ + case (BUTTON_GAMELIST | KN_BUTTON): + if (joinstate==JOIN_CONFIRMED) { + gamelist.Set_Selected_Index(game_index); + } else { + if (gamelist.Current_Index() != game_index) { + Clear_Player_List (&playerlist); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, 0, 1); + } + } + break; + + /*------------------------------------------------------------------ + House Buttons: set the player's desired House + ------------------------------------------------------------------*/ + case (BUTTON_GDI | KN_BUTTON): + MPlayerHouse = HOUSE_GOOD; + gdibtn.Turn_On(); + nodbtn.Turn_Off(); + break; + + case (BUTTON_NOD | KN_BUTTON): + MPlayerHouse = HOUSE_BAD; + gdibtn.Turn_Off(); + nodbtn.Turn_On(); + break; + + /*------------------------------------------------------------------ + JOIN: send a join request packet & switch to waiting-for-confirmation + mode. (Request_To_Join fills in MPlayerName with my namebuf.) + ------------------------------------------------------------------*/ + case (BUTTON_JOIN | KN_BUTTON): + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + + join_index = gamelist.Current_Index(); + parms_received = 0; + if (Request_To_Join (namebuf, join_index, &playerlist, MPlayerHouse, + MPlayerColorIdx)) { + joinstate = JOIN_WAIT_CONFIRM; + } else { + display = REDRAW_ALL; + } + break; + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF + - If we're part of a game, stay in this dialog; otherwise, exit + ------------------------------------------------------------------*/ + case (KN_ESC): + if (Messages.Get_Edit_Buf() != NULL) { + Messages.Input(input); + display = REDRAW_MESSAGE; + break; + } + case (BUTTON_CANCEL | KN_BUTTON): + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_SIGN_OFF; + strcpy(GPacket.Name,MPlayerName); + + /*............................................................... + If we're joined to a game, make extra sure the other players in + that game know I'm exiting; send my SIGN_OFF as an ack-required + packet. Do not send this packet to myself (index 0). + ...............................................................*/ + if (joinstate == JOIN_CONFIRMED) { + // + // Remove myself from the player list box + // + if (playerlist.Count()) { // added: BRR 6/14/96 + item = (char *)(playerlist.Get_Item(0)); + playerlist.Remove_Item(item); + delete [] item; + playerlist.Flag_To_Redraw(); + } + + // + // Remove myself from the Players list + // + if (Players.Count()) { // added: BRR 6/14/96 + who = Players[0]; + Players.Delete(0); + delete who; + } + + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 1, + &(Players[i]->Address)); + Ipx.Service(); + } + } + + /*............................................................... + Now broadcast my SIGN_OFF so other players looking at this game + know I'm leaving. + ...............................................................*/ + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + + if (IsBridge) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + if (joinstate != JOIN_CONFIRMED) { + process = false; + rc = -1; + } else { + MPlayerGameName[0] = 0; + joinstate = JOIN_NOTHING; + display = REDRAW_ALL; + } + break; + + /*------------------------------------------------------------------ + NEW: bail out with return code 1 + ------------------------------------------------------------------*/ + case (BUTTON_NEW | KN_BUTTON): + /* + .................. Force user to enter a name ................... + */ + if (strlen(namebuf)==0) { + CCMessageBox().Process(TXT_NAME_ERROR); + display = REDRAW_ALL; + break; + } + /* + ..................... Ensure name is unique ..................... + */ + found = 0; + for (i = 0; i < Games.Count(); i++) { + if (!stricmp(Games[i]->Name, namebuf)) { + found = 1; + CCMessageBox().Process (TXT_GAMENAME_MUSTBE_UNIQUE); + display = REDRAW_ALL; + break; + } + } + if (found) + break; + /* + .................... Save player & game name .................... + */ + strcpy(MPlayerName,namebuf); + strcpy(MPlayerGameName,namebuf); + + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + + rc = 1; + process = false; + break; + + /*------------------------------------------------------------------ + Default: manage the inter-player messages + ------------------------------------------------------------------*/ + default: + /*............................................................... + F4/SEND/'M' = edit a message + ...............................................................*/ + if (Messages.Get_Edit_Buf()==NULL) { + if ( (input == KN_M && joinstate==JOIN_CONFIRMED) || + input==(BUTTON_SEND | KN_BUTTON) || input == KN_F4) { + memset (txt, 0, 80); + + strcpy(txt,Text_String(TXT_TO_ALL)); // "To All:" + + Messages.Add_Edit (MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, d_message_w-70*factor); + + if (joinstate <= JOIN_NOTHING) { + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + } + + display = REDRAW_MESSAGE; + + break; + } + } else + + /*............................................................... + If we're already editing a message and the user clicks on + 'Send', translate our input to a Return so Messages.Input() will + work properly. + ...............................................................*/ + if (input==(BUTTON_SEND | KN_BUTTON)) { + input = KN_RETURN; + } + + /*............................................................... + Manage the message system (get rid of old messages) + ...............................................................*/ + if (Messages.Manage()) { + display = REDRAW_MESSAGE; + } + + /*............................................................... + Service keyboard input for any message being edited. + ...............................................................*/ + i = Messages.Input(input); + + /*............................................................... + If 'Input' returned 1, it means refresh the message display. + ...............................................................*/ + if (i==1) { + Messages.Draw(); + } else { + + /*............................................................... + If 'Input' returned 2, it means redraw the message display. + ...............................................................*/ + if (i==2) { + display = REDRAW_MESSAGE; + } else { + + /*............................................................... + If 'input' returned 3, it means send the current message. + ...............................................................*/ + if (i==3) { + sent_so_far = 0; + magic_number = MESSAGE_HEAD_MAGIC_NUMBER; + message_length = strlen(Messages.Get_Edit_Buf()); + crc = (unsigned short) + (Calculate_CRC(Messages.Get_Edit_Buf(),message_length) &0xffff); + + while ( sent_so_far < message_length ){ + GPacket.Command = NET_MESSAGE; + strcpy (GPacket.Name, namebuf); + memcpy (GPacket.Message.Buf, Messages.Get_Edit_Buf()+sent_so_far, COMPAT_MESSAGE_LENGTH-5); + *(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-5) = 0; + *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-4)) = magic_number; + *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-2)) = crc; + GPacket.Message.ID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + GPacket.Message.NameCRC = Compute_Name_CRC(MPlayerGameName); + + /*.................................................................. + Send the message to every player in our player list. The local + system will also receive this message, since it's in the Player list. + ..................................................................*/ + if (joinstate == JOIN_CONFIRMED) { + for (i = 1; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, + sizeof(GlobalPacketType), 1, &(Players[i]->Address)); + Ipx.Service(); + } + + sprintf(txt,Text_String (TXT_FROM), MPlayerName, GPacket.Message.Buf); + Messages.Add_Message (txt, + MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, magic_number, crc); + } + else { + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, + sizeof(GlobalPacketType), 1, &(Players[i]->Address)); + Ipx.Service(); + } + } + magic_number++; + sent_so_far += COMPAT_MESSAGE_LENGTH-5; + } + } + } + } + break; + } + + /*--------------------------------------------------------------------- + Resend our query packets + ---------------------------------------------------------------------*/ + Send_Join_Queries(game_index, 0, 0); + + /*--------------------------------------------------------------------- + Process incoming packets + ---------------------------------------------------------------------*/ + event = Get_Join_Responses(&joinstate, &gamelist, &playerlist, + join_index); + /*..................................................................... + If we've changed state, redraw everything; if we're starting the game, + break out of the loop. If we've just joined, send out a player query + so I'll get added to the list instantly. + .....................................................................*/ + if (event == EV_STATE_CHANGE) { + display = REDRAW_ALL; + if (joinstate==JOIN_GAME_START) { + rc = 0; + process = false; + } else { + + /*.................................................................. + If we're newly-confirmed, immediately send out a player query + ..................................................................*/ + if (joinstate==JOIN_CONFIRMED) { + + Clear_Player_List(&playerlist); + + item = new char [MPLAYER_NAME_MAX + 4]; + if (MPlayerHouse==HOUSE_GOOD) { + sprintf(item,"%s\t%s",MPlayerName,Text_String(TXT_G_D_I)); + } else { + sprintf(item,"%s\t%s",MPlayerName,Text_String(TXT_N_O_D)); + } + playerlist.Add_Item (item, MPlayerTColors[MPlayerColorIdx]); + + who = new NodeNameType; + strcpy(who->Name, MPlayerName); + who->Address = IPXAddressClass(); + who->Player.House = MPlayerHouse; + who->Player.Color = MPlayerColorIdx; + Players.Add (who); + + Send_Join_Queries (game_index, 0, 1); + } else { + + /*.................................................................. + If we've been rejected, clear any messages we may have been typing. + ..................................................................*/ + if (joinstate==JOIN_REJECTED) { + + // + // Remove myself from the player list box + // + if (playerlist.Count()) { // added: BRR 6/14/96 + item = (char *)(playerlist.Get_Item(0)); + if (item){ + playerlist.Remove_Item(item); + delete [] item; + playerlist.Flag_To_Redraw(); + } + } + + // + // Remove myself from the Players list + // + if (Players.Count()){ + who = Players[0]; + Players.Delete(0); + delete who; + } + + Messages.Init (d_message_x + 2, d_message_y + 2, 4, + MAX_MESSAGE_LENGTH, d_txt6_h); + } + } + } + } else + + /*..................................................................... + If a new game is detected, and it's the first game on our list, + automatically send out a player query for that game. + .....................................................................*/ + if (event == EV_NEW_GAME && gamelist.Count()==1) { + gamelist.Set_Selected_Index(0); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, 0, 1); + } else + + /*..................................................................... + If the game options have changed, print them. + .....................................................................*/ + if (event == EV_GAME_OPTIONS) { + parms_received = 1; + display = REDRAW_MESSAGE; + } else + + /*..................................................................... + Draw an incoming message + .....................................................................*/ + if (event == EV_MESSAGE) { + display = REDRAW_MESSAGE; + } else + + /*..................................................................... + A game before the one I've selected is gone, so we have a new index now. + 'game_index' must be kept set to the currently-selected list item, so + we send out queries for the currently-selected game. It's therefore + imperative that we detect any changes to the game list. + If we're joined in a game, we must decrement our game_index to keep + it aligned with the game we're joined to. + .....................................................................*/ + if (event == EV_GAME_SIGNOFF) { + if (joinstate==JOIN_CONFIRMED) { + game_index--; + join_index--; + gamelist.Set_Selected_Index(join_index); + } else { + gamelist.Flag_To_Redraw(); + Clear_Player_List(&playerlist); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, 0, 1); + } + } + + /*--------------------------------------------------------------------- + Service the Ipx connections + ---------------------------------------------------------------------*/ + Ipx.Service(); + + /*--------------------------------------------------------------------- + Clean out the Game List; if an old entry is found: + - Remove it + - Clear the player list + - Send queries for the new selected game, if there is one + ---------------------------------------------------------------------*/ + for (i = 0; i < Games.Count(); i++) { + if (TickCount.Time() - Games[i]->Game.LastTime > 400) { + Games.Delete(Games[i]); + item = (char *)(gamelist.Get_Item (i)); + gamelist.Remove_Item (item); + delete [] item; + if (i <= game_index) { + gamelist.Flag_To_Redraw(); + Clear_Player_List(&playerlist); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, 0, 1); + } + } + } + + /*--------------------------------------------------------------------- + Service the sounds & score; GameActive must be false at this point, + so Call_Back() doesn't intercept global messages from me! + ---------------------------------------------------------------------*/ + Call_Back(); + } + + /*------------------------------------------------------------------------ + Establish connections with all other players. + ------------------------------------------------------------------------*/ + if (rc == 0) { + /*..................................................................... + If the other guys are playing a scenario I don't have (sniff), I can't + play. Try to bail gracefully. + .....................................................................*/ + if (ScenarioIdx==-1) { + CCMessageBox().Process (TXT_UNABLE_PLAY_WAAUGH); + + // + // Remove myself from the player list box + // + if (playerlist.Count()) { // added: BRR 6/14/96 + item = (char *)(playerlist.Get_Item(0)); + playerlist.Remove_Item(item); + delete [] item; + playerlist.Flag_To_Redraw(); + } + + // + // Remove myself from the Players list + // + if (Players.Count()) { // added: BRR 6/14/96 + who = Players[0]; + Players.Delete(0); + delete who; + } + + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_SIGN_OFF; + strcpy (GPacket.Name, MPlayerName); + + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 1, + &(Players[i]->Address)); + Ipx.Service(); + } + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + + if (IsBridge) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + rc = -1; + + } else { + + /*.................................................................. + Set the number of players in this game, and my ID + ..................................................................*/ + MPlayerCount = Players.Count(); + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + /*.................................................................. + Get the scenario number + ..................................................................*/ + Scenario = MPlayerFilenum[ScenarioIdx]; + + /*.................................................................. + Form connections with all other players. Form the IPX Connection ID + from the player's Color and House. This will let us extract any + player's color & house at any time. Fill in 'tmp_id' while we're + doing this. + ..................................................................*/ + for (i = 0; i < Players.Count(); i++) { + + /*............................................................... + Only create the connection if it's not myself! + ...............................................................*/ + if (strcmp (MPlayerName, Players[i]->Name)) { + id = Build_MPlayerID(Players[i]->Player.Color, + Players[i]->Player.House); + + tmp_id[i] = id; + + Ipx.Create_Connection((int)id, Players[i]->Name, &(Players[i]->Address) ); + } else { + tmp_id[i] = MPlayerLocalID; + } + } + + /*.................................................................. + Store every player's ID in the MPlayerID[] array. This array will + determine the order of event execution, so the ID's must be stored + in the same order on all systems. + ..................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + min_index = 0; + min_id = 0xff; + for (j = 0; j < MPlayerCount; j++) { + if (tmp_id[j] < min_id) { + min_id = tmp_id[j]; + min_index = j; + } + } + MPlayerID[i] = tmp_id[min_index]; + tmp_id[min_index] = 0xff; + } + /*.................................................................. + Fill in the array of player names, including my own. + ..................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + if (MPlayerID[i] == MPlayerLocalID) { + strcpy (MPlayerNames[i], MPlayerName); + } else { + strcpy (MPlayerNames[i], Ipx.Connection_Name(MPlayerID[i])); + } + } + } + /*--------------------------------------------------------------------- + Wait a while, polling the IPX service routines, to give our ACK + a chance to get to the other system. If he doesn't get our ACK, he'll + be waiting the whole time we load MIX files. + ---------------------------------------------------------------------*/ + i = MAX(Ipx.Global_Response_Time() * 2, 60); + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < i) { + Ipx.Service(); + } + } + + /*------------------------------------------------------------------------ + Init network timing values, using previous response times as a measure + of what our retry delta & timeout should be. + ------------------------------------------------------------------------*/ + Ipx.Set_Timing (Ipx.Global_Response_Time() + 2, -1, + Ipx.Global_Response_Time() * 4); + + /*------------------------------------------------------------------------ + Clear all lists + ------------------------------------------------------------------------*/ + Clear_Game_List(&gamelist); + Clear_Player_List(&playerlist); + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Show_Mouse(); + + return(rc); +} + + +/*************************************************************************** + * Clear_Game_List -- Clears the game-name listbox & 'Games' Vector * + * * + * Assumes each entry in 'Games' & the list box have been allocated * + * separately. * + * * + * INPUT: * + * gamelist ptr to list box * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + *=========================================================================*/ +static void Clear_Game_List (ListClass *gamelist) +{ + char * item; + int i; + + /*------------------------------------------------------------------------ + Clear the list box + ------------------------------------------------------------------------*/ + while (gamelist->Count()) { + item = (char *)(gamelist->Get_Item (0)); + gamelist->Remove_Item(item); + delete [] item; + } + gamelist->Flag_To_Redraw(); + + /*------------------------------------------------------------------------ + Clear the 'Games' Vector + ------------------------------------------------------------------------*/ + for (i = 0; i < Games.Count(); i++) + delete Games[i]; + + Games.Clear(); + +} /* end of Clear_Game_List */ + + +/*************************************************************************** + * Clear_Player_List -- Clears the player-name listbox & Vector * + * * + * Assumes each entry in 'Players' & the list box have been allocated * + * separately. * + * * + * INPUT: * + * playerlist ptr to list box * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + *=========================================================================*/ +static void Clear_Player_List (ListClass *playerlist) +{ + char * item; + int i; + + /*------------------------------------------------------------------------ + Clear the list box + ------------------------------------------------------------------------*/ + while (playerlist->Count()) { + item = (char *)(playerlist->Get_Item(0)); + playerlist->Remove_Item(item); + delete [] item; + } + playerlist->Flag_To_Redraw(); + + /*------------------------------------------------------------------------ + Clear the 'Players' Vector + ------------------------------------------------------------------------*/ + for (i = 0; i < Players.Count(); i++) + delete Players[i]; + + Players.Clear(); + +} /* end of Clear_Player_List */ + + +/*************************************************************************** + * Request_To_Join -- Sends a JOIN request packet to game owner * + * * + * Regardless of the return code, the Join Dialog will need to be redrawn * + * after calling this routine. * + * * + * INPUT: * + * playername player's name * + * join_index index of game we're joining * + * playerlist listbox containing other players' names * + * house requested house * + * color requested color * + * * + * OUTPUT: * + * 1 = Packet sent, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + *=========================================================================*/ +static int Request_To_Join (char *playername, int join_index, ListClass *playerlist, + HousesType house, int color) +{ + int i; + + playerlist = playerlist; // shaddup, Mr stupid compiler! + + /* + --------------------------- Validate join_index -------------------------- + */ + if ( (Games.Count()==0) || join_index > Games.Count() || join_index < 0) { + CCMessageBox().Process (TXT_NOTHING_TO_JOIN); + return(false); + } + + /* + ----------------------- Force user to enter a name ----------------------- + */ + if (strlen(playername)==0) { + CCMessageBox().Process (TXT_NAME_ERROR); + return(false); + } + + /* + ------------------------- The game must be open -------------------------- + */ + if (!Games[join_index]->Game.IsOpen) { + CCMessageBox().Process(TXT_GAME_IS_CLOSED); + return (false); + } + + /* + ------------------------ Make sure name is unique ------------------------ + */ + for (i = 0; i < Players.Count(); i++) { + if (!stricmp(playername, Players[i]->Name)) { + CCMessageBox().Process (TXT_NAME_MUSTBE_UNIQUE); + return(false); + } + } + + /* + ----------------------------- Check version #'s -------------------------- + */ + int v; +#ifdef PATCH + if (IsV107) { + v = 1; + } else { + v = 2; + } +#else + v = Version_Number(); +#endif + if (Games[join_index]->Game.Version > v) { + CCMessageBox().Process (TXT_YOURGAME_OUTDATED); + return(false); + } else { + if (Games[join_index]->Game.Version < v) { + CCMessageBox().Process (TXT_DESTGAME_OUTDATED); + return(false); + } + } + + /* + ----------------------------- Save game name ----------------------------- + */ + strcpy (MPlayerName,playername); + + /* + ----------------------- Send packet to game's owner ---------------------- + */ + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_QUERY_JOIN; + strcpy (GPacket.Name, MPlayerName); + GPacket.PlayerInfo.House = house; + GPacket.PlayerInfo.Color = color; + + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 1, + &(Games[join_index]->Address)); + + return(true); +} + + +/*********************************************************************************************** + * Send_Join_Queries -- sends queries for the Join Dialog * + * * + * This routine [re]sends the queries related to the Join Dialog: * + * - NET_QUERY_GAME * + * - NET_QUERY_PLAYER for the game currently selected (if there is one) * + * * + * The queries are "staggered" in time so they aren't all sent at once; otherwise, we'd * + * be inundated with reply packets & we'd miss some (even though the replies will require * + * ACK's). * + * * + * INPUT: * + * curgame index of currently-selected game; -1 = none * + * gamenow if 1, will immediately send the game query * + * playernow if 1, will immediately send the player query for currently-selected game * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 04/15/1995 BRR : Created. * + *=============================================================================================*/ +static void Send_Join_Queries(int curgame, int gamenow, int playernow) +{ + static int lasttime1 = 0; // time since last Game query sent out + static int lasttime2 = 0; // time since last Player query sent out + + /*------------------------------------------------------------------------ + Send the game-name query if the time has expired, or we're told to do + it right now + ------------------------------------------------------------------------*/ + if ( (TickCount.Time() - lasttime1 > 120) || gamenow) { + lasttime1 = TickCount.Time(); + + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_QUERY_GAME; + + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, NULL); + + /*..................................................................... + If the user specified a remote server address, broadcast over that + network, too. + .....................................................................*/ + if (IsBridge) + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + } + + /*------------------------------------------------------------------------ + Send the player query for the game currently clicked on, if the time has + expired and there is a currently-selected game, or we're told to do it + right now + ------------------------------------------------------------------------*/ + if ( (curgame != -1) && curgame < Games.Count() && + ((TickCount.Time() - lasttime2 > 35) || playernow) ) { + lasttime2 = TickCount.Time(); + + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_QUERY_PLAYER; + strcpy (GPacket.Name, Games[curgame]->Name); + + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, NULL); + + /*..................................................................... + If the user specified a remote server address, broadcast over that + network, too. + .....................................................................*/ + if (IsBridge) + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + } + +} /* end of Send_Join_Queries */ + + +/*********************************************************************************************** + * Get_Join_Responses -- sends queries for the Join Dialog * + * * + * This routine polls the Global Channel to see if there are any incoming packets; * + * if so, it processes them. This routine can change the state of the Join Dialog, or * + * the contents of the list boxes, based on what the packet is. * + * * + * The list boxes are passed in as pointers; they can't be made globals, because they * + * can't be constructed, because they require shape pointers to the arrow buttons, and * + * the mix files won't have been initialized when the global variables' constructors are * + * called. * + * * + * This routine sets the globals * + * MPlayerHouse (from NET_CONFIRM_JOIN) * + * MPlayerColorIdx (from NET_CONFIRM_JOIN) * + * MPlayerBases (from NET_GAME_OPTIONS) * + * MPlayerTiberium (from NET_GAME_OPTIONS) * + * MPlayerGoodies (from NET_GAME_OPTIONS) * + * MPlayerGhosts (from NET_GAME_OPTIONS) * + * ScenarioIdx (from NET_GAME_OPTIONS; -1 = scenario not found) * + * * + * INPUT: * + * joinstate current state of Join Dialog * + * gamelist list box containing game names * + * playerlist list box containing player names for the currently-selected game * + * join_index index of the game we've joined or are asking to join * + * * + * OUTPUT: * + * Event that occurred * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 04/15/1995 BRR : Created. * + *=============================================================================================*/ +static JoinEventType Get_Join_Responses(JoinStateType *joinstate, ListClass *gamelist, + ColorListClass *playerlist, int join_index) +{ + int rc; + char * item; // general-purpose string + NodeNameType *who; // node to add to Games or Players + int i; + int found; + JoinEventType retcode = EV_NONE; + char txt[80]; + int color; + unsigned short magic_number; + unsigned short crc; + + /*------------------------------------------------------------------------ + If there is no incoming packet, just return + ------------------------------------------------------------------------*/ + rc = Ipx.Get_Global_Message (&GPacket, &GPacketlen, &GAddress, &GProductID); + if (!rc || GProductID != IPXGlobalConnClass::COMMAND_AND_CONQUER) + return(EV_NONE); + + /*------------------------------------------------------------------------ + If we're joined in a game, handle the packet in a standard way; otherwise, + don't answer standard queries. + ------------------------------------------------------------------------*/ + if ( (*joinstate)==JOIN_CONFIRMED && + Process_Global_Packet(&GPacket,&GAddress)!=0) + return(EV_NONE); + + /*------------------------------------------------------------------------ + NET_ANSWER_GAME: Another system is answering our GAME query, so add that + system to our list box if it's new. + ------------------------------------------------------------------------*/ + if (GPacket.Command==NET_ANSWER_GAME) { + /*..................................................................... + See if this name is unique + .....................................................................*/ + retcode = EV_NONE; + found = 0; + for (i = 0; i < Games.Count(); i++) { + if (!strcmp(Games[i]->Name, GPacket.Name)) { + found = 1; + /*............................................................... + If name was found, update the node's time stamp & IsOpen flag. + ...............................................................*/ + Games[i]->Game.LastTime = TickCount.Time(); + if (Games[i]->Game.IsOpen != GPacket.GameInfo.IsOpen) { + item = (char *)gamelist->Get_Item(i); + if (GPacket.GameInfo.IsOpen) { + sprintf(item,Text_String(TXT_THATGUYS_GAME),GPacket.Name); + } else { + sprintf(item,Text_String(TXT_THATGUYS_GAME_BRACKET),GPacket.Name); + } + Games[i]->Game.IsOpen = GPacket.GameInfo.IsOpen; + gamelist->Flag_To_Redraw(); + /*............................................................ + If this game has gone from closed to open, copy the responder's + address into our Game slot, since the guy responding to this + must be game owner. + ............................................................*/ + if (Games[i]->Game.IsOpen) + Games[i]->Address = GAddress; + } + break; + } + } + /*..................................................................... + name not found (or addresses are different); add it to 'Games' + .....................................................................*/ + if (found==0) { + /*.................................................................. + Create a new node structure, fill it in, add it to 'Games' + ..................................................................*/ + who = new NodeNameType; + strcpy(who->Name, GPacket.Name); + who->Address = GAddress; + who->Game.Version = GPacket.GameInfo.Version; + who->Game.IsOpen = GPacket.GameInfo.IsOpen; + who->Game.LastTime = TickCount.Time(); + Games.Add (who); + + /*.................................................................. + Create a string for "xxx's Game", leaving room for brackets around + the string if it's a closed game + ..................................................................*/ + item = new char [MPLAYER_NAME_MAX + 9]; + if (GPacket.GameInfo.IsOpen) { + sprintf(item,Text_String(TXT_THATGUYS_GAME),GPacket.Name); + } else { + sprintf(item,Text_String(TXT_THATGUYS_GAME_BRACKET),GPacket.Name); + } + gamelist->Add_Item(item); + + retcode = EV_NEW_GAME; + } + } + + /*------------------------------------------------------------------------ + NET_ANSWER_PLAYER: Another system is answering our PLAYER query, so add it + to our player list box & the Player Vector if it's new + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_ANSWER_PLAYER) { + /*..................................................................... + See if this name is unique + .....................................................................*/ + retcode = EV_NONE; + found = 0; + for (i = 0; i < Players.Count(); i++) { + /*.................................................................. + If the address is already present, re-copy their name, color & + house into the existing entry, in case they've changed it without + our knowledge; set the 'found' flag so we won't create a new entry. + ..................................................................*/ + if (Players[i]->Address==GAddress) { + strcpy(Players[i]->Name, GPacket.Name); + Players[i]->Player.House = GPacket.PlayerInfo.House; + Players[i]->Player.Color = GPacket.PlayerInfo.Color; + playerlist->Colors[i] = MPlayerTColors[GPacket.PlayerInfo.Color]; + found = 1; + break; + } + } + /*..................................................................... + Don't add this player if he's not part of the game that's selected. + .....................................................................*/ + i = gamelist->Current_Index(); + if (Games.Count() && GPacket.PlayerInfo.NameCRC != Compute_Name_CRC(Games[i]->Name)) + found = 1; + + /* + ** Dont add this player if its really me! (hack, hack) + */ + if (!strcmp(GPacket.Name, MPlayerName)){ + found = 1; + } + + + /*..................................................................... + name not found (or address didn't match); add to player list box & Vector + .....................................................................*/ + if (found==0) { + /*.................................................................. + Create & add a node to the Vector + ..................................................................*/ + who = new NodeNameType; + strcpy(who->Name, GPacket.Name); + who->Address = GAddress; + who->Player.House = GPacket.PlayerInfo.House; + who->Player.Color = GPacket.PlayerInfo.Color; + Players.Add (who); + + /*.................................................................. + Create & add a string to the list box + ..................................................................*/ + item = new char [MPLAYER_NAME_MAX + 4]; + if (GPacket.PlayerInfo.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",GPacket.Name,Text_String(TXT_G_D_I)); + } else { + sprintf(item,"%s\t%s",GPacket.Name,Text_String(TXT_N_O_D)); + } + playerlist->Add_Item(item, MPlayerTColors[who->Player.Color]); + + retcode = EV_NEW_PLAYER; + } + } + + /*------------------------------------------------------------------------ + NET_CONFIRM_JOIN: The game owner has confirmed our JOIN query; mark us as + being confirmed, and start answering queries from other systems + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_CONFIRM_JOIN) { + if ( (*joinstate) != JOIN_CONFIRMED) { + strcpy (MPlayerGameName, GPacket.Name); + MPlayerHouse = GPacket.PlayerInfo.House; + MPlayerColorIdx = GPacket.PlayerInfo.Color; + + (*joinstate) = JOIN_CONFIRMED; + retcode = EV_STATE_CHANGE; + } + } + + /*------------------------------------------------------------------------ + NET_REJECT_JOIN: The game owner has turned down our JOIN query; restore + the dialog state to its first pop-up state. Broadcast a sign-off to + tell all other systems that I'm no longer a part of any game; this way, + I'll be properly removed from their dialogs. + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_REJECT_JOIN) { + if ( (*joinstate) != JOIN_REJECTED) { + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_SIGN_OFF; + strcpy (GPacket.Name,MPlayerName); + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), 0, NULL); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), 0, NULL); + + if (IsBridge) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + MPlayerGameName[0] = 0; + + (*joinstate) = JOIN_REJECTED; + retcode = EV_STATE_CHANGE; + } + } + + /*------------------------------------------------------------------------ + NET_GAME_OPTIONS: The game owner has changed the game options & is sending + us the new values. + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_GAME_OPTIONS) { + if ( (*joinstate)==JOIN_CONFIRMED) { + MPlayerCredits = GPacket.ScenarioInfo.Credits; + MPlayerBases = GPacket.ScenarioInfo.IsBases; + MPlayerTiberium = GPacket.ScenarioInfo.IsTiberium; + MPlayerGoodies = GPacket.ScenarioInfo.IsGoodies; + MPlayerGhosts = GPacket.ScenarioInfo.IsGhosties; + BuildLevel = GPacket.ScenarioInfo.BuildLevel; + MPlayerUnitCount = GPacket.ScenarioInfo.UnitCount; + Seed = GPacket.ScenarioInfo.Seed; + Special = GPacket.ScenarioInfo.Special; + Options.GameSpeed = GPacket.ScenarioInfo.GameSpeed; + + if (MPlayerTiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + + if (Winsock.Get_Connected()){ + ScenarioIdx = GPacket.ScenarioInfo.Scenario; + }else{ + + ScenarioIdx = -1; + for (i = 0; i < MPlayerFilenum.Count(); i++) { + if (GPacket.ScenarioInfo.Scenario == MPlayerFilenum[i]) + ScenarioIdx = i; + } + } + + retcode = EV_GAME_OPTIONS; + } + } + + /*------------------------------------------------------------------------ + NET_SIGN_OFF: Another system is signing off: search for that system in + both the game list & player list, & remove it if found + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_SIGN_OFF) { + /*..................................................................... + Remove this name from the list of games + .....................................................................*/ + for (i = 0; i < Games.Count(); i++) { + if (!strcmp(Games[i]->Name, GPacket.Name) && + Games[i]->Address==GAddress) { + /*............................................................... + If the system signing off is the currently-selected list + item, clear the player list since that game is no longer + forming. + ...............................................................*/ + if (i==gamelist->Current_Index()) { + Clear_Player_List (playerlist); + } + + /*............................................................... + If the system signing off was the owner of our game, mark + ourselves as rejected + ...............................................................*/ + if ( (*joinstate) > JOIN_NOTHING && i==join_index) { + (*joinstate) = JOIN_REJECTED; + retcode = EV_STATE_CHANGE; + } + + /* + ....................... Set my return code ...................... + */ + if (retcode == EV_NONE) { + if (i <= gamelist->Current_Index()) { + retcode = EV_GAME_SIGNOFF; + } else { + retcode = EV_PLAYER_SIGNOFF; + } + } + + /* + ................. Remove game name from game list ............... + */ + Games.Delete(Games[i]); + item = (char *)(gamelist->Get_Item (i)); + gamelist->Remove_Item (item); + delete [] item; + gamelist->Flag_To_Redraw(); + + } + } + /*..................................................................... + Remove this name from the list of players + .....................................................................*/ + for (i = 0; i < Players.Count(); i++) { + /* + ..................... Name found; remove it ..................... + */ + if (Players[i]->Address==GAddress) { + item = (char *)(playerlist->Get_Item(i)); + playerlist->Remove_Item(item); + delete [] item; + Players.Delete(Players[i]); + playerlist->Flag_To_Redraw(); + + if (retcode == EV_NONE) + retcode = EV_PLAYER_SIGNOFF; + } + } + } + + /*------------------------------------------------------------------------ + NET_GO: The game's owner is signalling us to start playing. + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_GO) { + if ( (*joinstate)==JOIN_CONFIRMED) { + MPlayerMaxAhead = GPacket.ResponseTime.OneWay; + (*joinstate) = JOIN_GAME_START; + retcode = EV_STATE_CHANGE; +CCDebugString ("C&C95 - Received the 'GO' packet\n"); + } + } + + /*------------------------------------------------------------------------ + NET_MESSAGE: Someone is sending us a message + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_MESSAGE) { + sprintf(txt,Text_String (TXT_FROM), GPacket.Name, GPacket.Message.Buf); + magic_number = *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-4)); + crc = *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-2)); + color = MPlayerID_To_ColorIndex(GPacket.Message.ID); + Messages.Add_Message (txt, MPlayerTColors[color], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, magic_number, crc); + retcode = EV_MESSAGE; + } + + /*------------------------------------------------------------------------ + NET_PING: Someone is pinging me to get a response time measure (will only + happen after I've joined a game). Do nothing; the IPX Manager will handle + sending an ACK, and updating the response time measurements. + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_PING) { + retcode = EV_NONE; + } + + /*------------------------------------------------------------------------ + Default case: nothing happened. (This case will be hit every time I + receive my own NET_QUERY_GAME or NET_QUERY_PLAYER packets.) + ------------------------------------------------------------------------*/ + else { + retcode = EV_NONE; + } + + return(retcode); +} + + +/*********************************************************************************************** + * Net_New_Dialog -- lets user start a new game * + * * + * This dialog shows a list of who's requesting to join this game, and lets * + * the game initiator selectively approve each user. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ New Network Game ³ * + * ³ ³ * + * ³ Players Scenario ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³ Boffo ³³ ³ Hell's Kitchen ³³ ³ * + * ³ ³ Bozo ÃÄ´ ³ Heaven's Gate ÃÄ´ ³ * + * ³ ³ Bonzo ³ ³ ³ ... ³ ³ ³ * + * ³ ³ ÃÄ´ ³ ÃÄ´ ³ * + * ³ ³ ³³ ³ ³³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ [Reject] Count:--- ## ³ * + * ³ Level:--- ## ³ * + * ³ ³ * + * ³ Credits: _____ ³ * + * ³ [ Bases ] [ Crates ] ³ * + * ³ [ Tiberium ] [ AI Players ] ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ [Send Message] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = success, false = cancel * + * * + * WARNINGS: * + * MPlayerName & MPlayerGameName must contain this player's name. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +static int Net_New_Dialog(void) +{ +/* ###Change collision detected! C:\PROJECTS\CODE\NETDLG.CPP... */ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + //D_DIALOG_W = 281; // dialog width + int d_dialog_w = 287*factor; // dialog width + int d_dialog_h = 177*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6*factor+1; // ht of 6-pt text + int d_margin1 = 5*factor; // margin width/height + int d_margin2 = 2*factor; // margin width/height + + //d_playerlist_w = 100; + int d_playerlist_w = 106*factor; + int d_playerlist_h = 27*factor; + int d_playerlist_x = d_dialog_x + d_margin1; + int d_playerlist_y = d_dialog_y + d_margin1 + (d_txt6_h * 3); + + int d_scenariolist_w = 162*factor; + int d_scenariolist_h = 27*factor; + int d_scenariolist_x = d_dialog_x + d_dialog_w - d_margin1 - d_scenariolist_w; + int d_scenariolist_y = d_dialog_y + d_margin1 + (d_txt6_h * 3); + +#if (GERMAN | FRENCH) + int d_reject_w = 55*factor; +#else + int d_reject_w = 45*factor; +#endif + int d_reject_h = 9*factor; + int d_reject_x = d_playerlist_x + (d_playerlist_w / 2) - (d_reject_w / 2); + int d_reject_y = d_playerlist_y + d_playerlist_h + d_margin2; + + int d_count_w = 25*factor; + int d_count_h = d_txt6_h; + int d_count_x = d_scenariolist_x + (d_scenariolist_w / 2); + int d_count_y = d_scenariolist_y + d_scenariolist_h + d_margin2; + + int d_level_w = 25*factor; + int d_level_h = d_txt6_h; + int d_level_x = d_scenariolist_x + (d_scenariolist_w / 2); + int d_level_y = d_count_y + d_count_h; + + int d_credits_w = ((CREDITSBUF_MAX - 1) * 7*factor) + 4*factor; + //int d_credits_w = ((CREDITSBUF_MAX - 1) * 6*factor) + 3*factor; + int d_credits_h = 9*factor; + int d_credits_x = d_dialog_cx + 2*factor; + int d_credits_y = d_level_y + d_level_h + d_margin1; + +#if (GERMAN | FRENCH) + int d_bases_w = 120*factor;//bga:100; +#else + int d_bases_w = 100*factor; +#endif + int d_bases_h = 9*factor; + int d_bases_x = d_dialog_cx - d_bases_w - d_margin2; + int d_bases_y = d_credits_y + d_credits_h + d_margin2; + +#if (GERMAN | FRENCH) + int d_tiberium_w = 120*factor; +#else + int d_tiberium_w = 100*factor; +#endif + int d_tiberium_h = 9*factor; + int d_tiberium_x = d_dialog_cx - d_bases_w - d_margin2; + int d_tiberium_y = d_bases_y + d_bases_h + d_margin2; + +#if (GERMAN | FRENCH) + int d_goodies_w = 120*factor; +#else + int d_goodies_w = 100*factor; +#endif + int d_goodies_h = 9*factor; + int d_goodies_x = d_dialog_cx + d_margin2; + int d_goodies_y = d_credits_y + d_credits_h + d_margin2; + +#if (GERMAN | FRENCH) + int d_ghosts_w = 120*factor; +#else + int d_ghosts_w = 100*factor; +#endif + int d_ghosts_h = 9*factor; + int d_ghosts_x = d_dialog_cx + d_margin2; + int d_ghosts_y = d_goodies_y + d_goodies_h + d_margin2; + + int d_ok_w = 45*factor; + int d_ok_h = 9*factor; + int d_ok_x = d_dialog_cx - d_margin2 - (d_bases_w / 2) - (d_ok_w / 2); + int d_ok_y = d_ghosts_y + d_ghosts_h + d_margin1; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*factor; +#else + int d_cancel_w = 45*factor; +#endif + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx + d_margin2 + (d_goodies_w / 2) - (d_cancel_w / 2); + int d_cancel_y = d_ghosts_y + d_ghosts_h + d_margin1; + + int d_message_w = d_dialog_w - (d_margin1 * 2); + int d_message_h = 34*factor; + int d_message_x = d_dialog_x + d_margin1; + int d_message_y = d_cancel_y + d_cancel_h + d_margin1; + + int d_send_w = 80*factor; + int d_send_h = 9*factor; + int d_send_x = d_dialog_cx - (d_send_w / 2); + int d_send_y = d_message_y + d_message_h + d_margin2; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_PLAYERLIST = 100, + BUTTON_SCENARIOLIST, + BUTTON_REJECT, + BUTTON_COUNT, + BUTTON_LEVEL, + BUTTON_CREDITS, + BUTTON_BASES, + BUTTON_TIBERIUM, + BUTTON_GOODIES, + BUTTON_GHOSTS, + BUTTON_OK, + BUTTON_CANCEL, + BUTTON_SEND, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_UNIT_COUNT, + REDRAW_MESSAGE, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + char credbuf[CREDITSBUF_MAX]; // for credit edit box + int old_cred; // old value in credits buffer + int transmit; // 1 = re-transmit new game options + + long ok_timer = 0; // for timing OK button + int index; // index for rejecting a player + int rc; + int i,j; + char *item; + int tabs[] = {77*factor}; // tabs for player list box + + long ping_timer = 0; // for sending Ping packets + + unsigned char tmp_id[MAX_PLAYERS]; // temp storage for sorting player ID's + int min_index; // for sorting player ID's + unsigned char min_id; // for sorting player ID's + unsigned char id; // connection ID + char txt[80]; + JoinEventType whahoppa; // event generated by received packets + static int first_time = 1; // 1 = 1st time this dialog is run + + int message_length; + int sent_so_far; + unsigned short magic_number; + unsigned short crc; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + + ColorListClass playerlist(BUTTON_PLAYERLIST, + d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + ListClass scenariolist(BUTTON_SCENARIOLIST, + d_scenariolist_x, d_scenariolist_y, d_scenariolist_w, d_scenariolist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + EditClass credit_edt (BUTTON_CREDITS, + credbuf, CREDITSBUF_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_credits_x, d_credits_y, d_credits_w, d_credits_h, EditClass::ALPHANUMERIC); + + TextButtonClass rejectbtn(BUTTON_REJECT, TXT_REJECT, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + d_reject_x, d_reject_y); +#else + d_reject_x, d_reject_y, d_reject_w, d_reject_h); +#endif + + GaugeClass countgauge (BUTTON_COUNT, + d_count_x, d_count_y, d_count_w, d_count_h); + + GaugeClass levelgauge (BUTTON_LEVEL, + d_level_x, d_level_y, d_level_w, d_level_h); + + TextButtonClass basesbtn(BUTTON_BASES, TXT_BASES_OFF, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_bases_x, d_bases_y, d_bases_w, d_bases_h); + + TextButtonClass tiberiumbtn(BUTTON_TIBERIUM, TXT_TIBERIUM_OFF, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_tiberium_x, d_tiberium_y, d_tiberium_w, d_tiberium_h); + + TextButtonClass goodiesbtn(BUTTON_GOODIES, TXT_CRATES_OFF, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_goodies_x, d_goodies_y, d_goodies_w, d_goodies_h); + + TextButtonClass ghostsbtn(BUTTON_GHOSTS, TXT_AI_PLAYERS_OFF, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_ghosts_x, d_ghosts_y, d_ghosts_w, d_ghosts_h); + + TextButtonClass okbtn(BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_ok_x, d_ok_y, d_ok_w, d_ok_h); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + d_cancel_x, d_cancel_y); +#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +#endif + + TextButtonClass sendbtn(BUTTON_SEND, TXT_SEND_MESSAGE, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + d_send_x, d_send_y); +#else + d_send_x, d_send_y, d_send_w, d_send_h); +#endif + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &playerlist; + scenariolist.Add_Tail(*commands); + credit_edt.Add_Tail(*commands); + rejectbtn.Add_Tail(*commands); + countgauge.Add_Tail(*commands); + levelgauge.Add_Tail(*commands); + basesbtn.Add_Tail(*commands); + tiberiumbtn.Add_Tail(*commands); + goodiesbtn.Add_Tail(*commands); + ghostsbtn.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + sendbtn.Add_Tail(*commands); + + playerlist.Set_Tabs(tabs); + + /* + ----------------------------- Various Inits ------------------------------ + */ + /*........................................................................ + Init dialog values, only the first time through + ........................................................................*/ + if (first_time) { + MPlayerCredits = 3000; // init credits & credit buffer + MPlayerBases = 1; // init scenario parameters + MPlayerTiberium = 0; + MPlayerGoodies = 0; + MPlayerGhosts = 0; + Special.IsCaptureTheFlag = 0; + MPlayerUnitCount = (MPlayerCountMax[MPlayerBases] + MPlayerCountMin[MPlayerBases]) / 2; + first_time = 0; + } + + /*........................................................................ + Init button states + ........................................................................*/ + if (MPlayerBases) { + basesbtn.Turn_On(); + basesbtn.Set_Text(TXT_BASES_ON); + } + if (MPlayerTiberium) { + tiberiumbtn.Turn_On(); + tiberiumbtn.Set_Text(TXT_TIBERIUM_ON); + } + if (MPlayerGoodies) { + goodiesbtn.Turn_On(); + goodiesbtn.Set_Text(TXT_CRATES_ON); + } + if (MPlayerGhosts) { + ghostsbtn.Turn_On(); + ghostsbtn.Set_Text(TXT_AI_PLAYERS_ON); + } + if (Special.IsCaptureTheFlag) { + MPlayerGhosts = 0; + ghostsbtn.Turn_On(); + ghostsbtn.Set_Text(TXT_CAPTURE_THE_FLAG); + } + + sprintf(credbuf, "%d", MPlayerCredits); + credit_edt.Set_Text(credbuf, CREDITSBUF_MAX); + old_cred = MPlayerCredits; + + levelgauge.Set_Maximum(MPLAYER_BUILD_LEVEL_MAX - 1); + levelgauge.Set_Value(BuildLevel - 1); + + countgauge.Set_Maximum(MPlayerCountMax[MPlayerBases] - MPlayerCountMin[MPlayerBases]); + countgauge.Set_Value(MPlayerUnitCount - MPlayerCountMin[MPlayerBases]); + + /*........................................................................ + Init other scenario parameters + ........................................................................*/ + Special.IsTGrowth = MPlayerTiberium; + Special.IsTSpread = MPlayerTiberium; + transmit = 0; + + /*........................................................................ + Init scenario description list box + ........................................................................*/ + for (i = 0; i < MPlayerScenarios.Count(); i++) { + scenariolist.Add_Item (strupr(MPlayerScenarios[i])); + } + ScenarioIdx = 0; // 1st scenario is selected + + /*........................................................................ + Init player color-used flags + ........................................................................*/ + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + ColorUsed[i] = 0; // init all colors to available + } + ColorUsed[MPlayerColorIdx] = 1; // set my color to used + playerlist.Set_Selected_Style(ColorListClass::SELECT_BAR, CC_GREEN_SHADOW); + + /*........................................................................ + Init random-number generator, & create a seed to be used for all random + numbers from here on out + ........................................................................*/ + randomize(); + Seed = rand(); + + /*........................................................................ + Init the message display system + ........................................................................*/ + Messages.Init (d_message_x + 2*factor, d_message_y + 2*factor, 4, MAX_MESSAGE_LENGTH, + d_txt6_h); + + /*------------------------------------------------------------------------ + Add myself to the list. Note that since I'm not in the Players Vector, + the Vector & listbox are now 1 out of sync. + ------------------------------------------------------------------------*/ + item = new char [MPLAYER_NAME_MAX + 4]; + if (MPlayerHouse==HOUSE_GOOD) { + sprintf(item,"%s\t%s",MPlayerName,Text_String(TXT_G_D_I)); + } else { + sprintf(item,"%s\t%s",MPlayerName,Text_String(TXT_N_O_D)); + } + playerlist.Add_Item(item, MPlayerTColors[MPlayerColorIdx]); + + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + while (Get_Mouse_State() > 0) Show_Mouse(); + + /* + ---------------------------- Processing loop ----------------------------- + */ + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + #if(SHOW_MONO) + Ipx.Mono_Debug_Print(-1,0); + #endif + /* + ...................... Refresh display if needed ...................... + */ + if (display == REDRAW_UNIT_COUNT){ + /* + ** Wipe the background behind the unit count then reprint it + */ + LogicPage->Fill_Rect(d_count_x + d_count_w + 2*factor, + d_count_y, + d_count_x + d_count_w + 2*factor + 20, + d_count_y + 12, + 0 ); + sprintf(txt,"%d",MPlayerUnitCount); + Fancy_Text_Print (txt, d_count_x + d_count_w + 2*factor, d_count_y, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL); + display = REDRAW_NONE; + } + + + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + + /* + ** Reload and draw the title page + */ + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_NETGAME_SETUP, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(TXT_PLAYERS, + d_playerlist_x + (d_playerlist_w / 2), d_playerlist_y - d_txt6_h, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_CENTER); + + Fancy_Text_Print(TXT_SCENARIOS, + d_scenariolist_x + (d_scenariolist_w / 2), + d_scenariolist_y - d_txt6_h, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_CENTER); + + Fancy_Text_Print (TXT_COUNT, d_count_x - 2*factor, d_count_y, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_RIGHT); + + sprintf(txt,"%d",MPlayerUnitCount); + Fancy_Text_Print (txt, d_count_x + d_count_w + 2*factor, d_count_y, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL); + + Fancy_Text_Print (TXT_LEVEL, d_level_x - 2*factor, d_level_y, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_RIGHT); + + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(txt,"%d",BuildLevel); + } else { + sprintf(txt, "**"); + } + Fancy_Text_Print (txt, d_level_x + d_level_w + 2*factor, d_level_y, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL); + + Fancy_Text_Print (TXT_START_CREDITS_COLON, d_credits_x - 5*factor, + d_credits_y + 1*factor, CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_RIGHT); + + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + + /*.................................................................. + Draw the messages: + - Erase an old message first + - If we're in a game, print the game options (if they've been + received) + - If we've been rejected from a game, print that message + ..................................................................*/ + if (display >= REDRAW_MESSAGE) { + Draw_Box(d_message_x, d_message_y, d_message_w, d_message_h, + BOXSTYLE_GREEN_BORDER, true); + Messages.Draw(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + /*------------------------------------------------------------------ + New Scenario selected. + ------------------------------------------------------------------*/ + case (BUTTON_SCENARIOLIST | KN_BUTTON): + if (scenariolist.Current_Index() != ScenarioIdx) { + ScenarioIdx = scenariolist.Current_Index(); + MPlayerCredits = atoi(credbuf); + transmit = 1; + } + break; + + /*------------------------------------------------------------------ + Reject the currently-selected player (don't allow rejecting myself, + who will be the first entry in the list) + ------------------------------------------------------------------*/ + case (BUTTON_REJECT | KN_BUTTON): + index = playerlist.Current_Index(); + if (index == 0) { + CCMessageBox().Process (TXT_CANT_REJECT_SELF, TXT_OOPS); + display = REDRAW_ALL; + break; + } else { + if (index < 0 || index >= playerlist.Count()) { + CCMessageBox().Process (TXT_SELECT_PLAYER_REJECT,TXT_OOPS); + display = REDRAW_ALL; + break; + } + } + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_REJECT_JOIN; + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), 1, + &(Players[index - 1]->Address)); + break; + + /*------------------------------------------------------------------ + User adjusts max # units + ------------------------------------------------------------------*/ + case (BUTTON_COUNT | KN_BUTTON): + MPlayerUnitCount = countgauge.Get_Value() + MPlayerCountMin[MPlayerBases]; + + Hide_Mouse(); + LogicPage->Fill_Rect (d_count_x + d_count_w + 2*factor, d_count_y, + d_count_x + d_count_w + 14*factor, d_count_y + 6*factor, BLACK); + + sprintf(txt,"%d",MPlayerUnitCount); + Fancy_Text_Print (txt, d_count_x + d_count_w + 2*factor, d_count_y, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL); + Show_Mouse(); + + transmit = 1; + break; + + /*------------------------------------------------------------------ + User adjusts build level + ------------------------------------------------------------------*/ + case (BUTTON_LEVEL | KN_BUTTON): + BuildLevel = levelgauge.Get_Value() + 1; + if (BuildLevel > MPLAYER_BUILD_LEVEL_MAX) // if it's pegged, max it out + BuildLevel = MPLAYER_BUILD_LEVEL_MAX; + + Hide_Mouse(); + LogicPage->Fill_Rect (d_level_x + d_level_w + 2*factor, d_level_y, + d_level_x + d_level_w + 14*factor, d_level_y + 6*factor, BLACK); + + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(txt,"%d",BuildLevel); + } else { + sprintf(txt, "**"); + } + Fancy_Text_Print (txt, d_level_x + d_level_w + 2*factor, d_level_y, + CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL); + Show_Mouse(); + + transmit = 1; + break; + + /*------------------------------------------------------------------ + User edits the credits value; retransmit new game options + ------------------------------------------------------------------*/ + case (BUTTON_CREDITS | KN_BUTTON): + MPlayerCredits = atoi(credbuf); + transmit = 1; + break; + + /*------------------------------------------------------------------ + Toggle bases: + - Clear scenario list & rebuild it with new names + - toggle bases button, change its text + - adjust the MPlayerUnitCount to reflect the new allowed range, + using the current gauge setting + - Change the unit count gauge limit & value + ------------------------------------------------------------------*/ + case (BUTTON_BASES | KN_BUTTON): + if (MPlayerBases) { + MPlayerBases = 0; + basesbtn.Turn_Off(); + basesbtn.Set_Text(TXT_BASES_OFF); + MPlayerUnitCount = Fixed_To_Cardinal (MPlayerCountMax[0]-MPlayerCountMin[0], + Cardinal_To_Fixed(MPlayerCountMax[1]-MPlayerCountMin[1], + MPlayerUnitCount-MPlayerCountMin[1])) + MPlayerCountMin[0]; + } else { + MPlayerBases = 1; + basesbtn.Turn_On(); + basesbtn.Set_Text(TXT_BASES_ON); + MPlayerUnitCount = Fixed_To_Cardinal (MPlayerCountMax[1]-MPlayerCountMin[1], + Cardinal_To_Fixed(MPlayerCountMax[0]-MPlayerCountMin[0], + MPlayerUnitCount-MPlayerCountMin[0])) + MPlayerCountMin[1]; + } + MPlayerCredits = atoi(credbuf); + countgauge.Set_Maximum(MPlayerCountMax[MPlayerBases] - MPlayerCountMin[MPlayerBases]); + countgauge.Set_Value(MPlayerUnitCount - MPlayerCountMin[MPlayerBases]); + transmit = 1; + countgauge.Flag_To_Redraw(); + display = REDRAW_UNIT_COUNT; + break; + + /*------------------------------------------------------------------ + Toggle tiberium + ------------------------------------------------------------------*/ + case (BUTTON_TIBERIUM | KN_BUTTON): + if (MPlayerTiberium) { + MPlayerTiberium = 0; + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + tiberiumbtn.Turn_Off(); + tiberiumbtn.Set_Text(TXT_TIBERIUM_OFF); + } else { + MPlayerTiberium = 1; + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + tiberiumbtn.Turn_On(); + tiberiumbtn.Set_Text(TXT_TIBERIUM_ON); + } + MPlayerCredits = atoi(credbuf); + transmit = 1; + break; + + /*------------------------------------------------------------------ + Toggle goodies + ------------------------------------------------------------------*/ + case (BUTTON_GOODIES | KN_BUTTON): + if (MPlayerGoodies) { + MPlayerGoodies = 0; + goodiesbtn.Turn_Off(); + goodiesbtn.Set_Text(TXT_CRATES_OFF); + } else { + MPlayerGoodies = 1; + goodiesbtn.Turn_On(); + goodiesbtn.Set_Text(TXT_CRATES_ON); + } + MPlayerCredits = atoi(credbuf); + transmit = 1; + break; + + /*------------------------------------------------------------------ + Toggle ghosts/capture-the-flag + ------------------------------------------------------------------*/ + case (BUTTON_GHOSTS | KN_BUTTON): + if (!MPlayerGhosts && !Special.IsCaptureTheFlag) { // ghosts OFF => ghosts ON + MPlayerGhosts = 1; + Special.IsCaptureTheFlag = 0; + ghostsbtn.Turn_On(); + ghostsbtn.Set_Text(TXT_AI_PLAYERS_ON); + } else { + if (MPlayerGhosts) { // ghosts ON => capture-flag + MPlayerGhosts = 0; + Special.IsCaptureTheFlag = 1; + ghostsbtn.Turn_On(); + ghostsbtn.Set_Text(TXT_CAPTURE_THE_FLAG); + } else { + if (Special.IsCaptureTheFlag) { // capture-flag => AI OFF + MPlayerGhosts = 0; + Special.IsCaptureTheFlag = 0; + ghostsbtn.Turn_Off(); + ghostsbtn.Set_Text(TXT_AI_PLAYERS_OFF); + } + } + } + + MPlayerCredits = atoi(credbuf); + transmit = 1; + break; + + /*------------------------------------------------------------------ + OK: exit loop with TRUE status + ------------------------------------------------------------------*/ + case (BUTTON_OK | KN_BUTTON): + /*............................................................... + If a new player has joined in the last second, don't allow + an OK; force a wait longer than 1 second (to give all players + a chance to know about this new guy) + ...............................................................*/ + i = MAX(Ipx.Global_Response_Time() * 2, 60); + while (TickCount.Time() - ok_timer < i) + Ipx.Service(); + + /*............................................................... + If there are at least 2 players, go ahead & play; error otherwise + ...............................................................*/ + if (MPlayerSolo || Players.Count() > 0) { + rc = TRUE; + process = FALSE; + } else { + CCMessageBox().Process (TXT_ONLY_ONE,TXT_OOPS,NULL); + display = REDRAW_ALL; + } + break; + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF, bail out with error code + ------------------------------------------------------------------*/ + case (KN_ESC): + if (Messages.Get_Edit_Buf() != NULL) { + Messages.Input(input); + display = REDRAW_MESSAGE; + break; + } + case (BUTTON_CANCEL | KN_BUTTON): + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_SIGN_OFF; + strcpy (GPacket.Name, MPlayerName); + + /*............................................................... + Broadcast my sign-off over my network + ...............................................................*/ + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + /*............................................................... + Broadcast my sign-off over a bridged network if there is one + ...............................................................*/ + if (IsBridge) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + } + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + /*............................................................... + And now, just be absolutely sure, send my sign-off to each + player in my game. (If there's a bridge between us, the other + player will have specified my address, so he can cross the + bridge; but I may not have specified a bridge address, so the + only way I have of crossing the bridge is to send a packet + directly to him.) + ...............................................................*/ + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 1, + &(Players[i]->Address)); + Ipx.Service(); + } + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + MPlayerGameName[0] = 0; + process = false; + rc = false; + break; + + /*------------------------------------------------------------------ + Default: manage the inter-player messages + ------------------------------------------------------------------*/ + default: + /*............................................................... + F4/SEND/'M' = send a message + ...............................................................*/ + if (Messages.Get_Edit_Buf()==NULL) { + if (input == KN_M || input==(BUTTON_SEND | KN_BUTTON) || + input == KN_F4) { + memset (txt, 0, 80); + + strcpy(txt,Text_String(TXT_TO_ALL)); // "To All:" + + Messages.Add_Edit (MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, d_message_w-70*factor); + + credit_edt.Clear_Focus(); + credit_edt.Flag_To_Redraw(); + display = REDRAW_MESSAGE; + + break; + } + } else { + + /*............................................................... + If we're already editing a message and the user clicks on + 'Send', translate our input to a Return so Messages.Input() will + work properly. + ...............................................................*/ + if (input==(BUTTON_SEND | KN_BUTTON)) { + input = KN_RETURN; + } + } + + /*............................................................... + Manage the message system (get rid of old messages) + ...............................................................*/ + if (Messages.Manage()) { + display = REDRAW_MESSAGE; + } + + /*............................................................... + Re-draw the messages & service keyboard input for any message + being edited. + ...............................................................*/ + i = Messages.Input(input); + + /*............................................................... + If 'Input' returned 1, it means refresh the message display. + ...............................................................*/ + if (i==1) { + Messages.Draw(); + } + + /*............................................................... + If 'Input' returned 2, it means redraw the message display. + ...............................................................*/ + else if (i==2) { + display = REDRAW_MESSAGE; + } + + /*............................................................... + If 'input' returned 3, it means send the current message. + ...............................................................*/ + else if (i==3) { + + sent_so_far = 0; + magic_number = MESSAGE_HEAD_MAGIC_NUMBER; + message_length = strlen(Messages.Get_Edit_Buf()); + crc = (unsigned short) + (Calculate_CRC(Messages.Get_Edit_Buf(), message_length) &0xffff); + while ( sent_so_far < message_length ){ + memset (&GPacket, 0, sizeof(GlobalPacketType)); + GPacket.Command = NET_MESSAGE; + strcpy (GPacket.Name, MPlayerName); + memcpy (GPacket.Message.Buf, Messages.Get_Edit_Buf()+sent_so_far, COMPAT_MESSAGE_LENGTH-5); + *(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-5) = 0; + *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-4)) = magic_number; + *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-2)) = crc; + GPacket.Message.ID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + GPacket.Message.NameCRC = Compute_Name_CRC(MPlayerGameName); + + /*.................................................................. + Send the message to every player in our player list. + ..................................................................*/ + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 1, + &(Players[i]->Address)); + Ipx.Service(); + } + /*.................................................................. + Add the message to our own list, since we're not in the player list + on this dialog. + ..................................................................*/ + sprintf(txt,Text_String (TXT_FROM), MPlayerName, GPacket.Message.Buf); + Messages.Add_Message (txt, MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, magic_number, crc); + + magic_number++; + sent_so_far += COMPAT_MESSAGE_LENGTH-5; + + } + display = REDRAW_MESSAGE; + } + } + + /*--------------------------------------------------------------------- + Detect editing of the credits buffer, transmit new values to players + ---------------------------------------------------------------------*/ + if (atoi(credbuf) != old_cred) { + old_cred = Bound(atoi(credbuf), 0, 9999); + MPlayerCredits = old_cred; + transmit = 1; + sprintf(credbuf, "%d", MPlayerCredits); + credit_edt.Set_Text(credbuf, CREDITSBUF_MAX); + } + + /*--------------------------------------------------------------------- + Process incoming packets + ---------------------------------------------------------------------*/ + whahoppa = Get_NewGame_Responses(&playerlist); + if (whahoppa == EV_NEW_PLAYER) { + ok_timer = TickCount.Time(); + transmit = 1; + } else { + if (whahoppa == EV_MESSAGE) { + display = REDRAW_MESSAGE; + } + } + + /*--------------------------------------------------------------------- + If our Transmit flag is set, we need to send out a game option packet + ---------------------------------------------------------------------*/ + if (transmit) { + for (i = 0; i < Players.Count(); i++) { + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_GAME_OPTIONS; + GPacket.ScenarioInfo.Scenario = MPlayerFilenum[ScenarioIdx]; + GPacket.ScenarioInfo.Credits = MPlayerCredits; + GPacket.ScenarioInfo.IsBases = MPlayerBases; + GPacket.ScenarioInfo.IsTiberium = MPlayerTiberium; + GPacket.ScenarioInfo.IsGoodies = MPlayerGoodies; + GPacket.ScenarioInfo.IsGhosties = MPlayerGhosts; + GPacket.ScenarioInfo.BuildLevel = BuildLevel; + GPacket.ScenarioInfo.UnitCount = MPlayerUnitCount; + GPacket.ScenarioInfo.Seed = Seed; + GPacket.ScenarioInfo.Special = Special; + GPacket.ScenarioInfo.GameSpeed = Options.GameSpeed; + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 1, &(Players[i]->Address) ); + } + transmit = 0; + } + + /*--------------------------------------------------------------------- + Ping every player in my game, to force the Global Channel to measure + the connection response time. + ---------------------------------------------------------------------*/ + if (TickCount.Time() - ping_timer > 15) { + memset (&GPacket, 0, sizeof(GlobalPacketType)); + GPacket.Command = NET_PING; + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 1, &(Players[i]->Address) ); + } + ping_timer = TickCount.Time(); + } + + /*--------------------------------------------------------------------- + Service the Ipx connections + ---------------------------------------------------------------------*/ + Ipx.Service(); + + /*--------------------------------------------------------------------- + Service the sounds & score; GameActive must be false at this point, + so Call_Back() doesn't intercept global messages from me! + ---------------------------------------------------------------------*/ + Call_Back(); + + } /* end of while */ + + /*------------------------------------------------------------------------ + Establish connections with all other players. + ------------------------------------------------------------------------*/ + if (rc) { + /*..................................................................... + Set the number of players in this game, and my ID + .....................................................................*/ + MPlayerCount = Players.Count() + 1; + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + /*..................................................................... + Get the scenario filename + .....................................................................*/ + Scenario = MPlayerFilenum[ScenarioIdx]; + + /*..................................................................... + Compute frame delay value for packet transmissions: + - Divide global channel's response time by 8 (2 to convert to 1-way + value, 4 more to convert from ticks to frames) + .....................................................................*/ + MPlayerMaxAhead = MAX( (Ipx.Global_Response_Time() / 8), 2); + + /*..................................................................... + Send all players the NET_GO packet. Wait until all ACK's have been + received. + .....................................................................*/ + memset (&GPacket, 0, sizeof(GlobalPacketType)); + GPacket.Command = NET_GO; + GPacket.ResponseTime.OneWay = MPlayerMaxAhead; + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 1, &(Players[i]->Address) ); + /*.................................................................. + Wait for all the ACK's to come in. + ..................................................................*/ + while (Ipx.Global_Num_Send() > 0) + Ipx.Service(); + } + + /*..................................................................... + Form connections with all other players. Form the IPX Connection ID + from the player's Color (high byte) and House (low byte). This + will let us extract any player's color & house at any time. + Fill in 'tmp_id' while we're doing this. + .....................................................................*/ + for (i = 0; i < Players.Count(); i++) { + id = Build_MPlayerID (Players[i]->Player.Color, + Players[i]->Player.House); + + tmp_id[i] = id; + + Ipx.Create_Connection(id, Players[i]->Name, &(Players[i]->Address) ); + } + tmp_id[i] = MPlayerLocalID; + + /*..................................................................... + Store every player's ID in the MPlayerID[] array. This array will + determine the order of event execution, so the ID's must be stored + in the same order on all systems. + .....................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + min_index = 0; + min_id = 0xff; + for (j = 0; j < MPlayerCount; j++) { + if (tmp_id[j] < min_id) { + min_id = tmp_id[j]; + min_index = j; + } + } + MPlayerID[i] = tmp_id[min_index]; + tmp_id[min_index] = 0xff; + } + /*..................................................................... + Fill in the array of player names, including my own. + .....................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + if (MPlayerID[i] == MPlayerLocalID) { + strcpy (MPlayerNames[i], MPlayerName); + } else { + strcpy (MPlayerNames[i], Ipx.Connection_Name(MPlayerID[i])); + } + } + } + + /*------------------------------------------------------------------------ + Init network timing values, using previous response times as a measure + of what our retry delta & timeout should be. + ------------------------------------------------------------------------*/ + Ipx.Set_Timing (Ipx.Global_Response_Time() + 2, -1, + Ipx.Global_Response_Time() * 4); + + /*------------------------------------------------------------------------ + Clear all lists + ------------------------------------------------------------------------*/ + while (scenariolist.Count()) { + scenariolist.Remove_Item(scenariolist.Get_Item(0)); + } + Clear_Player_List(&playerlist); + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Show_Mouse(); + + return(rc); +} + + +/*************************************************************************** + * Get_NewGame_Responses -- processes packets for New Game dialog * + * * + * This routine can modify the contents of the given list box, as well * + * as the contents of the Players Vector global. * + * * + * INPUT: * + * playerlist list of players in this game * + * * + * OUTPUT: * + * EV_NONE = nothing happened * + * EV_NEW_PLAYER = a new player has joined; false otherwise * + * EV_MESSAGE = a message was received * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/18/1995 BRR : Created. * + *=========================================================================*/ +static JoinEventType Get_NewGame_Responses(ColorListClass *playerlist) +{ + int rc; + char * item; // general-purpose string + NodeNameType *who; // node to add to Players Vector + int i; + int found; + JoinEventType retval = EV_NONE; + int resend; + char txt[80]; + int color; + unsigned short magic_number; + unsigned short crc; + + /*------------------------------------------------------------------------ + If there is no incoming packet, just return + ------------------------------------------------------------------------*/ + rc = Ipx.Get_Global_Message (&GPacket, &GPacketlen, &GAddress, &GProductID); + if (!rc || GProductID != IPXGlobalConnClass::COMMAND_AND_CONQUER) { + return(EV_NONE); + } + + /*------------------------------------------------------------------------ + Try to handle the packet in a standard way + ------------------------------------------------------------------------*/ + if (Process_Global_Packet(&GPacket,&GAddress) != 0) { + return(EV_NONE); + } else + + /*------------------------------------------------------------------------ + NET_QUERY_JOIN: + ------------------------------------------------------------------------*/ + if (GPacket.Command==NET_QUERY_JOIN) { + /*..................................................................... + See if this name is unique: + - If the name matches, but the address is different, reject this player + - If the name & address match, this packet must be a re-send of a + prevous request; in this case, do nothing. The other player must have + received my CONFIRM_JOIN packet (since it was sent with an ACK + required), so we can ignore this resend. + .....................................................................*/ + found = 0; + resend = 0; + for (i = 0; i < Players.Count(); i++) { + if (!strcmp(Players[i]->Name,GPacket.Name)) { + if (Players[i]->Address != GAddress) { + found = 1; + } + else { + resend = 1; + } + break; + } + } + if (!strcmp (MPlayerName, GPacket.Name)) { + found = 1; + } + + /*..................................................................... + Reject if name is a duplicate, or if there are too many players: + .....................................................................*/ + if (found || (Players.Count() >= (MPlayerMax - 1) && !resend) ) { + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_REJECT_JOIN; + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 1, &GAddress); + } + + /*..................................................................... + If this packet is NOT a resend, accept the player. Grant him the + requested color if possible. + .....................................................................*/ + else if (!resend) { + /*.................................................................. + Add node to the Vector list + ..................................................................*/ + who = new NodeNameType; + strcpy(who->Name, GPacket.Name); + who->Address = GAddress; + who->Player.House = GPacket.PlayerInfo.House; + Players.Add (who); + + /*.................................................................. + Set player's color; if requested color isn't used, give it to him; + otherwise, give him the 1st available color. Mark the color we + give him as used. + ..................................................................*/ + if (ColorUsed[GPacket.PlayerInfo.Color] == 0) { + who->Player.Color = GPacket.PlayerInfo.Color; + } else { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + if (ColorUsed[i]==0) { + who->Player.Color = i; + break; + } + } + } + ColorUsed[who->Player.Color] = 1; + + /*.................................................................. + Add player name to the list box + ..................................................................*/ + item = new char [MPLAYER_NAME_MAX + 4]; + if (GPacket.PlayerInfo.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",GPacket.Name,Text_String(TXT_G_D_I)); + } else { + sprintf(item,"%s\t%s",GPacket.Name,Text_String(TXT_N_O_D)); + } + playerlist->Add_Item (item, MPlayerTColors[who->Player.Color]); + + /*.................................................................. + Send a confirmation packet + ..................................................................*/ + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_CONFIRM_JOIN; + strcpy(GPacket.Name,MPlayerName); + GPacket.PlayerInfo.House = who->Player.House; + GPacket.PlayerInfo.Color = who->Player.Color; + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 1, &GAddress); + + retval = EV_NEW_PLAYER; + } + } + + /*------------------------------------------------------------------------ + NET_SIGN_OFF: Another system is signing off: search for that system in + the player list, & remove it if found + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_SIGN_OFF) { + for (i = 0; i < Players.Count(); i++) { + /* + ....................... Name found; remove it ...................... + */ + if (!strcmp (Players[i]->Name, GPacket.Name) && + Players[i]->Address==GAddress) { + /*............................................................... + Remove from the list box + ...............................................................*/ + item = (char *)(playerlist->Get_Item(i + 1)); + playerlist->Remove_Item(item); + playerlist->Flag_To_Redraw(); + delete [] item; + /*............................................................... + Mark his color as available + ...............................................................*/ + ColorUsed[Players[i]->Player.Color] = 0; + /*............................................................... + Delete from the Vector list + ...............................................................*/ + Players.Delete(Players[i]); + break; + } + } + } + + /*------------------------------------------------------------------------ + NET_MESSAGE: Someone is sending us a message + ------------------------------------------------------------------------*/ + else if (GPacket.Command==NET_MESSAGE) { + sprintf(txt,Text_String (TXT_FROM), GPacket.Name, GPacket.Message.Buf); + magic_number = *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-4)); + crc = *((unsigned short*)(GPacket.Message.Buf + COMPAT_MESSAGE_LENGTH-2)); + color = MPlayerID_To_ColorIndex(GPacket.Message.ID); + Messages.Add_Message (txt, MPlayerTColors[color], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, magic_number, crc); + retval = EV_MESSAGE; + } + + return(retval); +} + + + +/*************************************************************************** + * Compute_Name_CRC -- computes CRC from char string * + * * + * INPUT: * + * name string to create CRC for * + * * + * OUTPUT: * + * CRC * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/29/1995 BRR : Created. * + *=========================================================================*/ +unsigned long Compute_Name_CRC(char *name) +{ + char buf[80]; + unsigned long crc = 0L; + int i; + + strcpy (buf, name); + strupr (buf); + + for (i = 0; i < strlen(buf); i++) { + Add_CRC (&crc, (unsigned long)buf[i]); + } + + return (crc); +} + +/*************************************************************************** + * Net_Reconnect_Dialog -- Draws/updates the network reconnect dialog * + * * + * INPUT: * + * reconn 1 = reconnect, 0 = waiting for first-time connection * + * fresh 1 = draw from scratch, 0 = only update time counter * + * oldest_index IPX connection index of oldest connection * + * (only used for reconnection) * + * timeval value to print in the countdown field * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/08/1995 BRR : Created. * + *=========================================================================*/ +void Net_Reconnect_Dialog(int reconn, int fresh, int oldest_index, + unsigned long timeval) +{ + static int x,y,w,h; + int id; + char buf1[40] = {0}; + char buf2[40] = {0}; + char const *buf3 = ""; + + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + int d_txt6_h = 6*factor+1; + int d_margin = 5*factor; + + + /*------------------------------------------------------------------------ + Draw the dialog from scratch + ------------------------------------------------------------------------*/ + if (fresh) { + Fancy_Text_Print ("", 0, 0, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + if (reconn) { + id = Ipx.Connection_ID(oldest_index); + sprintf(buf1,Text_String(TXT_RECONNECTING_TO), + Ipx.Connection_Name(id)); + } else { + sprintf(buf1,Text_String(TXT_WAITING_FOR_CONNECTIONS)); + } + sprintf(buf2,Text_String(TXT_TIME_ALLOWED), timeval + 1); + buf3 = Text_String(TXT_PRESS_ESC); + + w = MAX(String_Pixel_Width(buf1),String_Pixel_Width(buf2)); + w = MAX(String_Pixel_Width(buf3), w); + w += (d_margin * 2); + h = (d_txt6_h * 3) + (d_margin * 6); + x = 160*factor - (w / 2); + y = 100*factor - (h / 2); + + Hide_Mouse(); + Set_Logic_Page(SeenBuff); + Dialog_Box(x, y, w, h); + + Fancy_Text_Print (buf1, 160*factor, y + (d_margin * 2), CC_GREEN, BLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print (buf2, 160*factor, y + (d_margin * 2) + d_txt6_h + d_margin, + CC_GREEN, BLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print (buf3, 160*factor, y + (d_margin * 2) + (d_txt6_h + d_margin) * 2, + CC_GREEN, BLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Show_Mouse(); + + } else { + + /*------------------------------------------------------------------------ + Just update the timeout value on the dialog + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Set_Logic_Page(SeenBuff); + + sprintf(buf2,Text_String(TXT_TIME_ALLOWED), timeval + 1); + Fancy_Text_Print (buf2, 160*factor, y + (d_margin * 2) + d_txt6_h + d_margin, + CC_GREEN, BLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Show_Mouse(); + } +} + + + + + + + + + + + +/*********************************************************************************************** + * Net_Fake_New_Dialog -- Just like Net_New_Dialog but without the Dialog. For internet play * + * * + * This 'dialog' does all the non-dialog game set up stuff that is done in the normal * + * network game set up dialog. The only visible button is 'cancel' * + * * + * INPUT: Nothing * + * * + * OUTPUT: true if successfully connected * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/24/96 10:34AM ST : Created * + *=============================================================================================*/ +static int Net_Fake_New_Dialog(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + int d_dialog_w = 120*factor; // dialog width + int d_dialog_h = 80*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + //d_playerlist_w = 100; + int d_playerlist_w = 106*factor; + int d_playerlist_h = 27*factor; + int d_playerlist_x = 10 * factor; //off screen + //int d_playerlist_x = 500*factor; //10 * factor; //off screen + int d_playerlist_y = d_dialog_y + 20; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*factor; +#else + int d_cancel_w = 45*factor; +#endif + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx - (d_cancel_w / 2); + int d_cancel_y = d_dialog_y + d_dialog_h - 20*factor; + + char text_buffer[64]; + int width; + int height; + + bool player_joined = false; + CountDownTimerClass join_timer; + + strcpy(text_buffer, "Connecting...."); + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(text_buffer, SeenBuff.Get_Height(), width, height); + + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_CANCEL = 100, + BUTTON_PLAYERLIST, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_MESSAGE, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + char credbuf[CREDITSBUF_MAX]; // for credit edit box + int old_cred; // old value in credits buffer + int transmit; // 1 = re-transmit new game options + + long ok_timer = 0; // for timing OK button + int rc; + int i,j; + char *item; + int tabs[] = {77*factor}; // tabs for player list box + + long ping_timer = 0; // for sending Ping packets + + unsigned char tmp_id[MAX_PLAYERS]; // temp storage for sorting player ID's + int min_index; // for sorting player ID's + unsigned char min_id; // for sorting player ID's + unsigned char id; // connection ID + JoinEventType whahoppa; // event generated by received packets + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + ColorListClass playerlist(BUTTON_PLAYERLIST, + d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + d_cancel_x, d_cancel_y); +#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +#endif + + + CCDebugString ("C&C95 - In new game dialog - initialising lists.\n"); + /* + ------------------------- Build the button list -------------------------- + */ + commands = &playerlist; + cancelbtn.Add_Tail(*commands); + + playerlist.Set_Tabs(tabs); + + /* + ----------------------------- Various Inits ------------------------------ + */ + + sprintf(credbuf, "%d", MPlayerCredits); + old_cred = MPlayerCredits; + + /*........................................................................ + Init other scenario parameters + ........................................................................*/ + Special.IsTGrowth = MPlayerTiberium; + Special.IsTSpread = MPlayerTiberium; + transmit = 0; + + /*........................................................................ + Init player color-used flags + ........................................................................*/ + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + ColorUsed[i] = 0; // init all colors to available + } + ColorUsed[MPlayerColorIdx] = 1; // set my color to used + playerlist.Set_Selected_Style(ColorListClass::SELECT_BAR, CC_GREEN_SHADOW); + + /*........................................................................ + Init random-number generator, & create a seed to be used for all random + numbers from here on out + ........................................................................*/ + randomize(); + Seed = rand(); + + /*------------------------------------------------------------------------ + Add myself to the list. Note that since I'm not in the Players Vector, + the Vector & listbox are now 1 out of sync. + ------------------------------------------------------------------------*/ + item = new char [MPLAYER_NAME_MAX + 4]; + if (MPlayerHouse==HOUSE_GOOD) { + sprintf(item,"%s\t%s",MPlayerName,Text_String(TXT_G_D_I)); + } else { + sprintf(item,"%s\t%s",MPlayerName,Text_String(TXT_N_O_D)); + } + playerlist.Add_Item(item, MPlayerTColors[MPlayerColorIdx]); + + /* + ** Process the message loop until we are in focus. + */ + if (!GameInFocus){ + CCDebugString ("C&C95 - Waiting for game to come into focus."); + do { + OutputDebugString ("."); + Keyboard::Check(); + }while (!GameInFocus); + CCDebugString ("\n"); + AllSurfaces.SurfacesRestored=FALSE; + } + + + CCDebugString ("C&C95 - About to uncompress title page.\n"); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + CCDebugString ("C&C95 - About to set the palette.\n"); + Set_Palette(Palette); + CCDebugString ("C&C95 - Palette was set OK.\n"); + + if (LogicPage != &SeenBuff && LogicPage!= &HidPage){ + CCDebugString ("C&C95 - Logic page invalid"); + Set_Logic_Page (SeenBuff); + } + + char a_buffer [128]; + sprintf (a_buffer, "Number of players:%d", Players.Count()); + CCDebugString (a_buffer); + + + /* + ** Send a bogus packet to wake up the VSS + */ + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = (NetCommandType)50; //Invalid command + strcpy (GPacket.Name, MPlayerName); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), 0, NULL); + + + CCDebugString ("C&C95 - About to reveal mouse\n"); + while (Get_Mouse_State() > 0) Show_Mouse(); + + /* + ---------------------------- Processing loop ----------------------------- + */ + CCDebugString ("C&C95 - Entering join dialogue loop\n"); + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_NONE, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(text_buffer, d_dialog_cx-width/2, d_dialog_y + 25*factor, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF, bail out with error code + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_SIGN_OFF; + strcpy (GPacket.Name, MPlayerName); + + /*............................................................... + Broadcast my sign-off over my network + ...............................................................*/ + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + /*............................................................... + Broadcast my sign-off over a bridged network if there is one + ...............................................................*/ + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + /*............................................................... + And now, just be absolutely sure, send my sign-off to each + player in my game. (If there's a bridge between us, the other + player will have specified my address, so he can cross the + bridge; but I may not have specified a bridge address, so the + only way I have of crossing the bridge is to send a packet + directly to him.) + ...............................................................*/ + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 1, + &(Players[i]->Address)); + Ipx.Service(); + } + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + MPlayerGameName[0] = 0; + process = false; + rc = false; + break; + + /*------------------------------------------------------------------ + default: exit loop with TRUE status + ------------------------------------------------------------------*/ + default: +char ddkks[128]; + if (Players.Count() == InternetMaxPlayers-1){ + +sprintf (ddkks, "C&C95 - Players.Count() = %d\n", Players.Count()); +CCDebugString (ddkks); + + + /* + ** Wait for several secs after receiving request to join before sending + ** start game packet + */ + if (!player_joined){ + player_joined = true; + join_timer.Set (3*60, true); + break; + }else{ + if (join_timer.Time()) break; + } + +CCDebugString ("C&C95 - Join timer expired\n"); + + /*............................................................... + If a new player has joined in the last second, don't allow + an OK; force a wait longer than 2 seconds (to give all players + a chance to know about this new guy) + ...............................................................*/ + i = MAX(Ipx.Global_Response_Time() * 2, 120); + while (TickCount.Time() - ok_timer < i) + Ipx.Service(); + + /*............................................................... + If there are at least 2 players, go ahead & play; error otherwise + ...............................................................*/ + if (MPlayerSolo || Players.Count() > 0) { + rc = TRUE; + process = FALSE; + } else { + CCMessageBox().Process (TXT_ONLY_ONE,TXT_OOPS,NULL); + display = REDRAW_ALL; + } + } + break; + } + + /*--------------------------------------------------------------------- + Process incoming packets + ---------------------------------------------------------------------*/ + whahoppa = Get_NewGame_Responses(&playerlist); + if (whahoppa == EV_NEW_PLAYER) { + ok_timer = TickCount.Time(); + transmit = 1; + } else { + if (whahoppa == EV_MESSAGE) { + display = REDRAW_MESSAGE; + } + } + + /*--------------------------------------------------------------------- + If our Transmit flag is set, we need to send out a game option packet + ---------------------------------------------------------------------*/ + if (transmit) { + for (i = 0; i < Players.Count(); i++) { + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_GAME_OPTIONS; + GPacket.ScenarioInfo.Scenario = ScenarioIdx; //MPlayerFilenum[ScenarioIdx]; + GPacket.ScenarioInfo.Credits = MPlayerCredits; + GPacket.ScenarioInfo.IsBases = MPlayerBases; + GPacket.ScenarioInfo.IsTiberium = MPlayerTiberium; + GPacket.ScenarioInfo.IsGoodies = MPlayerGoodies; + GPacket.ScenarioInfo.IsGhosties = MPlayerGhosts; + GPacket.ScenarioInfo.BuildLevel = BuildLevel; + GPacket.ScenarioInfo.UnitCount = MPlayerUnitCount; + GPacket.ScenarioInfo.Seed = Seed; + GPacket.ScenarioInfo.Special = Special; + GPacket.ScenarioInfo.GameSpeed = Options.GameSpeed; + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 1, &(Players[i]->Address) ); + } + transmit = 0; + } + + /*--------------------------------------------------------------------- + Ping every player in my game, to force the Global Channel to measure + the connection response time. + ---------------------------------------------------------------------*/ + if (TickCount.Time() - ping_timer > 15) { + memset (&GPacket, 0, sizeof(GlobalPacketType)); + GPacket.Command = NET_PING; + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 1, &(Players[i]->Address) ); + } + ping_timer = TickCount.Time(); + } + + /*--------------------------------------------------------------------- + Service the Ipx connections + ---------------------------------------------------------------------*/ + Ipx.Service(); + + /*--------------------------------------------------------------------- + Service the sounds & score; GameActive must be false at this point, + so Call_Back() doesn't intercept global messages from me! + ---------------------------------------------------------------------*/ + Call_Back(); + + } /* end of while */ + +CCDebugString ("C&C95 - Exited process loop\n"); + + /*------------------------------------------------------------------------ + Establish connections with all other players. + ------------------------------------------------------------------------*/ + if (rc) { + /*..................................................................... + Set the number of players in this game, and my ID + .....................................................................*/ + MPlayerCount = Players.Count() + 1; + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + /*..................................................................... + Get the scenario filename + .....................................................................*/ + Scenario = ScenarioIdx; //PlayerFilenum[ScenarioIdx]; We are passed actual number now from wchat not index from + //Scenario = MPlayerFilenum[ScenarioIdx]; + + /*..................................................................... + Compute frame delay value for packet transmissions: + - Divide global channel's response time by 8 (2 to convert to 1-way + value, 4 more to convert from ticks to frames) + .....................................................................*/ + MPlayerMaxAhead = MAX( (Ipx.Global_Response_Time() / 8), 2); + + /*..................................................................... + Send all players the NET_GO packet. Wait until all ACK's have been + received. + .....................................................................*/ +CCDebugString ("C&C95 - Sending the 'GO' packet\n"); + memset (&GPacket, 0, sizeof(GlobalPacketType)); + GPacket.Command = NET_GO; + GPacket.ResponseTime.OneWay = MPlayerMaxAhead; + for (i = 0; i < Players.Count(); i++) { +char flopbuf [128]; +sprintf (flopbuf, "Sending 'GO' packet to address %d\n", *((unsigned short*)&(Players[i]->Address))); +CCDebugString (flopbuf); + + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 1, &(Players[i]->Address) ); + /*.................................................................. + Wait for all the ACK's to come in. + ..................................................................*/ + while (Ipx.Global_Num_Send() > 0) + Ipx.Service(); + } + + /*..................................................................... + Form connections with all other players. Form the IPX Connection ID + from the player's Color (high byte) and House (low byte). This + will let us extract any player's color & house at any time. + Fill in 'tmp_id' while we're doing this. + .....................................................................*/ + for (i = 0; i < Players.Count(); i++) { + id = Build_MPlayerID (Players[i]->Player.Color, + Players[i]->Player.House); + + tmp_id[i] = id; + + Ipx.Create_Connection(id, Players[i]->Name, &(Players[i]->Address) ); + } + +CCDebugString ("C&C95 - Creating connection to the VSS\n"); + /* + ** Create an additional connection to the VSS + */ + if (UseVirtualSubnetServer){ + IPXAddressClass vss_global_address; + NetNodeType vss_node; + NetNumType vss_net; + memset (vss_net, 1, sizeof (vss_net)); + memset (vss_node, 0, sizeof (vss_node)); + vss_global_address.Set_Address(vss_net, vss_node); + Ipx.Create_Connection( VSS_ID, "VSS", &vss_global_address); + } + + tmp_id[i] = MPlayerLocalID; + + /*..................................................................... + Store every player's ID in the MPlayerID[] array. This array will + determine the order of event execution, so the ID's must be stored + in the same order on all systems. + .....................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + min_index = 0; + min_id = 0xff; + for (j = 0; j < MPlayerCount; j++) { + if (tmp_id[j] < min_id) { + min_id = tmp_id[j]; + min_index = j; + } + } + MPlayerID[i] = tmp_id[min_index]; + tmp_id[min_index] = 0xff; + } + /*..................................................................... + Fill in the array of player names, including my own. + .....................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + if (MPlayerID[i] == MPlayerLocalID) { + strcpy (MPlayerNames[i], MPlayerName); + } else { + strcpy (MPlayerNames[i], Ipx.Connection_Name(MPlayerID[i])); + } + } + } + + /*------------------------------------------------------------------------ + Init network timing values, using previous response times as a measure + of what our retry delta & timeout should be. + ------------------------------------------------------------------------*/ + Ipx.Set_Timing (Ipx.Global_Response_Time() + 2, -1, + Ipx.Global_Response_Time() * 4); + + /*------------------------------------------------------------------------ + Clear all lists + ------------------------------------------------------------------------*/ + Clear_Player_List(&playerlist); + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Show_Mouse(); + + return(rc); +} + + + + + + +/*********************************************************************************************** + * Net_Fake_Join_Dialog -- Like Net_Join_Dialog but with no dialogs. For Internet Play. * + * * + * This 'dialog' does all the non-dialog game set up stuff that is done in the normal * + * network game set up dialog. The only visible button is 'cancel' * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: 0 = good, -1 = bad * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/24/96 11:07AM ST : Created * + *=============================================================================================*/ + +static int Net_Fake_Join_Dialog(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ +/* ###Change collision detected! C:\PROJECTS\CODE\NETDLG.CPP... */ + int d_dialog_w = 120 *factor; // dialog width + int d_dialog_h = 80*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_margin1=10; + int d_txt6_h=15; + + int d_gamelist_w = 160*factor; + int d_gamelist_h = 27*factor; + //int d_gamelist_x = 500*factor; //230*factor; //Off screen + int d_gamelist_x = 230*factor; //Off screen + int d_gamelist_y = d_dialog_y + 20; + + int d_playerlist_w = 106*factor; + int d_playerlist_h = 27*factor; + int d_playerlist_x = 10 * factor; //Off screen + //int d_playerlist_x = 500*factor; //10 * factor; //Off screen + int d_playerlist_y = d_gamelist_y + 20; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*factor; +#else + int d_cancel_w = 40*factor; +#endif + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*factor; + + bool ready_to_go = false; + + char text_buffer[64]; + int width; + int height; + strcpy(text_buffer, "Connecting...."); + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(text_buffer, SeenBuff.Get_Height(), width, height); + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_CANCEL = 100, + BUTTON_GAMELIST, + BUTTON_PLAYERLIST, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + JoinStateType joinstate = JOIN_NOTHING; // current "state" of this dialog + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int game_index = -1; // index of currently-selected game + int join_index = -1; // index of game we're joining + int rc = 0; // -1 = user cancelled, 1 = New + JoinEventType event; // event from incoming packet + int i,j; // loop counter + int parms_received; // 1 = game options received + + unsigned char tmp_id[MAX_PLAYERS]; // temp storage for sorting player ID's + int min_index; // for sorting player ID's + unsigned char min_id; // for sorting player ID's + unsigned char id; // connection ID + char * item; + unsigned long starttime; + + NodeNameType *who; + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + ColorListClass playerlist(BUTTON_PLAYERLIST, + d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + ListClass gamelist(BUTTON_GAMELIST, + d_gamelist_x, d_gamelist_y, d_gamelist_w, d_gamelist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + d_cancel_x, d_cancel_y); +#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +#endif + + /* + ----------------------------- Various Inits ------------------------------ + */ + //MPlayerColorIdx = MPlayerPrefColor; // init my preferred color + + playerlist.Set_Selected_Style(ColorListClass::SELECT_NONE); + + Fancy_Text_Print("", 0, 0, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /* + --------------------------- Send network query --------------------------- + */ + CCDebugString ("C&C95 - About to call Send_Join_Queries.\n"); + Send_Join_Queries (game_index, 1, 0); + + /* + ** Process the message loop until we are in focus. + */ + if (!GameInFocus){ + CCDebugString ("C&C95 - Waiting for game to come into focus."); + do { + CCDebugString ("."); + Keyboard::Check(); + }while (!GameInFocus); + CCDebugString ("\n"); + AllSurfaces.SurfacesRestored=FALSE; + } + + CCDebugString ("C&C95 - About to uncompress title page.\n"); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + CCDebugString ("C&C95 - About to set the palette.\n"); + Set_Palette(Palette); + CCDebugString ("C&C95 - Palette was set OK.\n"); + + if (LogicPage != &SeenBuff && LogicPage!= &HidPage){ + CCDebugString ("C&C95 - Logic page invalid\n"); + Set_Logic_Page (SeenBuff); + } + + + char a_buffer [128]; + sprintf (a_buffer, "C&C95 - Number of players:%d\n", Players.Count()); + CCDebugString (a_buffer); + + /* + ---------------------------- Init Mono Output ---------------------------- + */ + CCDebugString ("C&C95 - About to reveal mouse\n"); + while (Get_Mouse_State() > 0) Show_Mouse(); + + /* + ---------------------------- Processing loop ----------------------------- + */ + CCDebugString ("C&C95 - Entering join dialogue loop\n"); + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_NONE, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(text_buffer, d_dialog_cx-width/2, d_dialog_y + 25*factor, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /* + .................... Rebuild the button list .................... + */ + cancelbtn.Zap(); + gamelist.Zap(); + playerlist.Zap(); + + commands = &cancelbtn; + gamelist.Add_Tail(*commands); + playerlist.Add_Tail(*commands); + } + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF + - If we're part of a game, stay in this dialog; otherwise, exit + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_SIGN_OFF; + strcpy(GPacket.Name,MPlayerName); + + /*............................................................... + If we're joined to a game, make extra sure the other players in + that game know I'm exiting; send my SIGN_OFF as an ack-required + packet. Do not send this packet to myself (index 0). + ...............................................................*/ + if (joinstate == JOIN_CONFIRMED) { + // + // Remove myself from the player list box + // + item = (char *)(playerlist.Get_Item(0)); + playerlist.Remove_Item(item); + delete [] item; + playerlist.Flag_To_Redraw(); + + // + // Remove myself from the Players list + // + who = Players[0]; + Players.Delete(0); + delete who; + + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 1, + &(Players[i]->Address)); + Ipx.Service(); + } + } + + /*............................................................... + Now broadcast my SIGN_OFF so other players looking at this game + know I'm leaving. + ...............................................................*/ + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + + if (IsBridge) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + if (joinstate != JOIN_CONFIRMED) { + process = false; + rc = -1; + } else { + MPlayerGameName[0] = 0; + joinstate = JOIN_NOTHING; + display = REDRAW_ALL; + } + break; + + /*------------------------------------------------------------------ + JOIN: send a join request packet & switch to waiting-for-confirmation + mode. (Request_To_Join fills in MPlayerName with my namebuf.) + ------------------------------------------------------------------*/ + default: + if (joinstate == JOIN_NOTHING && Games.Count()!=0){ + gamelist.Set_Selected_Index(0); + join_index = gamelist.Current_Index(); + parms_received = 0; + if (Request_To_Join (MPlayerName, join_index, &playerlist, MPlayerHouse, + MPlayerColorIdx)) { + joinstate = JOIN_WAIT_CONFIRM; + } else { + display = REDRAW_ALL; + } + } + break; + + } + + /*--------------------------------------------------------------------- + Resend our query packets + ---------------------------------------------------------------------*/ + Send_Join_Queries(game_index, 0, 0); + + /*--------------------------------------------------------------------- + Process incoming packets + ---------------------------------------------------------------------*/ + event = Get_Join_Responses(&joinstate, &gamelist, &playerlist, + join_index); + /*..................................................................... + If we've changed state, redraw everything; if we're starting the game, + break out of the loop. If we've just joined, send out a player query + so I'll get added to the list instantly. + .....................................................................*/ + if (event == EV_STATE_CHANGE) { + display = REDRAW_ALL; + if (joinstate==JOIN_GAME_START) { +CCDebugString ("C&C95 - Received 'GO' packet\n"); + + ready_to_go = true; + } else { + + /*.................................................................. + If we're newly-confirmed, immediately send out a player query + ..................................................................*/ + if (joinstate==JOIN_CONFIRMED) { + + Clear_Player_List(&playerlist); + + item = new char [MPLAYER_NAME_MAX + 4]; + if (MPlayerHouse==HOUSE_GOOD) { + sprintf(item,"%s\t%s",MPlayerName,Text_String(TXT_G_D_I)); + } else { + sprintf(item,"%s\t%s",MPlayerName,Text_String(TXT_N_O_D)); + } + playerlist.Add_Item (item, MPlayerTColors[MPlayerColorIdx]); + + who = new NodeNameType; + strcpy(who->Name, MPlayerName); + who->Address = IPXAddressClass(); + who->Player.House = MPlayerHouse; + who->Player.Color = MPlayerColorIdx; + Players.Add (who); + + Send_Join_Queries (game_index, 0, 1); + } else { + + /*.................................................................. + If we've been rejected, clear any messages we may have been typing. + ..................................................................*/ + if (joinstate==JOIN_REJECTED) { + + // + // Remove myself from the player list box + // + item = (char *)(playerlist.Get_Item(0)); + if (item){ + playerlist.Remove_Item(item); + delete [] item; + playerlist.Flag_To_Redraw(); + } + + // + // Remove myself from the Players list + // + if (Players.Count()){ + who = Players[0]; + Players.Delete(0); + delete who; + } + } + } + } + } else + + /*..................................................................... + If a new game is detected, and it's the first game on our list, + automatically send out a player query for that game. + .....................................................................*/ + if (event == EV_NEW_GAME && gamelist.Count()==1) { + gamelist.Set_Selected_Index(0); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, 0, 1); + } else + + /*..................................................................... + If the game options have changed, print them. + .....................................................................*/ + if (event == EV_GAME_OPTIONS) { + parms_received = 1; + display = REDRAW_MESSAGE; + } else + + /*..................................................................... + Draw an incoming message + .....................................................................*/ + if (event == EV_MESSAGE) { + display = REDRAW_MESSAGE; + } else + + /*..................................................................... + A game before the one I've selected is gone, so we have a new index now. + 'game_index' must be kept set to the currently-selected list item, so + we send out queries for the currently-selected game. It's therefore + imperative that we detect any changes to the game list. + If we're joined in a game, we must decrement our game_index to keep + it aligned with the game we're joined to. + .....................................................................*/ + if (event == EV_GAME_SIGNOFF) { + if (joinstate==JOIN_CONFIRMED) { + game_index--; + join_index--; + gamelist.Set_Selected_Index(join_index); + } else { + gamelist.Flag_To_Redraw(); + Clear_Player_List(&playerlist); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, 0, 1); + } + } + + /*--------------------------------------------------------------------- + Service the Ipx connections + ---------------------------------------------------------------------*/ + Ipx.Service(); + + /*--------------------------------------------------------------------- + Clean out the Game List; if an old entry is found: + - Remove it + - Clear the player list + - Send queries for the new selected game, if there is one + ---------------------------------------------------------------------*/ + for (i = 0; i < Games.Count(); i++) { + if (TickCount.Time() - Games[i]->Game.LastTime > 400) { + Games.Delete(Games[i]); + item = (char *)(gamelist.Get_Item (i)); + gamelist.Remove_Item (item); + delete [] item; + if (i <= game_index) { + gamelist.Flag_To_Redraw(); + Clear_Player_List(&playerlist); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, 0, 1); + } + } + } + + /* + ** If we were flagged to start the game and we recognise both players then quit the loop + */ + +char ddkks[128]; +sprintf (ddkks, "C&C95 - Players.Count() = %d\n", Players.Count()); +CCDebugString (ddkks); + if (ready_to_go){ // && Players.Count() == InternetMaxPlayers){ + rc = 0; + process = false; + } + + /*--------------------------------------------------------------------- + Service the sounds & score; GameActive must be false at this point, + so Call_Back() doesn't intercept global messages from me! + ---------------------------------------------------------------------*/ + Call_Back(); + } + + /*------------------------------------------------------------------------ + Establish connections with all other players. + ------------------------------------------------------------------------*/ + if (rc == 0) { + /*..................................................................... + If the other guys are playing a scenario I don't have (sniff), I can't + play. Try to bail gracefully. + .....................................................................*/ + if (ScenarioIdx==-1) { + CCMessageBox().Process (TXT_UNABLE_PLAY_WAAUGH); + + // + // Remove myself from the player list box + // + item = (char *)(playerlist.Get_Item(0)); + playerlist.Remove_Item(item); + delete [] item; + playerlist.Flag_To_Redraw(); + + // + // Remove myself from the Players list + // + who = Players[0]; + Players.Delete(0); + delete who; + + memset (&GPacket, 0, sizeof(GlobalPacketType)); + + GPacket.Command = NET_SIGN_OFF; + strcpy (GPacket.Name, MPlayerName); + + for (i = 0; i < Players.Count(); i++) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 1, + &(Players[i]->Address)); + Ipx.Service(); + } + + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&GPacket, sizeof (GlobalPacketType), + 0, NULL); + + if (IsBridge) { + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + Ipx.Send_Global_Message (&GPacket, sizeof(GlobalPacketType), 0, + &BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + rc = -1; + + } else { + + /*.................................................................. + Set the number of players in this game, and my ID + ..................................................................*/ + MPlayerCount = Players.Count(); + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + /*.................................................................. + Get the scenario number + ..................................................................*/ + Scenario = ScenarioIdx; //PlayerFilenum[ScenarioIdx]; We are passed actual number now from wchat not index from + + /*.................................................................. + Form connections with all other players. Form the IPX Connection ID + from the player's Color and House. This will let us extract any + player's color & house at any time. Fill in 'tmp_id' while we're + doing this. + ..................................................................*/ + for (i = 0; i < Players.Count(); i++) { + + /*............................................................... + Only create the connection if it's not myself! + ...............................................................*/ + if (strcmp (MPlayerName, Players[i]->Name)) { + id = Build_MPlayerID(Players[i]->Player.Color, + Players[i]->Player.House); + + tmp_id[i] = id; + + Ipx.Create_Connection((int)id, Players[i]->Name, &(Players[i]->Address) ); + } else { + tmp_id[i] = MPlayerLocalID; + } + } + + /* + ** Create an additional connection to the VSS + */ + if (UseVirtualSubnetServer){ + IPXAddressClass vss_global_address; + NetNodeType vss_node; + NetNumType vss_net; + memset (vss_net, 1, sizeof (vss_net)); + memset (vss_node, 0, sizeof (vss_node)); + vss_global_address.Set_Address(vss_net, vss_node); + Ipx.Create_Connection( VSS_ID, "VSS", &vss_global_address); + } + + /*.................................................................. + Store every player's ID in the MPlayerID[] array. This array will + determine the order of event execution, so the ID's must be stored + in the same order on all systems. + ..................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + min_index = 0; + min_id = 0xff; + for (j = 0; j < MPlayerCount; j++) { + if (tmp_id[j] < min_id) { + min_id = tmp_id[j]; + min_index = j; + } + } + MPlayerID[i] = tmp_id[min_index]; + tmp_id[min_index] = 0xff; + } + /*.................................................................. + Fill in the array of player names, including my own. + ..................................................................*/ + for (i = 0; i < MPlayerCount; i++) { + if (MPlayerID[i] == MPlayerLocalID) { + strcpy (MPlayerNames[i], MPlayerName); + } else { + strcpy (MPlayerNames[i], Ipx.Connection_Name(MPlayerID[i])); + } + } + } + /*--------------------------------------------------------------------- + Wait a while, polling the IPX service routines, to give our ACK + a chance to get to the other system. If he doesn't get our ACK, he'll + be waiting the whole time we load MIX files. + ---------------------------------------------------------------------*/ + i = MAX(Ipx.Global_Response_Time() * 2, 120); + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < i) { + Ipx.Service(); + } + } + + /*------------------------------------------------------------------------ + Init network timing values, using previous response times as a measure + of what our retry delta & timeout should be. + ------------------------------------------------------------------------*/ + Ipx.Set_Timing (Ipx.Global_Response_Time() + 2, -1, + Ipx.Global_Response_Time() * 4); + + /*------------------------------------------------------------------------ + Clear all lists + ------------------------------------------------------------------------*/ + Clear_Game_List(&gamelist); + Clear_Player_List(&playerlist); + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Show_Mouse(); + + return(rc); +} + + + +#endif diff --git a/NOD.BAT b/NOD.BAT new file mode 100644 index 0000000..e8c6d9b --- /dev/null +++ b/NOD.BAT @@ -0,0 +1,5 @@ +@echo off +pushd +cd ..\run +conquer -CD..\cd2\aud1;..\cd2;..\cd2\install %1 %2 %3 %4 %5 +popd diff --git a/NOSEQCON.CPP b/NOSEQCON.CPP new file mode 100644 index 0000000..8da01b8 --- /dev/null +++ b/NOSEQCON.CPP @@ -0,0 +1,690 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\noseqcon.cpv 1.9 16 Oct 1995 16:49:38 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SEQCONN.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : April 9, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * NonSequencedConnClass::NonSequencedConnClass -- class constructor * + * NonSequencedConnClass::~NonSequencedConnClass -- class destructor * + * NonSequencedConnClass::Init -- Initializes connection queue to empty * + * NonSequencedConnClass::Send_Packet -- adds a packet to the send queue * + * NonSequencedConnClass::Receive_Packet -- adds packet to receive queue * + * NonSequencedConnClass::Get_Packet -- gets a packet from receive queue * + * NonSequencedConnClass::Service_Send_Queue -- services the send queue * + * NonSequencedConnClass::Service_Receive_Queue -- services recieve queue* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * NonSequencedConnClass::NonSequencedConnClass -- class constructor * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the recieve queue * + * maxlen max length of an application packet * + * magicnum the packet "magic number" for this connection * + * retry_delta the time to wait between sends * + * max_retries the max # of retries allowed for a packet * + * (-1 means retry forever, based on this parameter) * + * timeout the max amount of time before we give up on a packet * + * (-1 means retry forever, based on this parameter) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NonSequencedConnClass::NonSequencedConnClass (int numsend, int numreceive, + int maxlen, unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout) : + ConnectionClass (maxlen, magicnum, retry_delta, max_retries, timeout) +{ + /*------------------------------------------------------------------------ + Allocate the packet Queue. This will store incoming packets (which will + be placed there by the Connection Manager), and outgoing packets (which + are placed there by this class when it "sends" a packet). + ------------------------------------------------------------------------*/ + Queue = new CommBufferClass (numsend, numreceive, MaxPacketLen); +} + + +/*************************************************************************** + * NonSequencedConnClass::~NonSequencedConnClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NonSequencedConnClass::~NonSequencedConnClass () +{ + delete Queue; +} + + +/*************************************************************************** + * NonSequencedConnClass::Init -- Initializes connection queue to empty * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void NonSequencedConnClass::Init (void) +{ + NumRecNoAck = 0; + NumRecAck = 0; + NumSendNoAck = 0; + NumSendAck = 0; + + LastSeqID = 0xffffffff; + LastReadID = 0xffffffff; + + Queue->Init(); +} + + +/*************************************************************************** + * NonSequencedConnClass::Send_Packet -- adds a packet to the send queue * + * * + * This routine prefixes the given buffer with a CommHeaderType and * + * queues the resulting packet into the Send Queue. (It's actually the * + * Service() routine that handles the hardware-dependent Send of the data).* + * The packet's MagicNumber, Code, and PacketID are set here. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * ack_req true = ACK is required for this packet; false = isn't * + * * + * OUTPUT: * + * 1 = packet was queue'd OK, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NonSequencedConnClass::Send_Packet (void * buf, int buflen, int ack_req) +{ + /*........................................................................ + Set the magic # for the packet + ........................................................................*/ + ((CommHeaderType *)PacketBuf)->MagicNumber = MagicNum; + + /*........................................................................ + Set the packet Code: DATA_ACK if it requires an ACK, NOACK if it doesn't + Set the packet ID to the appropriate counter value. + ........................................................................*/ + if (ack_req) { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_ACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendAck; + } else { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_NOACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendNoAck; + } + + /*........................................................................ + Now build the packet + ........................................................................*/ + memcpy(PacketBuf + sizeof(CommHeaderType), buf, buflen); + + /*........................................................................ + Add it to the queue. + ........................................................................*/ + if (Queue->Queue_Send(PacketBuf,buflen + sizeof(CommHeaderType))) { + if (ack_req) { +// Smart_Printf( "Packet ack Queued ID %d \n", ((CommHeaderType *)PacketBuf)->PacketID ); + NumSendAck++; + } else { +// Smart_Printf( "Packet noack Queued ID %d \n", ((CommHeaderType *)PacketBuf)->PacketID ); + NumSendNoAck++; + } + return(true); + } else { +// Smart_Printf( "Packet not Queued ID %d \n", ((CommHeaderType *)PacketBuf)->PacketID ); + return(false); + } +} + + +/*************************************************************************** + * NonSequencedConnClass::Receive_Packet -- adds packet to the receive queue * + * * + * INPUT: * + * buf buffer to process (already includes CommHeaderType) * + * buflen length of buffer to process * + * * + * OUTPUT: * + * 1 = packet was processed OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NonSequencedConnClass::Receive_Packet (void * buf, int buflen) +{ + CommHeaderType *packet; // ptr to packet header + SendQueueType *send_entry; // ptr to send entry header + ReceiveQueueType *rec_entry; // ptr to recv entry header + CommHeaderType *entry_data; // ptr to queue entry data + CommHeaderType ackpacket; // ACK packet to send + int i; + int save_packet = 1; // 0 = this is a resend + int found; + + /* + --------------------------- Check the magic # ---------------------------- + */ + packet = (CommHeaderType *)buf; + if (packet->MagicNumber != MagicNum) { +// Smart_Printf( "Bad Magic Number\n" ); + return(false); + } + + /*------------------------------------------------------------------------ + Handle an incoming ACK + ------------------------------------------------------------------------*/ + if (packet->Code == PACKET_ACK) { + + for (i = 0; i < Queue->Num_Send(); i++) { + /* + ....................... Get queue entry ptr ........................ + */ + send_entry = Queue->Get_Send(i); + /* + ............... If ptr is valid, get ptr to its data ............... + */ + if (send_entry != NULL) { + entry_data = (CommHeaderType *)send_entry->Buffer; + /* + .............. If ACK is for this entry, mark it ................ + */ + if (packet->PacketID==entry_data->PacketID && + entry_data->Code == PACKET_DATA_ACK) { +// Smart_Printf( "Received ACK for %d \n", packet->PacketID ); + send_entry->IsACK = 1; + break; + } + } + } + +//{ +// if (i == Queue->Num_Send() ) { +// Smart_Printf( "Received bad ACK for %d \n", packet->PacketID ); +// } +//} + + return(true); + } + + /*------------------------------------------------------------------------ + Handle an incoming PACKET_DATA_NOACK packet + ------------------------------------------------------------------------*/ + else if (packet->Code == PACKET_DATA_NOACK) { + /*--------------------------------------------------------------------- + If there's only one slot left, don't tie up the queue with this packet + ---------------------------------------------------------------------*/ + if (Queue->Max_Receive() - Queue->Num_Receive() <= 1) { +// Smart_Printf( "Only one slot left don't tie up with DATA NOACK packet %d \n", packet->PacketID ); + return(false); + } + + /*--------------------------------------------------------------------- + Error if we can't queue the packet + ---------------------------------------------------------------------*/ + if (!Queue->Queue_Receive (buf, buflen)) { +// Smart_Printf( "Can't Queue the packet %d \n", packet->PacketID ); + return(false); + } + +// Smart_Printf( "Queued DATA NOACK for %d \n", packet->PacketID ); + NumRecNoAck++; + + return(true); + } + + /*------------------------------------------------------------------------ + Handle an incoming PACKET_DATA_ACK packet + ------------------------------------------------------------------------*/ + else if (packet->Code == PACKET_DATA_ACK) { +// Smart_Printf( "Looking at ID %d, LastSeqID=%d \n", packet->PacketID, LastSeqID ); + /*.................................................................... + If this is a packet requires an ACK, and it's ID is older than our + "oldest" ID, we know it's a resend; send an ACK, but don't queue it + ....................................................................*/ + if (packet->PacketID <= LastSeqID && LastSeqID != 0xffffffff) { +// Smart_Printf( "Older than oldest\n" ); + save_packet = 0; + } + /*.................................................................... + Otherwise, scan the queue for this entry; if it's found, it's a + resend, so don't save it. + ....................................................................*/ + else { + save_packet = 1; + for (i = 0; i < Queue->Num_Receive(); i++) { + rec_entry = Queue->Get_Receive(i); + if (rec_entry) { + entry_data = (CommHeaderType *)rec_entry->Buffer; + /*........................................................... + Packet is found; it's a resend + ...........................................................*/ + if (entry_data->Code == PACKET_DATA_ACK && + entry_data->PacketID == packet->PacketID) { +// Smart_Printf( "It's a resend\n" ); + save_packet = 0; + break; + } + } + } + } /* end of scan for resend */ + + /*--------------------------------------------------------------------- + Queue the packet & update our LastSeqID value. + ---------------------------------------------------------------------*/ + if (save_packet) { + /*------------------------------------------------------------------ + If there's only one slot left, make sure we only put a packet in it if + this packet will let us increment our LastSeqID; otherwise, we'll get + stuck, forever unable to increment LastSeqID. + ------------------------------------------------------------------*/ + if (Queue->Max_Receive() - Queue->Num_Receive() <= 1) { + if (packet->PacketID != (LastSeqID + 1) ) { +// Smart_Printf( "One slot left not what we looking for max=%d,num=%d \n", +// Queue->Max_Receive(), Queue->Num_Receive() ); + return(0); + } + } + + /*------------------------------------------------------------------ + If we can't queue the packet, return; don't send an ACK. + ------------------------------------------------------------------*/ + if (!Queue->Queue_Receive (buf, buflen)) { +// Smart_Printf( "unable to queue packet\n" ); + return(0); + } + + NumRecAck++; + + /*------------------------------------------------------------------ + Update our LastSeqID value if we can. Anything less than LastSeqID + we'll know is a resend. + ------------------------------------------------------------------*/ + if (packet->PacketID == (LastSeqID + 1)) { + LastSeqID = packet->PacketID; + /*............................................................ + Now that we have a new 'LastSeqID', search our Queue to see if + the next ID is there; if so, keep checking for the next one; + break only when the next one isn't found. This forces + LastSeqID to be the largest possible value. + ............................................................*/ + do { + found = 0; + for (i = 0; i < Queue->Num_Receive(); i++) { + + rec_entry = Queue->Get_Receive(i); + + if (rec_entry) { + entry_data = (CommHeaderType *)rec_entry->Buffer; + + /*...................................................... + Entry is found + ......................................................*/ + if (entry_data->Code == PACKET_DATA_ACK && + entry_data->PacketID == (LastSeqID + 1)) { + + LastSeqID = entry_data->PacketID; + found = 1; + break; + } + } + } + } while (found); + } + } /* end of save packet */ + + /*--------------------------------------------------------------------- + Send an ACK, regardless of whether this was a resend or not. + ---------------------------------------------------------------------*/ + ackpacket.MagicNumber = Magic_Num(); + ackpacket.Code = PACKET_ACK; + ackpacket.PacketID = packet->PacketID; +// Smart_Printf( "Sending ACK for %d \n", packet->PacketID ); + Send ((char *)&ackpacket, sizeof(CommHeaderType)); + + return(true); + + } else { +// Smart_Printf( "invalid packet type %d \n", packet->Code ); + } + + return(false); +} + + +/*************************************************************************** + * NonSequencedConnClass::Get_Packet -- gets a packet from receive queue * + * * + * INPUT: * + * buf location to store buffer * + * buflen filled in with length of 'buf' * + * * + * OUTPUT: * + * 1 = packet was read, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NonSequencedConnClass::Get_Packet (void * buf, int *buflen) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + int packetlen; // size of received packet + CommHeaderType *entry_data; + int i; + + /*------------------------------------------------------------------------ + Ensure that we read the packets in order. LastReadID is the ID of the + last PACKET_DATA_ACK packet we read. + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Receive(); i++) { + + rec_entry = Queue->Get_Receive(i); + + /*..................................................................... + Only read this entry if it hasn't been yet + .....................................................................*/ + if (rec_entry && rec_entry->IsRead==0) { + + entry_data = (CommHeaderType *)rec_entry->Buffer; + + /*.................................................................. + If this is a DATA_ACK packet, its ID must be one greater than + the last one we read. + ..................................................................*/ + if ( (entry_data->Code == PACKET_DATA_ACK) && + (entry_data->PacketID == (LastReadID + 1))) { + + LastReadID = entry_data->PacketID; + rec_entry->IsRead = 1; + + packetlen = rec_entry->BufLen - sizeof(CommHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType), packetlen); + } + (*buflen) = packetlen; + return(true); + } + /*.................................................................. + If this is a DATA_NOACK packet, who cares what the ID is? + ..................................................................*/ + else if (entry_data->Code == PACKET_DATA_NOACK) { + + rec_entry->IsRead = 1; + + packetlen = rec_entry->BufLen - sizeof(CommHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType), packetlen); + } + (*buflen) = packetlen; + return(true); + } + } + } + + return(false); +} + + +/*************************************************************************** + * NonSequencedConnClass::Service_Send_Queue -- services the send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NonSequencedConnClass::Service_Send_Queue (void) +{ + int i; + int num_entries; + SendQueueType *send_entry; // ptr to send queue entry + CommHeaderType *packet_hdr; // packet header + unsigned long curtime; // current time + int bad_conn = 0; + + /*------------------------------------------------------------------------ + Remove any ACK'd packets from the queue + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Send(); i++) { + /* + ------------------------- Get this queue entry ------------------------ + */ + send_entry = Queue->Get_Send(i); + /* + ---------------- If ACK has been received, unqueue it ----------------- + */ + if (send_entry->IsACK) { + /* + ................ Update this queue's response time ................. + */ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_ACK) { + Queue->Add_Delay(Time() - send_entry->FirstTime); + } + /* + ....................... unqueue the packet ......................... + */ + Queue->UnQueue_Send(NULL,NULL,i); + i--; + } + } + + /*------------------------------------------------------------------------ + Loop through all entries in the Send queue. [Re]Send any entries that + need it. + ------------------------------------------------------------------------*/ + num_entries = Queue->Num_Send(); + + for (i = 0; i < num_entries; i++) { + send_entry = Queue->Get_Send(i); + + if (send_entry->IsACK) { + continue; + } + + /*..................................................................... + Only send the message if time has elapsed. (The message's Time + fields are init'd to 0 when a message is queue'd or unqueue'd, so the + first time through, the delta time will appear large.) + .....................................................................*/ + curtime = Time(); + if (curtime - send_entry->LastTime > RetryDelta) { + /* + ......................... Send the message ......................... + */ +#if(0) +{ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (send_entry->SendCount) { + if (packet_hdr->Code == PACKET_DATA_NOACK) { +// Smart_Printf( "Resending DATA NOACK for %d \n", packet_hdr->PacketID ); + } else { + if (packet_hdr->Code == PACKET_DATA_ACK) { +// Smart_Printf( "Resending DATA ACK for %d \n", packet_hdr->PacketID ); + } + } + } else { + if (packet_hdr->Code == PACKET_DATA_NOACK) { +// Smart_Printf( "Sending DATA NOACK for %d \n", packet_hdr->PacketID ); + } else { + if (packet_hdr->Code == PACKET_DATA_ACK) { +// Smart_Printf( "Sending DATA ACK for %d \n", packet_hdr->PacketID ); + } + } + } +} +#endif + Send (send_entry->Buffer, send_entry->BufLen); + + /* + ....................... Fill in Time fields ........................ + */ + send_entry->LastTime = curtime; + if (send_entry->SendCount==0) { + send_entry->FirstTime = curtime; + /*............................................................... + If this is the 1st time we're sending this packet, and it doesn't + require an ACK, mark it as ACK'd; then, the next time through, + it will just be removed from the queue. + ...............................................................*/ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_NOACK) { + send_entry->IsACK = 1; + } + } + /* + ......................... Update SendCount ......................... + */ + send_entry->SendCount++; + /*.................................................................. + Perform error detection, based on either MaxRetries or Timeout + ..................................................................*/ + if (MaxRetries != -1 && send_entry->SendCount > MaxRetries) { +// Smart_Printf( "Max Retries!!! %d !!! \n", MaxRetries ); + bad_conn = 1; + } + + if (Timeout != -1 && + (send_entry->LastTime - send_entry->FirstTime) > Timeout) { +// Smart_Printf( "Timed out!!! Time %d, Timeout %d, buflen %d !!! \n", +// (send_entry->LastTime - send_entry->FirstTime), Timeout, +// send_entry->BufLen ); + bad_conn = 1; + } + } + } + + /*------------------------------------------------------------------------ + If the connection is going bad, return an error + ------------------------------------------------------------------------*/ + if (bad_conn) { +// Smart_Printf( "Connection going bad!!! \n" ); + return(false); + } else { + return(true); + } +} + + +/*************************************************************************** + * NonSequencedConnClass::Service_Receive_Queue -- services recieve queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NonSequencedConnClass::Service_Receive_Queue (void) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + CommHeaderType *packet_hdr; // packet header + int i; + + /*------------------------------------------------------------------------ + Remove all dead packets. + PACKET_DATA_NOACK: if it's been read, throw it away. + PACKET_DATA_ACK: if it's been read, and its ID is older than LastSeqID, + throw it away. + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Receive(); i++) { + rec_entry = Queue->Get_Receive(i); + + if (rec_entry->IsRead) { + packet_hdr = (CommHeaderType *)(rec_entry->Buffer); + + if (packet_hdr->Code == PACKET_DATA_NOACK) { + Queue->UnQueue_Receive(NULL,NULL,i); + i--; + } else { + if (packet_hdr->PacketID < LastSeqID) { + Queue->UnQueue_Receive(NULL,NULL,i); + i--; + } + } + } + } + + return(true); +} + + diff --git a/NOSEQCON.H b/NOSEQCON.H new file mode 100644 index 0000000..9be0fe6 --- /dev/null +++ b/NOSEQCON.H @@ -0,0 +1,129 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\noseqcon.h_v 1.12 16 Oct 1995 16:46:22 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NOSEQCON.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 9, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This class provides a "Non-Sequenced" ACK/Retry approach to packet * + * transmission. It sends out as many packets as are in the queue, whose * + * resend delta times have expired; and it ACK's any packets its received * + * who haven't been ACK'd yet. Thus, order of delivery is NOT guaranteed; * + * however, the performance is better than the Sequenced approach. * + * * + * A derived class must provide: * + * - Init: Initialization of any hardware-specific values. * + * - Send: a hardware-dependent send routine. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef NONSEQCONN_H +#define NONSEQCONN_H + + +/* +********************************* Includes ********************************** +*/ +#include "connect.h" +#include "combuf.h" + + +/* +***************************** Class Declaration ***************************** +*/ +class NonSequencedConnClass : public ConnectionClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + NonSequencedConnClass (int numsend, int numrecieve, int maxlen, + unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout); + virtual ~NonSequencedConnClass (); + + /*..................................................................... + Initialization. + .....................................................................*/ + virtual void Init (void); + + /*..................................................................... + Send/Receive routines. + .....................................................................*/ + virtual int Send_Packet (void * buf, int buflen, int ack_req); + virtual int Receive_Packet (void * buf, int buflen); + virtual int Get_Packet (void * buf, int *buflen); + + /*..................................................................... + The packet "queue"; this non-sequenced version isn't really much of + a queue, but more of a repository. + .....................................................................*/ + CommBufferClass *Queue; + + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + /*..................................................................... + Routines to service the Send & Receive queues. + .....................................................................*/ + virtual int Service_Send_Queue (void); + virtual int Service_Receive_Queue (void); + + /*..................................................................... + Running totals of # of packets we send & receive which require an ACK, + and those that don't. + .....................................................................*/ + unsigned long NumRecNoAck; + unsigned long NumRecAck; + unsigned long NumSendNoAck; + unsigned long NumSendAck; + + /*..................................................................... + This is the ID of the last consecutively-received packet; anything older + than this, we know is a resend. Anything newer than this MUST be lying + around in the Queue for us to detect it as a resend. + .....................................................................*/ + unsigned long LastSeqID; + + /*..................................................................... + This is the ID of the PACKET_DATA_ACK packet we read last; it ensures + that the application reads that type of packet in order. + .....................................................................*/ + unsigned long LastReadID; +}; + +#endif + + diff --git a/NULLCONN.CPP b/NULLCONN.CPP new file mode 100644 index 0000000..bd75959 --- /dev/null +++ b/NULLCONN.CPP @@ -0,0 +1,265 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\nullconn.cpv 1.10 16 Oct 1995 16:51:36 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NULLCONN.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : April 5, 1995 * + * * + * Last Update : April 20, 1995 [DRD] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * NullModemConnClass::NullModemConnClass -- class constructor * + * NullModemConnClass::~NullModemConnClass -- class destructor * + * NullModemConnClass::Init -- hardware-dependent initialization * + * NullModemConnClass::Send -- hardware-dependent packet sending * + * NullModemConnClass::Compute_CRC -- computes CRC for given buffer * + * NullModemConnClass::Packet_Overhead_Size -- number of extra bytes * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "wincomm.h" +#include "tcpip.h" + +/*************************************************************************** + * NullModemConnClass::NullModemConnClass -- class constructor * + * * + * INPUT: * + * numsend desired # send queue entries * + * numreceive desired # send receive entries * + * maxlen max length of application's packets * + * magicnum application-defined magic # for the packets * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NullModemConnClass::NullModemConnClass (int numsend, int numreceive, + int maxlen, unsigned short magicnum) : + NonSequencedConnClass (numsend, numreceive, maxlen, magicnum, + 60, // Retry Delta Time + -1, // Max Retries (-1 means ignore this timeout parameter) + 1200) // Timeout: 20 seconds +{ + /*------------------------------------------------------------------------ + Pre-set the port value to NULL, so Send won't send until we've been Init'd + ------------------------------------------------------------------------*/ + PortHandle = NULL; + + /*------------------------------------------------------------------------ + Allocate the Send Buffer; the parent constructor has set MaxPacketLen, + so we can use it in our computation. + ------------------------------------------------------------------------*/ +// SendBuf = new char [MaxPacketLen + sizeof(int) * 3]; + SendBuf = new char [ Actual_Max_Packet() ]; + +} /* end of NullModemConnClass */ + + +/*************************************************************************** + * NullModemConnClass::~NullModemConnClass -- class destructor * + * * + * INPUT: * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NullModemConnClass::~NullModemConnClass () +{ + delete [] SendBuf; + +} /* end of ~NullModemConnClass */ + + +/*************************************************************************** + * NullModemConnClass::Init -- hardware-dependent initialization * + * * + * INPUT: * + * port GreenLeaf port handle * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void NullModemConnClass::Init (HANDLE port_handle) +{ + NonSequencedConnClass::Init(); + PortHandle = port_handle; + +} /* end of Init */ + + +/*************************************************************************** + * NullModemConnClass::Send -- hardware-dependent packet sending * + * * + * INPUT: * + * port GreenLeaf port handle * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * 1 = OK, 0 = error * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NullModemConnClass::Send (char *buf, int buflen) +{ + //int status; + int *ibuf; + SerialHeaderType *header; + unsigned long sendlen; + + + /*------------------------------------------------------------------------ + Error if we haven't been properly initialized + ------------------------------------------------------------------------*/ + if ( PortHandle == NULL ) + return(false); + + /*------------------------------------------------------------------------ + Package the data into the Send Buffer + ------------------------------------------------------------------------*/ + header = (SerialHeaderType *) SendBuf; + header->MagicNumber = PACKET_SERIAL_START; + header->Length = (short) buflen; + header->MagicNumber2 = PACKET_SERIAL_VERIFY; + + sendlen = sizeof( SerialHeaderType ); + memcpy (SendBuf + sendlen, buf, buflen); + sendlen += buflen; + ibuf = (int *)(SendBuf + sendlen); + *ibuf = Compute_CRC( buf, buflen ); + sendlen += sizeof( int ); + + *(SendBuf + sendlen) = '\r'; + sendlen += 1; + + /*------------------------------------------------------------------------ + Send the data + ------------------------------------------------------------------------*/ + //status = +#ifdef FORCE_WINSOCK + if (Winsock.Get_Connected() || GameToPlay == GAME_INTERNET){ + Winsock.Write(SendBuf, (int)sendlen); + }else{ + SerialPort->Write_To_Serial_Port((unsigned char *)SendBuf, (int)sendlen ); + } +#else + SerialPort->Write_To_Serial_Port((unsigned char *)SendBuf, (int)sendlen ); +#endif //WINSOCK + + //if ( status == ASSUCCESS ) { + return(true); + //} else { +// Smart_Printf( "Write Buffer status %d, Port->status %d, sendlen %d \n", status, Port->status, sendlen ); + // return(false); + //} +} + + +/*************************************************************************** + * NullModemConnClass::Compute_CRC -- computes CRC for given buffer * + * * + * INPUT: * + * buf buffer to compute CRC for * + * buflen length of buffer in bytes * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NullModemConnClass::Compute_CRC (char *buf, int buflen) +{ + unsigned int sum, hibit; + + sum = 0; + for (int i = 0; i < buflen; i++) { + if ( sum & 0x80000000 ) { // check hi bit to rotate into low bit + hibit = 1; + } else { + hibit = 0; + } + + sum <<= 1; + sum += (hibit + (unsigned char)buf[i]); + } + + return((int)sum); +} + + +/*************************************************************************** + * NullModemConnClass::Packet_Overhead_Size -- number of extra bytes * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * number of bytes used for communications only. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/20/1995 DRD : Created. * + *=========================================================================*/ +int NullModemConnClass::Packet_Overhead_Size ( void ) +{ + // + // short for Null Modem Magic Number + // short for Null Modem length of packet + // int for Null Modem CRC check + // CommHeaderType for Queued packets + // + + return( (PACKET_SERIAL_OVERHEAD_SIZE + sizeof(CommHeaderType)) ); + +} /* end of Packet_Overhead_Size */ + diff --git a/NULLCONN.H b/NULLCONN.H new file mode 100644 index 0000000..5e342f4 --- /dev/null +++ b/NULLCONN.H @@ -0,0 +1,140 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\nullconn.h_v 1.12 16 Oct 1995 16:45:54 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NULLCONN.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 3, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This is the Connection Class for a NULL-Modem connection. It inherits * + * a Queue, PacketBuf, timeout variables from ConnectionClass. It * + * inherits its Send_/Receive_/Get_Packet functions, and the non-sequenced * + * ACK/Retry logic in Service_Send_Queue & Service_Recieve_Queue from * + * NonSequencedConnClass. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef NULLCONN_H +#define NULLCONN_H + + +/* +********************************* Includes ********************************** +*/ +#include "noseqcon.h" +#include "commlib.h" + +/* +********************************** Defines ********************************** +*/ +#define PACKET_SERIAL_START 0xDABD +#define PACKET_SERIAL_VERIFY 0xDEAF + +#define PACKET_SERIAL_OVERHEAD_SIZE (sizeof( SerialHeaderType ) + sizeof( SerialCRCType )) + +typedef struct { + unsigned short MagicNumber; + unsigned short Length; + unsigned short MagicNumber2; +} SerialHeaderType; + +typedef struct { + int SerialCRC; +} SerialCRCType; + + +/* +***************************** Class Declaration ***************************** +*/ +class NullModemConnClass : public NonSequencedConnClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + NullModemConnClass (int numsend, int numrecieve, int maxlen, + unsigned short magicnum); + virtual ~NullModemConnClass (); + + /*..................................................................... + Initialization. + .....................................................................*/ + void Init (HANDLE port_handle); + + /*..................................................................... + Utility routines. + .....................................................................*/ + unsigned long Actual_Max_Packet (void) { return (MaxPacketLen + (sizeof(SerialHeaderType)) + sizeof(int) + sizeof (char)); } + + /*..................................................................... + This routine computes a CRC value for the given buffer. + .....................................................................*/ + static int Compute_CRC(char *buf, int buflen); + + /*..................................................................... + This routine returns the number of bytes extra added the packet + for communication. + .....................................................................*/ + static int Packet_Overhead_Size( void ); + + /* + --------------------------- Private Interface ---------------------------- + */ + protected: + /*..................................................................... + This routine actually performs a hardware-dependent data send. + .....................................................................*/ + int Send (char *buf, int buflen); + + /*..................................................................... + This is the PORT value used by the GreenLeaf calls. + .....................................................................*/ + HANDLE PortHandle; + PORT *Port; + + /*..................................................................... + This buffer is a staging area for data sent out; it includes the + packet sent by the parent class (which includes the application's + packet, plus the CommHeaderType header), plus: + - 2-byte buffer start ID + - 2-byte length + - 4-byte CRC value (at the end of the buffer) + This is the actual packet that gets sent across the serial line. + .....................................................................*/ + char *SendBuf; +}; + +#endif + +/************************** end of nullconn.h ******************************/ + diff --git a/NULLDLG.CPP b/NULLDLG.CPP new file mode 100644 index 0000000..637b4b0 --- /dev/null +++ b/NULLDLG.CPP @@ -0,0 +1,8178 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\nulldlg.cpv 1.9 16 Oct 1995 16:52:12 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NULLDLG.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 04/29/95 * + * * + * Last Update : April 29, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Init_Null_Modem -- Initializes Null Modem communications * + * Shutdown_Modem -- Shuts down modem/null-modem communications * + * Test_Null_Modem -- Null-Modem test routine * + * Reconnect_Null_Modem -- allows user to reconnect * + * Destroy_Null_Connection -- destroys the given connection * + * Select_Serial_Dialog -- Serial Communications menu dialog * + * Com_Settings_Dialog -- Lets user select serial port settings * + * Com_Scenario_Dialog -- Serial game scenario selection dialog * + * Phone_Dialog -- Lets user edit phone directory & dial * + * Build_InitString_Listbox -- [re]builds the initstring entry listbox * + * Init_String_Compare -- for qsort * + * Build_Phone_Listbox -- [re]builds the phone entry listbox * + * Phone_Compare -- for qsort * + * Edit_Phone_Dialog -- lets user edit a phone book entry * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "function.h" +#include "wincomm.h" +#include "modemreg.h" +#include "tcpip.h" + +ModemRegistryEntryClass *ModemRegistry = NULL; //Ptr to modem registry data + +// +// how much time (ticks) to go by before thinking other system +// is not responding. +// +#define PACKET_SENDING_TIMEOUT 1800 +#define PACKET_CANCEL_TIMEOUT 900 + +// +// how much time (ticks) to go by before sending another packet +// of game options or serial connect. +// +#define PACKET_RETRANS_TIME 30 +#define PACKET_REDRAW_TIME 60 + + +static int Reconnect_Null_Modem( void ); +static int Com_Settings_Dialog( SerialSettingsType *settings ); +static int Phone_Dialog (void); +static void Build_Init_String_Listbox (ListClass *list, EditClass *edit, char *buf, int *index); +static int Init_String_Compare (const void *p1, const void *p2); +static void Build_Phone_Listbox (ListClass *list, EditClass *edit, char *buf); +static int Phone_Compare (const void *p1, const void *p2); +static int Edit_Phone_Dialog (PhoneEntryClass *phone); +static bool Dial_Modem( SerialSettingsType *settings, bool reconnect ); +static bool Answer_Modem( SerialSettingsType *settings, bool reconnect ); +static void Modem_Echo( char c ); + +void Smart_Printf( char *format, ... ); +void Hex_Dump_Data( char *buffer, int length ); +void itoh( int i, char *s); + + +static SerialPacketType SendPacket; +static SerialPacketType ReceivePacket; +char TheirName[MPLAYER_NAME_MAX]; +unsigned char TheirColor; +HousesType TheirHouse; +unsigned char TheirID; +static char DialString[ CWAITSTRBUF_MAX + PhoneEntryClass::PHONE_MAX_NUM - 1 ]; +static SerialSettingsType *DialSettings; + +#define SHOW_MONO 0 + +/*************************************************************************** + * Init_Null_Modem -- Initializes Null Modem communications * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = OK, false = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +int Init_Null_Modem( SerialSettingsType *settings ) +{ + + if ( NullModem.Init( settings->Port, settings->IRQ, + settings->ModemName, + settings->Baud, 0, 8, 1, + settings->HardwareFlowControl ) ) { + + + return(true); + } else { + return(false); + } +} + + +/*************************************************************************** + * Shutdown_Modem -- Shuts down modem/null-modem communications * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +void Shutdown_Modem( void ) +{ + if (!PlaybackGame) { + if (GameToPlay == GAME_MODEM) { + NullModem.Hangup_Modem(); + } + } + + // + // close port + // + NullModem.Shutdown(); +} + + +/*************************************************************************** + * Modem_Signoff -- sends EXIT event * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/03/1995 DRD : Created. * + *=========================================================================*/ +void Modem_Signoff( void ) +{ + unsigned long starttime; + EventClass event; + + if (!PlaybackGame) { + /*------------------------------------------------------------------------ + Send a sign-off packet + ------------------------------------------------------------------------*/ + event.Type = EventClass::EXIT; + NullModem.Send_Message (&event,sizeof(EventClass),0); + NullModem.Send_Message (&event,sizeof(EventClass),0); + + starttime = TickCount.Time(); + while( (TickCount.Time() - starttime) < 30) { + NullModem.Service(); + } + } +} + + +/*************************************************************************** + * Test_Null_Modem -- Null-Modem test routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = failure to connect; 1 = I'm the game owner, 2 = I'm not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +int Test_Null_Modem( void ) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_CANCEL = 100, + }; + + /*........................................................................ + Dialog variables + ........................................................................*/ + bool process = true; // process while true + KeyNumType input; + + int retval; + unsigned long starttime; + int packetlen; + + int x,y,width,height; // dialog dimensions + char buffer[80*3]; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + strcpy( buffer, Text_String( TXT_WAITING_CONNECT ) ); + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(buffer, 200*factor, width, height); + + width = MAX(width, 50*factor); + width += 40*factor; + height += 60*factor; + + x = (320*factor - width) / 2; + y = (200*factor - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8 *factor)) >> 1), + y + height - (FontHeight + FontYSpacing + 2*factor) - 5*factor); + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + process = true; + + /* + ............................ Create the list ............................. + */ + commands = &cancelbtn; + + commands->Flag_List_To_Redraw(); + + /* + ............................ Draw the dialog ............................. + */ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(buffer, x + 20*factor, y + 25*factor, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + commands->Draw_All(); + while (Get_Mouse_State() > 0) Show_Mouse(); + + /* + ** This is supposed to be a direct connection so hang up any modem on this port + ** just to annoy British Telecom + */ + /* + ** Go into break mode + */ + SetCommBreak(SerialPort->Get_Port_Handle()); + + /* + ** Send hangup command + */ + SerialPort->Write_To_Serial_Port ((unsigned char*)"ATH\r", strlen("ATH\r")); + CountDownTimerClass time; + time.Set(2*60); + while (time.Time()){} + + /* + ** Back out of break mode + */ + ClearCommBreak(SerialPort->Get_Port_Handle()); + + /* + ** Drop DTR as well - just in case the modem still hasnt got the message + */ + EscapeCommFunction(SerialPort->Get_Port_Handle(), CLRDTR); + + + /*------------------------------------------------------------------------ + Check for a packet. If we detect one, the other system has already been + started. Wait 1/2 sec for him to receive my ACK, then exit with success. + Note: The initial time must be a little longer than the resend delay. + Just in case we just missed the packet. + ------------------------------------------------------------------------*/ + starttime = TickCount.Time(); + while ( TickCount.Time() - starttime < 80) { + NullModem.Service(); + if ( NullModem.Get_Message( &ReceivePacket, &packetlen ) > 0) { + if (ReceivePacket.Command == SERIAL_CONNECT) { + //Smart_Printf( "Received SERIAL_CONNECT %d, ID %d \n", ReceivePacket.Seed, ReceivePacket.ID ); + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < 30) + NullModem.Service(); + process = false; + retval = 2; + break; + } + } + } + + /*------------------------------------------------------------------------ + Send a packet across. As long as Num_Send() is non-zero, the other system + hasn't received it yet. + ------------------------------------------------------------------------*/ + if (process) { + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_CONNECT; + // + // put time from start of game for determining the host in case of tie. + // + SendPacket.Seed = TickCount.Time(); + SendPacket.ID = (int) buffer; // address of buffer for more uniqueness. + + //Smart_Printf( "Sending SERIAL_CONNECT %d, ID %d \n", SendPacket.Seed, SendPacket.ID ); + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < 80) { + NullModem.Service(); + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + if (ReceivePacket.Command == SERIAL_CONNECT) { + //Smart_Printf( "Received2 SERIAL_CONNECT %d, ID %d \n", ReceivePacket.Seed, ReceivePacket.ID ); + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < 30) + NullModem.Service(); + + // + // whoever has the highest time is the host + // + if (ReceivePacket.Seed > SendPacket.Seed) { + process = false; + retval = 2; + } else { + if (ReceivePacket.Seed == SendPacket.Seed) { + if (ReceivePacket.ID > SendPacket.ID) { + process = false; + retval = 2; + } else + // + // if they are equal then it's a loopback cable or a modem + // + if (ReceivePacket.ID == SendPacket.ID) { + process = false; + retval = 3; + } + } + } + + break; + } + } + } + } + + starttime = TickCount.Time(); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + commands->Draw_All(); + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + //Smart_Printf( "Canceled waiting for SERIAL_CONNECT\n" ); + retval = 0; + process = false; + break; + + default: + break; + } + /*..................................................................... + Service the connection. + .....................................................................*/ + NullModem.Service(); + if (NullModem.Num_Send() == 0) { + //Smart_Printf( "No more messages to send.\n" ); + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + if (ReceivePacket.Command == SERIAL_CONNECT) { + //Smart_Printf( "Received3 SERIAL_CONNECT %d, ID %d \n", ReceivePacket.Seed, ReceivePacket.ID ); + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < 30) + NullModem.Service(); + + // + // whoever has the highest time is the host + // + if (ReceivePacket.Seed > SendPacket.Seed) { + process = false; + retval = 2; + } else { + + if (ReceivePacket.Seed == SendPacket.Seed) { + if (ReceivePacket.ID > SendPacket.ID) { + process = false; + retval = 2; + } else { + + // + // if they are equal then it's a loopback cable or a modem + // + if (ReceivePacket.ID == SendPacket.ID) { + process = false; + retval = 3; + } + } + } + } + + } else { + retval = 0; + process = false; + } + } else { + retval = 1; + process = false; + } + } + + if (TickCount.Time() - starttime > 3600) { // only wait 1 minute + retval = 0; + process = false; + } + } /* end of while */ + + return( retval ); + +} + + +/*************************************************************************** + * Reconnect_Modem -- allows user to reconnect * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = failure to connect; 1 = connect OK * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +int Reconnect_Modem( void ) +{ + int status; + int modemstatus; + + + switch (ModemGameToPlay) { + case (MODEM_NULL_HOST): + case (MODEM_NULL_JOIN): + status = Reconnect_Null_Modem(); + break; + + case (MODEM_DIALER): + modemstatus = NullModem.Get_Modem_Status(); + if ( (modemstatus & CD_SET) ) { + //Smart_Printf( "Dial Modem connection error! Attempting reconnect....\n" ); + status = Reconnect_Null_Modem(); + } else { + status = Dial_Modem( DialSettings, true ); + } + break; + + case (MODEM_ANSWERER): + modemstatus = NullModem.Get_Modem_Status(); + if ( (modemstatus & CD_SET) ) { + //Smart_Printf( "Answer Modem connection error! Attempting reconnect....\n" ); + status = Reconnect_Null_Modem(); + } else { + status = Answer_Modem( DialSettings, true ); + } + break; + } + + return( status ); +} + + +/*************************************************************************** + * Reconnect_Null_Modem -- allows user to reconnect * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = failure to connect; 1 = connect OK * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static int Reconnect_Null_Modem( void ) +{ + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_CANCEL = 100, + }; + + /*........................................................................ + Dialog variables + ........................................................................*/ + bool process = true; // process while true + KeyNumType input; + + int retval; + unsigned long starttime; + unsigned long lastmsgtime; + int packetlen; + + int x,y,width,height; // dialog dimensions + char buffer[80*3]; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + strcpy( buffer, Text_String( TXT_NULL_CONNERR_CHECK_CABLES ) ); + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(buffer, 200, width, height); + + width = MAX(width, 50); + width += 40; + height += 60; + + x = (320 - width) / 2; + y = (200 - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8)) >> 1), + y + height - (FontHeight + FontYSpacing + 2) - 5); + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + process = true; + + /* + ............................ Create the list ............................. + */ + commands = &cancelbtn; + + commands->Flag_List_To_Redraw(); + + /* + ............................ Draw the dialog ............................. + */ + Hide_Mouse(); + + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(buffer, x + 20, y + 25, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + commands->Draw_All(); + Show_Mouse(); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + starttime = lastmsgtime = TickCount.Time(); + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + commands->Draw_All(); + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + retval = false; + process = false; + break; + + default: + break; + } + /*..................................................................... + Service the connection. + .....................................................................*/ + NullModem.Service(); + + /*..................................................................... + Resend our message if it's time + .....................................................................*/ + if (TickCount.Time() - starttime > PACKET_RETRANS_TIME) { + starttime = TickCount.Time(); + SendPacket.Command = SERIAL_CONNECT; + SendPacket.ID = MPlayerLocalID; + //Smart_Printf( "Sending a SERIAL_CONNECT packet !!!!!!!!\n" ); + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 0); + } + + /*..................................................................... + Check for an incoming message + .....................................................................*/ + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + + lastmsgtime = TickCount.Time(); + + if (ReceivePacket.Command == SERIAL_CONNECT) { + //Smart_Printf( "Received a SERIAL_CONNECT packet !!!!!!!!\n" ); + + // are we getting our own packets back?? + + if (ReceivePacket.ID == MPlayerLocalID) { + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + retval = false; + process = false; + break; + } + + /*............................................................... + OK, we got our message; now we have to make certain the other + guy gets his, so send him one with an ACK required. + ...............................................................*/ + SendPacket.Command = SERIAL_CONNECT; + SendPacket.ID = MPlayerLocalID; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < 60) + NullModem.Service(); + retval = true; + process = false; + } + } + + // + // timeout if we do not get any packets + // + if (TickCount.Time() - lastmsgtime > PACKET_CANCEL_TIMEOUT) { + retval = false; + process = false; + } + + } /* end of while */ + + return( retval ); + +} + + +/*********************************************************************************************** + * Destroy_Null_Connection -- destroys the given connection * + * * + * Call this routine when a connection goes bad, or another player signs off. * + * * + * INPUT: * + * id connection ID to destroy * + * error 0 = user signed off; 1 = connection error * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/31/1995 DRD : Created. * + *=============================================================================================*/ +void Destroy_Null_Connection(int id, int error) +{ + int i,j,idx; + HousesType house; + HouseClass *housep; + char txt[80]; + + + if ( MPlayerCount == 1 ) { + return; + } + + // find index for id + + idx = -1; + for (i = 0; i < MPlayerCount; i++) { + if (MPlayerID[i] == (unsigned char)id) { + idx = i; + break; + } + } + + if (idx == -1) { + return; + } + + /*------------------------------------------------------------------------ + Create a message to display to the user + ------------------------------------------------------------------------*/ + txt[0] = '\0'; + if (error == 1) { + sprintf(txt,Text_String(TXT_CONNECTION_LOST), MPlayerNames[idx] ); + } + else if (error == 0) { + sprintf(txt,Text_String(TXT_LEFT_GAME), MPlayerNames[idx] ); + } + else if (error == -1) { + NullModem.Delete_Connection(); + } + + if (strlen(txt)) { + Messages.Add_Message (txt, + MPlayerTColors[MPlayerID_To_ColorIndex((unsigned char)id)], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, 0, 0); + Map.Flag_To_Redraw(false); + } + + for (i = 0; i < MPlayerCount; i++) { + if (MPlayerID[i] == (unsigned char)id) { + /*.................................................................. + Turn the player's house over to the computer's AI + ..................................................................*/ + house = MPlayerHouses[i]; + housep = HouseClass::As_Pointer (house); + housep->IsHuman = false; + + /*.................................................................. + Move arrays back by one + ..................................................................*/ + for (j = i; j < MPlayerCount - 1; j++) { + MPlayerID[j] = MPlayerID[j + 1]; + MPlayerHouses[j] = MPlayerHouses[j + 1]; + strcpy (MPlayerNames[j], MPlayerNames[j+1]); + TheirProcessTime[j] = TheirProcessTime[j+1]; + } + } + } + + MPlayerCount--; + + /*------------------------------------------------------------------------ + If we're the last player left, tell the user. + ------------------------------------------------------------------------*/ + if (MPlayerCount == 1) { + sprintf(txt,"%s",Text_String(TXT_JUST_YOU_AND_ME)); + Messages.Add_Message (txt, + MPlayerTColors[MPlayerID_To_ColorIndex((unsigned char)id)], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, 0, 0); + Map.Flag_To_Redraw(false); + } + +} /* end of Destroy_Null_Connection */ + + +/*************************************************************************** + * Select_Serial_Dialog -- Serial Communications menu dialog * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * GAME_MODEM user wants to play a modem game * + * GAME_NULL_MODEM user wants to play a null-modem game * + * GAME_NORMAL user hit Cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +GameType Select_Serial_Dialog( void ) +{ + int rc; +// int value, i; + int com = -1, baud = -1; + int error = 0; + + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 160 *factor; // dialog width + int d_dialog_h = 94 *factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord +// D_DIALOG_Y = ((200 - D_DIALOG_H) / 2), // dialog y-coord + int d_dialog_y = ((136*factor - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 11*factor; // ht of 6-pt text + int d_margin = 7; // margin width/height + + int d_dial_w = 90 *factor; + int d_dial_h = 9 *factor; + int d_dial_x = d_dialog_cx - d_dial_w / 2; + int d_dial_y = d_dialog_y + d_margin + d_txt6_h + d_margin; + + int d_answer_w = 90 *factor; + int d_answer_h = 9 *factor; + int d_answer_x = d_dialog_cx - d_answer_w / 2; + int d_answer_y = d_dial_y + d_dial_h + 2; + + int d_nullmodem_w = 90 *factor; + int d_nullmodem_h = 9 *factor; + int d_nullmodem_x = d_dialog_cx - d_nullmodem_w / 2; + int d_nullmodem_y = d_answer_y + d_answer_h + 2; + + int d_settings_w = 90 *factor; + int d_settings_h = 9 *factor; + int d_settings_x = d_dialog_cx - d_settings_w / 2; + int d_settings_y = d_nullmodem_y + d_nullmodem_h + 2; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50 *factor; +#else + int d_cancel_w = 40 *factor; +#endif + int d_cancel_h = 9 *factor; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_settings_y + d_settings_h + d_margin; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_DIAL = 100, + BUTTON_ANSWER, + BUTTON_NULLMODEM, + BUTTON_SETTINGS, + BUTTON_CANCEL, + + NUM_OF_BUTTONS = 5, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int tabs[] = {77 * factor}; // tabs for player list box + GameType retval; // return value + + int selection; + bool pressed; + int curbutton; + TextButtonClass *buttons[NUM_OF_BUTTONS]; + + SerialSettingsType *settings; + bool selectsettings = false; + + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + TextButtonClass dialbtn(BUTTON_DIAL, TXT_DIAL_MODEM, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_dial_x, d_dial_y, d_dial_w, d_dial_h); + + TextButtonClass answerbtn(BUTTON_ANSWER, TXT_ANSWER_MODEM, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_answer_x, d_answer_y, d_answer_w, d_answer_h); + + TextButtonClass nullmodembtn(BUTTON_NULLMODEM, TXT_NULL_MODEM, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_nullmodem_x, d_nullmodem_y, d_nullmodem_w, d_nullmodem_h); + + TextButtonClass settingsbtn(BUTTON_SETTINGS, TXT_SETTINGS, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_settings_x, d_settings_y, d_settings_w, d_settings_h); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + /*........................................................................ + Read the CC.INI file to extract default serial settings, scenario numbers + & descriptions, and the phone list. + ........................................................................*/ + Read_MultiPlayer_Settings (); + + if (SerialDefaults.Port == 0 || + SerialDefaults.IRQ == -1 || + SerialDefaults.Baud == -1) { + selectsettings = true; + } else { + if ( NullModem.Detect_Port( &SerialDefaults ) != PORT_VALID ) { + selectsettings = true; + } + } + + /* + ............................ Create the list ............................. + */ + commands = &dialbtn; + answerbtn.Add_Tail(*commands); + nullmodembtn.Add_Tail(*commands); + settingsbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + + /* + ......................... Fill array of button ptrs ...................... + */ + curbutton = 0; + buttons[0] = &dialbtn; + buttons[1] = &answerbtn; + buttons[2] = &nullmodembtn; + buttons[3] = &settingsbtn; + buttons[4] = &cancelbtn; + buttons[curbutton]->Turn_On(); + + Keyboard::Clear(); + + Fancy_Text_Print(TXT_NONE, 0, 0, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + +Debug_Smart_Print = true; + + MPlayerLocalID = 0xff; // set to invalid value + + /* + -------------------------- Main Processing Loop -------------------------- + */ + display = REDRAW_ALL; + process = true; + pressed = false; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + /* + ..................... Refresh the backdrop ...................... + */ + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + /* + ..................... Draw the background ....................... + */ + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /* + ..................... Redraw the buttons ....................... + */ + commands->Draw_All(); + /* + ....................... Draw the labels ......................... + */ + Draw_Caption (TXT_SELECT_SERIAL_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (BUTTON_DIAL | KN_BUTTON): + selection = BUTTON_DIAL; + pressed = true; + break; + + case (BUTTON_ANSWER | KN_BUTTON): + selection = BUTTON_ANSWER; + pressed = true; + break; + + case (BUTTON_NULLMODEM | KN_BUTTON): + selection = BUTTON_NULLMODEM; + pressed = true; + break; + + case (BUTTON_SETTINGS | KN_BUTTON): + selection = BUTTON_SETTINGS; + pressed = true; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + selection = BUTTON_CANCEL; + pressed = true; + break; + + case (KN_UP): + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton--; + if (curbutton < 0) + curbutton = (NUM_OF_BUTTONS - 1); + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case (KN_DOWN): + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton++; + if (curbutton > (NUM_OF_BUTTONS - 1) ) + curbutton = 0; + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_DIAL; + pressed = true; + break; + + default: + break; + } + + if (pressed) { + // + // to make sure the selection is correct in case they used the mouse + // + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton = selection - BUTTON_DIAL; + buttons[curbutton]->Turn_On(); + buttons[curbutton]->IsPressed = true; + buttons[curbutton]->Draw_Me(true); + + switch (selection) { + case (BUTTON_DIAL): + + if (selectsettings) { + CCMessageBox().Process(TXT_SELECT_SETTINGS); + } + + /* + ** Remote-connect + */ + else if ( Phone_Dialog() ) { + if (PhoneBook[CurPhoneIdx]->Settings.Port == 0) { + settings = &SerialDefaults; + } else { + settings = &(PhoneBook[CurPhoneIdx]->Settings); + } + + if (SerialPort){ + delete SerialPort; + } + SerialPort = new WinModemClass; + + if ( Init_Null_Modem( settings ) ) { + + if (settings->CallWaitStringIndex == CALL_WAIT_CUSTOM) { + strcpy( DialString, settings->CallWaitString ); + } else { + strcpy( DialString, + CallWaitStrings[ settings->CallWaitStringIndex ] ); + } + strcat( DialString, PhoneBook[ CurPhoneIdx ]->Number ); + + if ( Dial_Modem( settings, false ) ) { + ModemGameToPlay = MODEM_DIALER; + if ( Com_Scenario_Dialog() ) { + retval = GAME_MODEM; + process = false; + } + } + + if (process) { // restore to default + NullModem.Change_IRQ_Priority( 0 ); + } + } else { + CCMessageBox().Process(TXT_SELECT_SETTINGS); + } + } + + if (process) { + buttons[curbutton]->IsPressed = false; + buttons[curbutton]->Flag_To_Redraw(); + } + + display = REDRAW_ALL; + break; + + case (BUTTON_ANSWER): + + if (selectsettings) { + CCMessageBox().Process(TXT_SELECT_SETTINGS); + } else { + /* + ** Remote-connect + */ + settings = &SerialDefaults; + + if (SerialPort){ + delete SerialPort; + } + SerialPort = new WinModemClass; + + if ( Init_Null_Modem( settings ) ) { + if ( Answer_Modem( settings, false ) ) { + ModemGameToPlay = MODEM_ANSWERER; + if ( Com_Show_Scenario_Dialog() ) { + retval = GAME_MODEM; + process = false; + } + } + + if (process) { // restore to default + NullModem.Change_IRQ_Priority( 0 ); + } + } else { + CCMessageBox().Process(TXT_SELECT_SETTINGS); + } + } + + if (process) { + buttons[curbutton]->IsPressed = false; + buttons[curbutton]->Flag_To_Redraw(); + } + + display = REDRAW_ALL; + break; + + case (BUTTON_NULLMODEM): + + if (selectsettings) { + CCMessageBox().Process(TXT_SELECT_SETTINGS); + } else { + /* + ** Otherwise, remote-connect; save values if we're recording + */ + + if (SerialPort){ + delete SerialPort; + } + SerialPort = new WinNullModemClass; + + if ( Init_Null_Modem( &SerialDefaults ) ) { + rc = Test_Null_Modem(); + switch (rc) { + case (1): + ModemGameToPlay = MODEM_NULL_HOST; + if ( Com_Scenario_Dialog() ) { + retval = GAME_NULL_MODEM; + process = false; + } + break; + + case (2): + ModemGameToPlay = MODEM_NULL_JOIN; + if ( Com_Show_Scenario_Dialog() ) { + retval = GAME_NULL_MODEM; + process = false; + } + break; + + case (3): + CCMessageBox().Process( TXT_MODEM_OR_LOOPBACK ); + break; + } + + if (process) { // restore to default + NullModem.Change_IRQ_Priority( 0 ); + } + } else { + CCMessageBox().Process(TXT_SELECT_SETTINGS); + } + } + + if (process) { + buttons[curbutton]->IsPressed = false; + buttons[curbutton]->Flag_To_Redraw(); + } + + display = REDRAW_ALL; + break; + + case (BUTTON_SETTINGS): + if ( Com_Settings_Dialog( &SerialDefaults ) ) { + Write_MultiPlayer_Settings (); + + selectsettings = true; + + if (SerialDefaults.Port != 0 && + SerialDefaults.IRQ != -1 && + SerialDefaults.Baud != -1) { + if ( NullModem.Detect_Port( &SerialDefaults ) == PORT_VALID) { + selectsettings = false; + } + } + } + + buttons[curbutton]->IsPressed = false; + buttons[curbutton]->Flag_To_Redraw(); + display = REDRAW_ALL; + break; + + case (BUTTON_CANCEL): + retval = GAME_NORMAL; + process = false; + break; + } + + pressed = false; + } + } /* end of while */ + +#if 0 + if (retval == GAME_NORMAL) { + Debug_Smart_Print = false; + } +#endif + +Debug_Smart_Print = false; + + return( retval ); +} + + + + +/*********************************************************************************************** + * Advanced_Modem_Settings -- Allows to user to set additional modem settings * + * * + * * + * * + * INPUT: current settings * + * * + * OUTPUT: modified settings * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/16/96 2:29PM ST : Created * + *=============================================================================================*/ +Advanced_Modem_Settings (SerialSettingsType *settings) +{ + + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 340; // dialog width + int d_dialog_h = 170; // dialog height + int d_dialog_x = 320 - d_dialog_w/2; // dialog x-coord + int d_dialog_y = 200 - d_dialog_h/ 2; // dialog y-coord + + + int d_compression_w = 50; + int d_compression_h = 18; + int d_compression_x = d_dialog_x + d_dialog_w/2 +40; + int d_compression_y = d_dialog_y + 30; + + int d_errorcorrection_w = 50; + int d_errorcorrection_h = 18; + int d_errorcorrection_x = d_dialog_x + d_dialog_w/2 +40; + int d_errorcorrection_y = d_dialog_y + 52; + + int d_hardwareflowcontrol_w = 50; + int d_hardwareflowcontrol_h = 18; + int d_hardwareflowcontrol_x = d_dialog_x + d_dialog_w/2 +40; + int d_hardwareflowcontrol_y = d_dialog_y + 74; + + int d_default_w = 100; + int d_default_h = 18; + int d_default_x = d_dialog_x + d_dialog_w / 2 - d_default_w / 2; + int d_default_y = d_dialog_y + 110; + + int d_ok_w = 100; + int d_ok_h = 18; + int d_ok_x = d_dialog_x + d_dialog_w/2 - d_ok_w / 2; + int d_ok_y = d_dialog_y + d_dialog_h - 24; + + enum { + BUTTON_COMPRESSION = 100, + BUTTON_ERROR_CORRECTION, + BUTTON_HARDWARE_FLOW_CONTROL, + BUTTON_DEFAULT, + BUTTON_OK, + }; + + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND, + } RedrawType; + + /* + ** Yes/No strings + */ + char compress_text [16]; + char correction_text [16]; + char flowcontrol_text[16]; + + + /* + ** Initialise the button text + */ + strcpy (compress_text, settings->Compression ? Text_String (TXT_ON) : Text_String (TXT_OFF) ); + strcpy (correction_text, settings->ErrorCorrection ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + strcpy (flowcontrol_text, settings->HardwareFlowControl ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + + + /* + ** Create the buttons + */ + TextButtonClass compressionbutton(BUTTON_COMPRESSION, compress_text, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_compression_x, d_compression_y, d_compression_w, d_compression_h); + + TextButtonClass errorcorrectionbutton(BUTTON_ERROR_CORRECTION, correction_text, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_errorcorrection_x, d_errorcorrection_y, d_errorcorrection_w, d_errorcorrection_h); + + TextButtonClass hardwareflowcontrolbutton(BUTTON_HARDWARE_FLOW_CONTROL, flowcontrol_text, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_hardwareflowcontrol_x, d_hardwareflowcontrol_y, d_hardwareflowcontrol_w, d_hardwareflowcontrol_h); + + TextButtonClass defaultbutton(BUTTON_DEFAULT, TXT_DEFAULT, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_default_x, d_default_y, d_default_w, d_default_h); + + TextButtonClass okbutton(BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_ok_x, d_ok_y, d_ok_w, d_ok_h); + + + /* + ** Misc. variables. + */ + RedrawType display = REDRAW_ALL; // redraw level + BOOL process = true; // process while true + KeyNumType input; + GadgetClass *commands; // button list + + + commands = &okbutton; + defaultbutton.Add_Tail(*commands); + compressionbutton.Add_Tail(*commands); + errorcorrectionbutton.Add_Tail(*commands); + hardwareflowcontrolbutton.Add_Tail(*commands); + + + /* + ** Main process loop + */ + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_MODEM_INITIALISATION, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print( TXT_DATA_COMPRESSION, + d_compression_x - 26, d_compression_y + 2, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print( TXT_ERROR_CORRECTION, + d_errorcorrection_x - 26, d_errorcorrection_y +2, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print( TXT_HARDWARE_FLOW_CONTROL, + d_hardwareflowcontrol_x -26, d_hardwareflowcontrol_y +2, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + compressionbutton.Flag_To_Redraw(); + errorcorrectionbutton.Flag_To_Redraw(); + hardwareflowcontrolbutton.Flag_To_Redraw(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + case (BUTTON_COMPRESSION | KN_BUTTON): + settings->Compression = settings->Compression ^ 1; + strcpy (compress_text, settings->Compression ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + break; + + case (BUTTON_ERROR_CORRECTION | KN_BUTTON): + settings->ErrorCorrection = settings->ErrorCorrection ^ 1; + strcpy (correction_text, settings->ErrorCorrection ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + break; + + case (BUTTON_HARDWARE_FLOW_CONTROL | KN_BUTTON): + settings->HardwareFlowControl = settings->HardwareFlowControl ^ 1; + strcpy (flowcontrol_text, settings->HardwareFlowControl ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + break; + + case (BUTTON_DEFAULT | KN_BUTTON): + settings->Compression = false; + settings->ErrorCorrection = false; + settings->HardwareFlowControl = true; + + strcpy (compress_text, settings->Compression ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + + strcpy (correction_text, settings->ErrorCorrection ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + + strcpy (flowcontrol_text, settings->HardwareFlowControl ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + + if (display < REDRAW_BUTTONS) display = REDRAW_BUTTONS; + break; + + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + } + } +} + + + + +/*************************************************************************** + * Com_Settings_Dialog -- Lets user select serial port settings * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Settings ³ * + * ³ ³ * + * ³ Port:____ IRQ:__ Baud:______ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ³ * + * ³ Initialization: [Add] [Delete] ³ * + * ³ _____________________________ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ³ * + * ³ Call Waiting: ³ * + * ³ _______________ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ [Tone Dialing] ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ [Pulse Dialing] ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * settings ptr to SerialSettingsType structure * + * * + * OUTPUT: * + * true = OK, false = Cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static int Com_Settings_Dialog( SerialSettingsType *settings ) +{ +/* ###Change collision detected! C:\PROJECTS\CODE\NULLDLG.CPP... */ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 301 *factor; // dialog width + int d_dialog_h = 200 *factor; // dialog height + int d_dialog_x = ((320 *factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200 *factor - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6 *factor +1; // ht of 6-pt text + int d_margin = 5 *factor; // margin width/height + +#ifdef EDIT_IRQ + int d_portlist_w = 80 *factor; + int d_portlist_h = 35 *factor; + int d_portlist_x = d_dialog_x + (d_dialog_w / 6) - (d_portlist_w / 2); + int d_portlist_y = d_dialog_y + ((d_margin + d_txt6_h) * 2) + d_margin + 10 *factor; + + int d_port_w = ((PORTBUF_MAX - 1) * 6 *factor) + 4 *factor; + int d_port_h = 9 *factor; + int d_port_x = d_portlist_x + 31 *factor; + int d_port_y = d_portlist_y - d_margin - d_txt6_h; + + + int d_irqlist_w = 80 *factor; + int d_irqlist_h = 35 *factor; + int d_irqlist_x = d_dialog_x + (d_dialog_w / 2) - (d_irqlist_w / 2); + int d_irqlist_y = d_portlist_y; + + int d_irq_w = ((IRQBUF_MAX - 1) * 6 *factor) + 3 *factor; + int d_irq_h = 9 *factor; + int d_irq_x = d_irqlist_x + 25 *factor; + int d_irq_y = d_irqlist_y - d_margin - d_txt6_h; + + int d_baudlist_w = 80 *factor; + int d_baudlist_h = 35 *factor; + int d_baudlist_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_baudlist_w / 2); + int d_baudlist_y = d_portlist_y; + + int d_baud_w = ((BAUDBUF_MAX - 1) * 6 *factor) + 3 *factor; + int d_baud_h = 9 *factor; + int d_baud_x = d_baudlist_x + 31 *factor; + int d_baud_y = d_baudlist_y - d_margin - d_txt6_h; + +#endif //EDIT_IRQ + int d_initstrlist_w = ((INITSTRBUF_MAX - 1) * 6 *factor) + 8 + 3 *factor; + int d_initstrlist_h = 21 *factor; + int d_initstrlist_x = d_dialog_cx - (d_initstrlist_w / 2); + int d_initstrlist_y = d_dialog_y + ((d_margin + d_txt6_h) * 2) + d_margin + 10 *factor + + 35*factor + + ((d_margin + d_txt6_h) * 2) + d_margin + 4 *factor; + + int d_initstr_w = ((INITSTRBUF_MAX - 1) * 6 *factor) + 3 *factor; + int d_initstr_h = 9 *factor; + int d_initstr_x = d_initstrlist_x; + int d_initstr_y = d_initstrlist_y - d_margin - d_txt6_h; + +#ifndef EDIT_IRQ + int d_portlist_w = 80 *factor + 80; + int d_portlist_h = 35 *factor; + int d_portlist_x = d_initstrlist_x; + int d_portlist_y = d_dialog_y + ((d_margin + d_txt6_h) * 2) + d_margin + 10 *factor; + + int d_port_w = d_portlist_w; + int d_port_h = 9 *factor; + int d_port_x = d_portlist_x; // + 31 *factor; + int d_port_y = d_portlist_y - d_margin - d_txt6_h; + + int d_baudlist_w = 80 *factor; + int d_baudlist_h = 35 *factor; + int d_baudlist_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_baudlist_w / 2); + d_baudlist_x -= 32; + //int d_baudlist_x = d_portlist_x + d_portlist_w + 20 * factor; + int d_baudlist_y = d_portlist_y; + + int d_baud_w = ((BAUDBUF_MAX - 1) * 6 *factor) + 3 *factor; + int d_baud_h = 9 *factor; + int d_baud_x = d_baudlist_x + 31 *factor; + int d_baud_y = d_baudlist_y - d_margin - d_txt6_h; + + int d_inittype_w = 30*factor; + int d_inittype_h = 9*factor; + int d_inittype_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_inittype_w / 2); + int d_inittype_y = d_baud_y + 20*factor; + +#endif //EDIT_IRQ + + int d_add_w = 45 *factor; + int d_add_h = 9 *factor; +#ifdef FRENCH + int d_add_x = (d_dialog_cx - (d_add_w / 2))+34*factor; +#else + int d_add_x = d_dialog_cx - (d_add_w / 2); +#endif + int d_add_y = d_initstr_y - d_add_h - 3*factor; + + int d_delete_w = 45 *factor; + int d_delete_h = 9 *factor; + +#ifdef FRENCH + int d_delete_x = 14*factor + d_dialog_x + ((d_dialog_w * 3) / 4) - (d_delete_w / 2); +#else + int d_delete_x = d_dialog_x + ((d_dialog_w * 3) / 4) - (d_delete_w / 2); +#endif + int d_delete_y = d_initstr_y - d_add_h - 3 *factor; + + int d_cwaitstrlist_w = (((CWAITSTRBUF_MAX - 1) + 9) * 6 *factor) + 3 *factor; + int d_cwaitstrlist_h = 27 *factor; + int d_cwaitstrlist_x = d_initstrlist_x; + int d_cwaitstrlist_y = d_initstrlist_y + d_initstrlist_h + ((d_margin + d_txt6_h) * 2) + 2 *factor; + + int d_cwaitstr_w = ((CWAITSTRBUF_MAX - 1) * 6 *factor) + 3 *factor; + int d_cwaitstr_h = 9 *factor; + int d_cwaitstr_x = d_cwaitstrlist_x; + int d_cwaitstr_y = d_cwaitstrlist_y - d_margin - d_txt6_h; + + int d_tone_w = 80 *factor; + int d_tone_h = 9 *factor; + int d_tone_x = d_dialog_x + ((d_dialog_w * 3) / 4) - (d_tone_w / 2); + int d_tone_y = d_cwaitstrlist_y; + + int d_pulse_w = 80 *factor; + int d_pulse_h = 9 *factor; + int d_pulse_x = d_dialog_x + ((d_dialog_w * 3) / 4) - (d_pulse_w / 2); + int d_pulse_y = d_tone_y + d_tone_h + d_margin; + + int d_save_w = 40 *factor; + int d_save_h = 9 *factor; + int d_save_x = d_dialog_x + (d_dialog_w / 5) - (d_save_w / 2); + int d_save_y = d_dialog_y + d_dialog_h - d_save_h - d_margin - 2 *factor; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50 *factor; +#else + int d_cancel_w = 40 *factor; +#endif + int d_cancel_h = 9 *factor; + int d_cancel_x = d_dialog_x + ((d_dialog_w * 4) / 5) - (d_cancel_w / 2); + int d_cancel_y = d_dialog_y + d_dialog_h - d_cancel_h - d_margin - 2 *factor; + +#if (GERMAN | FRENCH) + int d_advanced_w = 50*factor; +#else + int d_advanced_w = 40*factor; +#endif + int d_advanced_h = 9*factor; + int d_advanced_x = d_dialog_x + ((d_dialog_w) / 2) - (d_advanced_w / 2); + int d_advanced_y = d_dialog_y + d_dialog_h - d_advanced_h - d_margin - 2 *factor; + + + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_PORT = 100, + BUTTON_PORTLIST, + BUTTON_IRQ, + BUTTON_IRQLIST, + BUTTON_BAUD, + BUTTON_BAUDLIST, + BUTTON_INITSTR, + BUTTON_INITSTRLIST, + BUTTON_ADD, + BUTTON_DELETE, + BUTTON_CWAITSTR, + BUTTON_CWAITSTRLIST, + BUTTON_TONE, + BUTTON_PULSE, + BUTTON_SAVE, + BUTTON_ADVANCED, + BUTTON_INITTYPE, + BUTTON_CANCEL, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + static char *portname[4] = { + "COM1 - 3F8", + "COM2 - 2F8", + "COM3 - 3E8", + "COM4 - 2E8" + }; + + static char custom_port[10 + MODEM_NAME_MAX] = {"CUSTOM - ????"}; + +#ifdef EDIT_IRQ + static char *irqname[5] = { + "2 / 9", + "3 - [COM2 & 4]", + "4 - [COM1 & 3]", + "5", + "CUSTOM - ??" + }; + + static int _irqidx[4] = { + 2, + 1, + 2, + 1 + }; +#endif // EDIT_IRQ + + static char modemnames[10][MODEM_NAME_MAX]; + + static char *baudname[5] = { + "14400", + "19200", + "28800", + "38400", + "57600", + }; + + static char *init_types[2] = { + "Normal", + "Full", + }; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + BOOL process = true; // process while true + KeyNumType input; + char * item; // general-purpose string + char * temp; // general-purpose string + + char portbuf[ PORTBUF_MAX ] = {0}; // buffer for port +#ifdef EDIT_IRQ + char irqbuf[ IRQBUF_MAX ] = {0}; // buffer for irq +#endif //EDIT_IRQ + char baudbuf[ BAUDBUF_MAX ] = {0}; // buffer for baud + char initstrbuf[ INITSTRBUF_MAX ] = {0}; // buffer for init string + char cwaitstrbuf[ CWAITSTRBUF_MAX ] = {0}; // buffer for call waiting string + + int port_index = 1; // index of currently-selected port (default = com2) + int port_custom_index = 4; //index of custom entry in port list +#ifdef EDIT_IRQ + int irq_index = 1; // index of currently-selected irq (default = 3) +#endif //EDIT_IRQ + int baud_index = 1; // index of currently-selected baud (default = 19200) + int initstr_index = 0; // index of currently-selected modem init (default = "ATZ") + int cwaitstr_index = CALL_WAIT_CUSTOM; // index of currently-selected call waiting (default = "") + int rc = 0; // -1 = user cancelled, 1 = New + int i; // loop counter + int pos; + int len; + int firsttime = 1; + SerialSettingsType tempsettings; + DetectPortType dpstatus; + char init_text[32]; + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + EditClass port_edt (BUTTON_PORT, + portbuf, PORTBUF_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_port_x, d_port_y, d_port_w, d_port_h, EditClass::ALPHANUMERIC); + + ListClass portlist(BUTTON_PORTLIST, + d_portlist_x, d_portlist_y, d_portlist_w, d_portlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + +#ifdef EDIT_IRQ + EditClass irq_edt (BUTTON_IRQ, + irqbuf, IRQBUF_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_irq_x, d_irq_y, d_irq_w, d_irq_h, EditClass::NUMERIC); + + ListClass irqlist(BUTTON_IRQLIST, + d_irqlist_x, d_irqlist_y, d_irqlist_w, d_irqlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); +#endif //EDIT_IRQ + + EditClass baud_edt (BUTTON_BAUD, + baudbuf, BAUDBUF_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_baud_x, d_baud_y, d_baud_w, d_baud_h, EditClass::NUMERIC); + + ListClass baudlist(BUTTON_BAUDLIST, + d_baudlist_x, d_baudlist_y, d_baudlist_w, d_baudlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + EditClass initstr_edt (BUTTON_INITSTR, + initstrbuf, INITSTRBUF_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_initstr_x, d_initstr_y, d_initstr_w, d_initstr_h, EditClass::ALPHANUMERIC); + + ListClass initstrlist(BUTTON_INITSTRLIST, + d_initstrlist_x, d_initstrlist_y, d_initstrlist_w, d_initstrlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + TextButtonClass addbtn(BUTTON_ADD, TXT_ADD, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#ifdef FRENCH +// d_add_x, d_add_y); +//#else + d_add_x, d_add_y, d_add_w, d_add_h); +//#endif + + TextButtonClass deletebtn(BUTTON_DELETE, TXT_DELETE_BUTTON, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#ifdef FRENCH +// d_delete_x, d_delete_y); +//#else + d_delete_x, d_delete_y, d_delete_w, d_delete_h); +//#endif + + EditClass cwaitstr_edt (BUTTON_CWAITSTR, + cwaitstrbuf, CWAITSTRBUF_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_cwaitstr_x, d_cwaitstr_y, d_cwaitstr_w, d_cwaitstr_h, EditClass::ALPHANUMERIC); + + ListClass cwaitstrlist(BUTTON_CWAITSTRLIST, + d_cwaitstrlist_x, d_cwaitstrlist_y, d_cwaitstrlist_w, d_cwaitstrlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + TextButtonClass tonebtn(BUTTON_TONE, TXT_TONE_BUTTON, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_tone_x, d_tone_y, d_tone_w, d_tone_h); + + TextButtonClass pulsebtn(BUTTON_PULSE, TXT_PULSE_BUTTON, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_pulse_x, d_pulse_y, d_pulse_w, d_pulse_h); + + TextButtonClass savebtn(BUTTON_SAVE, TXT_SAVE_BUTTON, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_save_x, d_save_y); +//#else + d_save_x, d_save_y, d_save_w, d_save_h); +//#endif + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif +#if (0) + TextButtonClass inittypebutton(BUTTON_INITTYPE, init_text, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_inittype_x, d_inittype_y, d_inittype_w, d_inittype_h); +#endif //(0) + + TextButtonClass advancedbutton(BUTTON_ADVANCED, TXT_ADVANCED, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_advanced_x, d_advanced_y, d_advanced_w, d_advanced_h); + + /* + ----------------------------- Various Inits ------------------------------ + */ + memcpy( &tempsettings, settings, sizeof(SerialSettingsType) ); + + strcpy (init_text, init_types[tempsettings.Init]); + + if (tempsettings.Port == 0) { + tempsettings.Port = 0x2f8; + } + + if (tempsettings.IRQ == -1) { + tempsettings.IRQ = 3; + } + + if (tempsettings.Baud == -1) { + tempsettings.Baud = 19200; + } + + /*........................................................................ + Set the current indices + ........................................................................*/ + +#ifdef EDIT_IRQ + switch ( tempsettings.IRQ ) { + case ( 2 ): + irq_index = 0; + strcpy (irqbuf, "2"); + break; + + case ( 3 ): + irq_index = 1; + strcpy (irqbuf, "3"); + break; + + case ( 4 ): + irq_index = 2; + strcpy (irqbuf, "4"); + break; + + case ( 5 ): + irq_index = 3; + strcpy (irqbuf, "5"); + break; + + default: + irq_index = 4; + sprintf (irqbuf, "%d", tempsettings.IRQ); + temp = strchr( irqname[4], '-' ); + if ( temp ) { + pos = (int)(temp - irqname[4]) + 2; + len = strlen( irqbuf ); + strncpy( irqname[4] + pos, irqbuf, len ); + *(irqname[4] + pos + len) = 0; + } + break; + } +#endif //EDIT_IRQ + + if (tempsettings.Baud == 14400) { + baud_index = 0; + } else { + if (tempsettings.Baud == 19200) { + baud_index = 1; + } else { + if (tempsettings.Baud == 28800) { + baud_index = 2; + } else { + if (tempsettings.Baud == 38400) { + baud_index = 3; + } else { + baud_index = 4; + } + } + } + } + + sprintf (baudbuf, "%d", tempsettings.Baud); + + /*........................................................................ + Set up the port list box & edit box + ........................................................................*/ + for (i = 0; i < 4; i++) { + portlist.Add_Item( portname[ i ] ); + } + + /* + ** Loop through the first 10 possible modem entries in the registry. Frankly, its just + ** tough luck if the user has more than 10 modems attached! + */ + if (ModemRegistry) { + delete ModemRegistry; + } + int modems_found = 0; + for (i=0 ; i<10 ; i++) { + ModemRegistry = new ModemRegistryEntryClass (i); + if (ModemRegistry->Get_Modem_Name()) { + strncpy (modemnames[modems_found], ModemRegistry->Get_Modem_Name(), MODEM_NAME_MAX); + portlist.Add_Item( modemnames [modems_found++] ); + port_custom_index ++; + } + delete ModemRegistry; + } + ModemRegistry = NULL; + + portlist.Add_Item ( custom_port ); + + + /* + ** Work out the current port index + */ + port_index = -1; + + if (tempsettings.ModemName[0]) { + for ( i=0 ; i= REDRAW_BACKGROUND) { + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_SETTINGS, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print( TXT_PORT_COLON, + d_port_x - 3, d_port_y + 1 *factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + +#ifdef EDIT_IRQ + Fancy_Text_Print( TXT_IRQ_COLON, + d_irq_x - 3, d_irq_y + 1 *factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); +#endif //EDIT_IRQ + + Fancy_Text_Print( TXT_BAUD_COLON, + d_baud_x - 3, d_baud_y + 1 *factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print( TXT_INIT_STRING, + d_initstr_x, d_initstr_y - d_txt6_h - 3 *factor, + CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print( TXT_CWAIT_STRING, + d_cwaitstr_x, d_cwaitstr_y - d_txt6_h - 3 *factor, + CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); +#if (0) + Fancy_Text_Print ( "Modem Init", + d_inittype_x, d_inittype_y - d_txt6_h - d_margin, + CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); +#endif //(0) + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + cancelbtn.Flag_To_Redraw(); + port_edt.Flag_To_Redraw(); + portlist.Flag_To_Redraw(); +#ifdef EDIT_IRQ + irq_edt.Flag_To_Redraw(); + irqlist.Flag_To_Redraw(); +#endif // EDIT_IRQ + baud_edt.Flag_To_Redraw(); + baudlist.Flag_To_Redraw(); + //inittypebutton.Flag_To_Redraw(); + advancedbutton.Flag_To_Redraw(); + initstr_edt.Flag_To_Redraw(); + initstrlist.Flag_To_Redraw(); + addbtn.Flag_To_Redraw(); + deletebtn.Flag_To_Redraw(); + cwaitstr_edt.Flag_To_Redraw(); + cwaitstrlist.Flag_To_Redraw(); + tonebtn.Flag_To_Redraw(); + pulsebtn.Flag_To_Redraw(); + savebtn.Flag_To_Redraw(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + if ( firsttime ) { + port_edt.Set_Focus(); + port_edt.Flag_To_Redraw(); + input = commands->Input(); + firsttime = 0; + } + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { +#if (0) + case (BUTTON_INITTYPE | KN_BUTTON): + tempsettings.Init = !tempsettings.Init; + strcpy (init_text, init_types[tempsettings.Init]); + inittypebutton.Flag_To_Redraw(); + break; + + +#endif //(0) + + case (BUTTON_ADVANCED | KN_BUTTON): + Advanced_Modem_Settings (&tempsettings); + display = REDRAW_ALL; + break; + + + case (BUTTON_PORT | KN_BUTTON): + item = (char *)portlist.Current_Item(); + if (port_index < 4) { + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( portbuf, item, PORTBUF_MAX ); + } else { + pos = (int)(temp - item); + strncpy( portbuf, item, pos ); + portbuf[pos] = 0; + } + port_edt.Set_Text( portbuf, PORTBUF_MAX ); + port_edt.Flag_To_Redraw(); +#ifdef EDIT_IRQ + irq_edt.Set_Focus(); + irq_edt.Flag_To_Redraw(); +#endif //EDIT_IRQ + } else { + strupr( portbuf ); + if ( stricmp(portbuf, "3F8") == 0 ) { + port_index = 0; + portlist.Set_Selected_Index( port_index ); + strcpy (portbuf, "COM1"); + display = REDRAW_BUTTONS; + } + else if ( stricmp(portbuf, "2F8") == 0 ) { + port_index = 1; + portlist.Set_Selected_Index( port_index ); + strcpy (portbuf, "COM2"); + display = REDRAW_BUTTONS; + } + else if ( stricmp(portbuf, "3E8") == 0 ) { + port_index = 2; + portlist.Set_Selected_Index( port_index ); + strcpy (portbuf, "COM3"); + display = REDRAW_BUTTONS; + } + else if ( stricmp(portbuf, "2E8") == 0 ) { + port_index = 3; + portlist.Set_Selected_Index( port_index ); + strcpy (portbuf, "COM4"); + display = REDRAW_BUTTONS; + } + else if ( strncmp(portbuf, "COM", 3) == 0 ) { + display = REDRAW_BUTTONS; + + switch ( (portbuf[3] - '0') ) { + case 1: + port_index = 0; + break; + + case 2: + port_index = 1; + break; + + case 3: + port_index = 2; + break; + + case 4: + port_index = 3; + break; + + default: + if (portbuf[3] <= '9' && portbuf[3] >'0') { + portbuf[4] = 0; + port_index = port_custom_index; + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + len = strlen( portbuf ); + strncpy( item + pos, portbuf, len ); + *(item + pos + len) = 0; + display = REDRAW_BUTTONS; + } + break; + } + CCMessageBox().Process( TXT_INVALID_PORT_ADDRESS ); + port_edt.Set_Focus(); + display = REDRAW_ALL; + break; + } + + portlist.Set_Selected_Index( port_index ); + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + len = strlen( portbuf ); + strncpy( item + pos, portbuf, len ); + *(item + pos + len) = 0; + display = REDRAW_BUTTONS; + } + } + +#ifdef EDIT_IRQ + if (display == REDRAW_BUTTONS) { + irq_edt.Set_Focus(); + irq_edt.Flag_To_Redraw(); + } +#endif //EDIT_IRQ + } + break; + + case (BUTTON_PORTLIST | KN_BUTTON): + if (portlist.Current_Index() != port_index) { + port_index = portlist.Current_Index(); + item = (char *)portlist.Current_Item(); + if (port_index < 4) { + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( portbuf, item, PORTBUF_MAX ); + } else { + pos = (int)(temp - item); + strncpy( portbuf, item, pos ); + portbuf[pos] = 0; + } + port_edt.Clear_Focus(); + + // auto select the irq for port + +#ifdef EDIT_IRQ + irq_index = _irqidx[ port_index ]; + irqlist.Set_Selected_Index( irq_index ); + item = (char *)irqlist.Current_Item(); + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( irqbuf, item, 2 ); + } else { + pos = (int)(temp - item); + strncpy( irqbuf, item, pos ); + irqbuf[pos] = 0; + } + irq_edt.Clear_Focus(); +#endif //EDIT_IRQ + } else { + if (port_index == port_custom_index) { + /* + ** This is the custom entry + */ + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + if ( *(item + pos) == '?' ) { + portbuf[0] = 0; + } else { + strncpy( portbuf, item + pos, PORTBUF_MAX ); + } + } + port_edt.Set_Focus(); + }else{ + /* + ** Must be a modem name entry so just copy iy + */ + strncpy (portbuf, item, PORTBUF_MAX); + } + + } + port_edt.Set_Text( portbuf, PORTBUF_MAX ); + display = REDRAW_BUTTONS; + } else { + if (port_index < port_custom_index) { + port_edt.Clear_Focus(); + } else { + port_edt.Set_Focus(); + } + display = REDRAW_BUTTONS; + } + break; + +#ifdef EDIT_IRQ + case (BUTTON_IRQ | KN_BUTTON): + item = (char *)irqlist.Current_Item(); + if (irq_index < 4) { + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( irqbuf, item, IRQBUF_MAX ); + } else { + pos = (int)(temp - item); + strncpy( irqbuf, item, pos ); + irqbuf[pos] = 0; + } + irq_edt.Set_Text( irqbuf, IRQBUF_MAX ); + irq_edt.Flag_To_Redraw(); + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + len = strlen( irqbuf ); + strncpy( item + pos, irqbuf, len ); + *(item + pos + len) = 0; + display = REDRAW_BUTTONS; + } + } + baud_edt.Set_Focus(); + baud_edt.Flag_To_Redraw(); + break; + + case (BUTTON_IRQLIST | KN_BUTTON): + if (irqlist.Current_Index() != irq_index) { + irq_index = irqlist.Current_Index(); + item = (char *)irqlist.Current_Item(); + if (irq_index < 4) { + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( irqbuf, item, IRQBUF_MAX ); + } else { + pos = (int)(temp - item); + strncpy( irqbuf, item, pos ); + irqbuf[pos] = 0; + } + irq_edt.Clear_Focus(); + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + if ( *(item + pos) == '?' ) { + irqbuf[0] = 0; + } else { + strncpy( irqbuf, item + pos, IRQBUF_MAX ); + } + } + irq_edt.Set_Focus(); + } + irq_edt.Set_Text( irqbuf, IRQBUF_MAX ); + } else { + if (irq_index < 4) { + irq_edt.Clear_Focus(); + } else { + irq_edt.Set_Focus(); + } + } + display = REDRAW_BUTTONS; + break; +#endif //EDIT_IRQ + + case (BUTTON_BAUD | KN_BUTTON): + item = (char *)baudlist.Current_Item(); + strncpy( baudbuf, item, BAUDBUF_MAX ); + baud_edt.Set_Text( baudbuf, BAUDBUF_MAX ); + initstr_edt.Set_Focus(); + initstr_edt.Flag_To_Redraw(); + display = REDRAW_BUTTONS; + break; + + case (BUTTON_BAUDLIST | KN_BUTTON): + if (baudlist.Current_Index() != baud_index) { + baud_index = baudlist.Current_Index(); + item = (char *)baudlist.Current_Item(); + strncpy( baudbuf, item, BAUDBUF_MAX ); + baud_edt.Set_Text( baudbuf, BAUDBUF_MAX ); + baud_edt.Clear_Focus(); + display = REDRAW_BUTTONS; + } + break; + +#if 0 + case (BUTTON_INITSTR | KN_BUTTON): + strupr( initstrbuf ); + strncpy( InitStrings[ initstr_index ], initstrbuf, INITSTRBUF_MAX ); + Build_Init_String_Listbox(&initstrlist, &initstr_edt, initstrbuf, + &initstr_index); + cwaitstr_edt.Set_Focus(); + cwaitstr_edt.Flag_To_Redraw(); + display = REDRAW_BUTTONS; + break; +#endif + + case (BUTTON_INITSTRLIST | KN_BUTTON): + if (initstrlist.Current_Index() != initstr_index) { + initstr_index = initstrlist.Current_Index(); + item = (char *)initstrlist.Current_Item(); + strncpy( initstrbuf, item, INITSTRBUF_MAX ); + initstr_edt.Set_Text( initstrbuf, INITSTRBUF_MAX ); + } + initstr_edt.Set_Focus(); + initstr_edt.Flag_To_Redraw(); + display = REDRAW_BUTTONS; + break; + + /*------------------------------------------------------------------ + Add a new InitString entry + ------------------------------------------------------------------*/ + case (BUTTON_ADD | KN_BUTTON): + + item = new char[ INITSTRBUF_MAX ]; + memset (item, 0, INITSTRBUF_MAX); + + strupr ( initstrbuf ); + strncpy ( item, initstrbuf, INITSTRBUF_MAX-1 ); + + InitStrings.Add ( item ); + Build_Init_String_Listbox (&initstrlist, &initstr_edt, initstrbuf, + &initstr_index); + /*............................................................ + Set the current listbox index to the newly-added item. + ............................................................*/ + for (i = 0; i < InitStrings.Count(); i++) { + if (item == InitStrings[i]) { + initstr_index = i; + strcpy( initstrbuf, InitStrings[ initstr_index ] ); + initstr_edt.Set_Text( initstrbuf, INITSTRBUF_MAX ); + initstrlist.Set_Selected_Index( initstr_index ); + } + } + initstr_edt.Set_Focus(); + initstr_edt.Flag_To_Redraw(); + display = REDRAW_BUTTONS; + break; + + /*------------------------------------------------------------------ + Delete the current InitString entry + ------------------------------------------------------------------*/ + case (BUTTON_DELETE | KN_BUTTON): + + if ( InitStrings.Count() && initstr_index != -1) { + InitStrings.Delete( initstr_index ); + Build_Init_String_Listbox(&initstrlist, &initstr_edt, initstrbuf, + &initstr_index); + } + break; + + case (BUTTON_CWAITSTR | KN_BUTTON): + item = (char *)cwaitstrlist.Current_Item(); + if (cwaitstr_index < 3) { + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + len = strlen( cwaitstrbuf ); + strncpy( item + pos, cwaitstrbuf, len ); + *(item + pos + len) = 0; + display = REDRAW_BUTTONS; + } + } + break; + + case (BUTTON_CWAITSTRLIST | KN_BUTTON): + if (cwaitstrlist.Current_Index() != cwaitstr_index) { + cwaitstr_index = cwaitstrlist.Current_Index(); + item = (char *)cwaitstrlist.Current_Item(); + if (cwaitstr_index < 3) { + strncpy( cwaitstrbuf, item, CWAITSTRBUF_MAX ); + cwaitstr_edt.Clear_Focus(); + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + strncpy( cwaitstrbuf, item + pos, CWAITSTRBUF_MAX ); + } + cwaitstr_edt.Set_Focus(); + } + cwaitstr_edt.Set_Text( cwaitstrbuf, CWAITSTRBUF_MAX ); + } else { + if (cwaitstr_index < 3) { + cwaitstr_edt.Clear_Focus(); + } else { + cwaitstr_edt.Set_Focus(); + } + } + display = REDRAW_BUTTONS; + break; + + case (BUTTON_TONE | KN_BUTTON): + tempsettings.DialMethod = DIAL_TOUCH_TONE; + tonebtn.Turn_On(); + pulsebtn.Turn_Off(); + break; + + case (BUTTON_PULSE | KN_BUTTON): + tempsettings.DialMethod = DIAL_PULSE; + tonebtn.Turn_Off(); + pulsebtn.Turn_On(); + break; + + /*------------------------------------------------------------------ + SAVE: save the com settings + ------------------------------------------------------------------*/ + case (KN_RETURN): + case (BUTTON_SAVE | KN_BUTTON): + switch (port_index) { + case ( 0 ): + tempsettings.Port = 0x3f8; + tempsettings.ModemName[0] = 0; + break; + + case ( 1 ): + tempsettings.Port = 0x2f8; + tempsettings.ModemName[0] = 0; + break; + + case ( 2 ): + tempsettings.Port = 0x3e8; + tempsettings.ModemName[0] = 0; + break; + + case ( 3 ): + tempsettings.Port = 0x2e8; + tempsettings.ModemName[0] = 0; + break; + + default: + if (port_index == port_custom_index) { + strncpy ( tempsettings.ModemName, portbuf, MODEM_NAME_MAX ); + tempsettings.Port = 1; + } else { + /* + ** Must be a modem name index + */ + strcpy (tempsettings.ModemName, portlist.Current_Item()); + tempsettings.Port = 1; + } + break; + } + +#ifdef EDIT_IRQ + switch (irq_index) { + case ( 0 ): + tempsettings.IRQ = 2; + break; + + case ( 1 ): + tempsettings.IRQ = 3; + break; + + case ( 2 ): + tempsettings.IRQ = 4; + break; + + case ( 3 ): + tempsettings.IRQ = 5; + break; + + default: + sscanf( irqbuf, "%d", &tempsettings.IRQ ); + break; + } +#endif //EDIT_IRQ + + sscanf( baudbuf, "%d", &tempsettings.Baud ); + + tempsettings.InitStringIndex = initstr_index; + tempsettings.CallWaitStringIndex = cwaitstr_index; + + item = CallWaitStrings[ CALL_WAIT_CUSTOM ]; + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + strncpy( cwaitstrbuf, item + pos, CWAITSTRBUF_MAX ); + } else { + cwaitstrbuf[ 0 ] = 0; + } + + strncpy( tempsettings.CallWaitString, cwaitstrbuf, CWAITSTRBUF_MAX ); + + dpstatus = NullModem.Detect_Port( &tempsettings ); + + if (dpstatus == PORT_VALID) { + process = false; + rc = true; + } + else if (dpstatus == PORT_INVALID) { + CCMessageBox().Process( TXT_INVALID_SETTINGS ); + firsttime = 1; + display = REDRAW_ALL; + } + else if (dpstatus == PORT_IRQ_INUSE) { + CCMessageBox().Process( TXT_IRQ_ALREADY_IN_USE ); + firsttime = 1; + display = REDRAW_ALL; + } + break; + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF, bail out with error code + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + } + + } /* end of while */ + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Show_Mouse(); + + /*------------------------------------------------------------------------ + Save values into the Settings structure + ------------------------------------------------------------------------*/ + if (rc) { + memcpy( settings, &tempsettings, sizeof(SerialSettingsType) ); + } + + return(rc); + +} /* end of Com_Settings_Dialog */ + + +/*************************************************************************** + * Build_Init_String_Listbox -- [re]builds the initstring listbox * + * * + * This routine rebuilds the initstring list box from scratch; it also * + * updates the contents of the initstring edit field. * + * * + * INPUT: * + * list ptr to list box * + * edit ptr to edit box * + * buf ptr to buffer for initstring * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/08/1995 DRD : Created. * + *=========================================================================*/ +static void Build_Init_String_Listbox (ListClass *list, EditClass *edit, char *buf, int *index) +{ + int i, curidx; + char *item; + + + curidx = *index; + + /*........................................................................ + Clear the list + ........................................................................*/ + while (list->Count()) { + item = (char *)(list->Get_Item(0)); + list->Remove_Item(item); + delete [] item; + } + + /* + ** Now sort the init string list by name then number + */ + qsort ((void *)(&InitStrings[0]), InitStrings.Count(), sizeof(char *), Init_String_Compare); + + /*........................................................................ + Build the list + ........................................................................*/ + for (i = 0; i < InitStrings.Count(); i++) { + item = new char[ INITSTRBUF_MAX ]; + strcpy( item, InitStrings[i] ); + list->Add_Item(item); + } + list->Flag_To_Redraw(); + + /*........................................................................ + Init the current phone book index + ........................................................................*/ + if (list->Count() == 0 || curidx < -1) { + curidx = -1; + } else { + if (curidx >= list->Count() ) { + curidx = 0; + } + } + + /*........................................................................ + Fill in initstring edit buffer + ........................................................................*/ + if (curidx > -1) { + strcpy (buf, InitStrings[ curidx ]); + edit->Set_Text (buf, INITSTRBUF_MAX ); + list->Set_Selected_Index( curidx ); + } + + *index = curidx; +} + + +/*************************************************************************** + * Init_String_Compare -- for qsort * + * * + * INPUT: * + * p1,p2 ptrs to elements to compare * + * * + * OUTPUT: * + * 0 = same, -1 = (*p1) goes BEFORE (*p2), 1 = (*p1) goes AFTER (*p2) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/08/1995 DRD : Created. * + *=========================================================================*/ +static int Init_String_Compare (const void *p1, const void *p2) +{ + return( strcmp( *((char **)p1), *((char **)p2) ) ); +} + + +/*********************************************************************************************** + * Com_Scenario_Dialog -- Serial game scenario selection dialog * + * * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Serial Game ³ * + * ³ ³ * + * ³ Your Name: __________ House: [GDI] [NOD] ³ * + * ³ Credits: ______ Desired Color: [ ][ ][ ][ ] ³ * + * ³ Opponent: Name ³ * + * ³ ³ * + * ³ Scenario ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³ Hell's Kitchen ³³ ³ * + * ³ ³ Heaven's Gate ÃÄ´ ³ * + * ³ ³ ... ³ ³ ³ * + * ³ ³ ÃÄ´ ³ * + * ³ ³ ³³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ [ Bases ] [ Crates ] ³ * + * ³ [ Tiberium ] [ AI Players ] ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ [Send Message] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = success, false = cancel * + * * + * WARNINGS: * + * MPlayerName & MPlayerGameName must contain this player's name. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +#define TXT_HOST_INTERNET_GAME 4567+1 +#define TXT_JOIN_INTERNET_GAME 4567+2 +int Com_Scenario_Dialog(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 290*factor; // dialog width + int d_dialog_h = 190*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6*factor+1; // ht of 6-pt text + int d_margin1 = 5*factor; // margin width/height + int d_margin2 = 2*factor; // margin width/height + + int d_name_w = 70*factor; + int d_name_h = 9*factor; + int d_name_x = d_dialog_x + 108*factor; + int d_name_y = d_dialog_y + d_margin1 + d_txt6_h + d_txt6_h + d_margin1; + + int d_credits_w = ((CREDITSBUF_MAX - 1) * 6*factor) + 3*factor; + int d_credits_h = 9*factor; + int d_credits_x = d_name_x; + int d_credits_y = d_name_y + d_name_h + d_margin2; + + int d_gdi_w = 30*factor; + int d_gdi_h = 9*factor; + int d_gdi_x = d_dialog_cx + (d_dialog_w / 4); + int d_gdi_y = d_dialog_y + d_margin1 + d_txt6_h + d_txt6_h + d_margin1; + + int d_nod_w = 30*factor; + int d_nod_h = 9*factor; + int d_nod_x = d_gdi_x + d_gdi_w + (d_margin1 / 2); + int d_nod_y = d_gdi_y; + + int d_color_w = 10*factor; + int d_color_h = 9*factor; + int d_color_y = d_gdi_y + d_gdi_h + d_margin2; + + int d_opponent_x = d_name_x; + int d_opponent_y = d_color_y + d_color_h + d_margin2; + + int d_scenariolist_w = 182*factor; + int d_scenariolist_h = 27*factor; + int d_scenariolist_x = d_dialog_cx - (d_scenariolist_w / 2); + int d_scenariolist_y = d_opponent_y + d_txt6_h + 3*factor + d_txt6_h; + + // d_count_x is calculated below after other enums + int d_count_w = 25*factor; + int d_count_h = 7*factor; + int d_count_y = d_scenariolist_y + d_scenariolist_h + d_margin2; + + // d_level_x is calculated below after other enums + int d_level_w = 25*factor; + int d_level_h = 7*factor; + int d_level_y = d_count_y; + +#if (GERMAN | FRENCH) + int d_bases_w = 120*factor;//BGA:100; +#else + int d_bases_w = 110*factor; +#endif + int d_bases_h = 9*factor; + int d_bases_x = d_dialog_cx - d_bases_w - d_margin2; + int d_bases_y = d_count_y + d_count_h + d_margin2; + +#if (GERMAN | FRENCH) + int d_goodies_w = 120*factor; +#else + int d_goodies_w = 110*factor; +#endif + int d_goodies_h = 9*factor; + int d_goodies_x = d_dialog_cx + d_margin2; + int d_goodies_y = d_bases_y; + + int d_count_x = d_dialog_cx - d_count_w - ((2 * 6*factor) + 3*factor) + - ((d_bases_w - ((13 * 6*factor) + 3*factor + d_count_w)) / 2) - d_margin2; + + int d_level_x = d_dialog_cx + (11 * 6*factor) + + ((d_goodies_w - ((13 * 6*factor) + 3*factor + d_level_w)) / 2) + d_margin2; + +#if (GERMAN | FRENCH) + int d_tiberium_w = 120*factor; +#else + int d_tiberium_w = 110*factor; +#endif + int d_tiberium_h = 9*factor; + int d_tiberium_x = d_dialog_cx - d_bases_w - d_margin2; + int d_tiberium_y = d_bases_y + d_bases_h + d_margin2; + +#if (GERMAN | FRENCH) + int d_ghosts_w = 120*factor; +#else + int d_ghosts_w = 110*factor; +#endif + int d_ghosts_h = 9*factor; + int d_ghosts_x = d_dialog_cx + d_margin2; + int d_ghosts_y = d_tiberium_y; + + int d_ok_w = 45*factor; + int d_ok_h = 9*factor; + int d_ok_x = d_tiberium_x + (d_tiberium_w / 2) - (d_ok_w / 2); + int d_ok_y = d_tiberium_y + d_tiberium_h + d_margin1; + + int d_cancel_w = 45*factor; + int d_cancel_h = 9*factor; + int d_cancel_x = d_ghosts_x + (d_ghosts_w / 2) - (d_cancel_w / 2); + int d_cancel_y = d_tiberium_y + d_tiberium_h + d_margin1; + + int d_message_w = d_dialog_w - (d_margin1 * 2); + int d_message_h = 34*factor; + int d_message_x = d_dialog_x + d_margin1; + int d_message_y = d_cancel_y + d_cancel_h + d_margin1; + + int d_send_w = 80*factor; + int d_send_h = 9*factor; + int d_send_x = d_dialog_cx - (d_send_w / 2); + int d_send_y = d_message_y + d_message_h + d_margin2; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_NAME = 100, + BUTTON_GDI, + BUTTON_NOD, + BUTTON_CREDITS, + BUTTON_SCENARIOLIST, + BUTTON_COUNT, + BUTTON_LEVEL, + BUTTON_BASES, + BUTTON_TIBERIUM, + BUTTON_GOODIES, + BUTTON_GHOSTS, + BUTTON_OK, + BUTTON_CANCEL, + BUTTON_SEND, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + char credbuf[CREDITSBUF_MAX]; // for credit edit box + int old_cred; // old value in credits buffer + int transmit; // 1 = re-transmit new game options + int cbox_x[] = { d_gdi_x, + d_gdi_x + d_color_w, + d_gdi_x + (d_color_w * 2), + d_gdi_x + (d_color_w * 3), + d_gdi_x + (d_color_w * 4), + d_gdi_x + (d_color_w * 5)}; + int parms_received = 0; // 1 = game options received + int changed = 0; // 1 = user has changed an option + + int rc; + int recsignedoff = false; + int i; + int version; + char txt[80]; + unsigned long starttime; + unsigned long timingtime; + unsigned long lastmsgtime; + unsigned long lastredrawtime; + unsigned long transmittime = 0; + unsigned long theirresponsetime; + int packetlen; + static int first_time = 1; + bool oppscorescreen = false; + bool gameoptions = false; + EventClass *event; // event ptr + unsigned long msg_timeout = 1200; // init to 20 seconds + + int message_length; + int sent_so_far; + unsigned short magic_number; + unsigned short crc; + bool ready_to_go = false; + CountDownTimerClass ready_time; + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + EditClass name_edt (BUTTON_NAME, + namebuf, MPLAYER_NAME_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_name_x, d_name_y, d_name_w, d_name_h, EditClass::ALPHANUMERIC); + + TextButtonClass gdibtn(BUTTON_GDI, TXT_G_D_I, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_gdi_x, d_gdi_y, d_gdi_w, d_gdi_h); + + TextButtonClass nodbtn(BUTTON_NOD, TXT_N_O_D, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_nod_x, d_nod_y, d_nod_w, d_nod_h); + + EditClass credit_edt (BUTTON_CREDITS, + credbuf, CREDITSBUF_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_credits_x, d_credits_y, d_credits_w, d_credits_h, EditClass::ALPHANUMERIC); + + ListClass scenariolist(BUTTON_SCENARIOLIST, + d_scenariolist_x, d_scenariolist_y, d_scenariolist_w, d_scenariolist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + GaugeClass countgauge (BUTTON_COUNT, + d_count_x, d_count_y, d_count_w, d_count_h); + + GaugeClass levelgauge (BUTTON_LEVEL, + d_level_x, d_level_y, d_level_w, d_level_h); + + TextButtonClass basesbtn(BUTTON_BASES, TXT_BASES_OFF, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_bases_x, d_bases_y, d_bases_w, d_bases_h); + + TextButtonClass tiberiumbtn(BUTTON_TIBERIUM, TXT_TIBERIUM_OFF, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_tiberium_x, d_tiberium_y, d_tiberium_w, d_tiberium_h); + + TextButtonClass goodiesbtn(BUTTON_GOODIES, TXT_CRATES_OFF, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_goodies_x, d_goodies_y, d_goodies_w, d_goodies_h); + + TextButtonClass ghostsbtn(BUTTON_GHOSTS, TXT_AI_PLAYERS_OFF, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_ghosts_x, d_ghosts_y, d_ghosts_w, d_ghosts_h); + + TextButtonClass okbtn(BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_ok_x, d_ok_y, d_ok_w, d_ok_h); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + TextButtonClass sendbtn(BUTTON_SEND, TXT_SEND_MESSAGE, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_send_x, d_send_y); +//#else + d_send_x, d_send_y, d_send_w, d_send_h); +//#endif + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &name_edt; + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); + credit_edt.Add_Tail(*commands); + scenariolist.Add_Tail(*commands); + countgauge.Add_Tail(*commands); + levelgauge.Add_Tail(*commands); + basesbtn.Add_Tail(*commands); + tiberiumbtn.Add_Tail(*commands); + goodiesbtn.Add_Tail(*commands); + ghostsbtn.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + sendbtn.Add_Tail(*commands); + + /* + ----------------------------- Various Inits ------------------------------ + */ + /*........................................................................ + Init player name & house + ........................................................................*/ + MPlayerColorIdx = MPlayerPrefColor; // init my preferred color + strcpy (namebuf, MPlayerName); // set my name + name_edt.Set_Text(namebuf,MPLAYER_NAME_MAX); + name_edt.Set_Color(MPlayerTColors[MPlayerColorIdx]); + + if (MPlayerHouse==HOUSE_GOOD) { + gdibtn.Turn_On(); + } else { + nodbtn.Turn_On(); + } + + /*........................................................................ + Init scenario values, only the first time through + ........................................................................*/ + if (first_time) { + MPlayerCredits = 3000; // init credits & credit buffer + MPlayerBases = 1; // init scenario parameters + MPlayerTiberium = 0; + MPlayerGoodies = 0; + MPlayerGhosts = 0; + Special.IsCaptureTheFlag = 0; + MPlayerUnitCount = (MPlayerCountMax[MPlayerBases] + MPlayerCountMin[MPlayerBases]) / 2; + first_time = 0; + } + + /*........................................................................ + Init button states + ........................................................................*/ + if (MPlayerBases) { + basesbtn.Turn_On(); + basesbtn.Set_Text(TXT_BASES_ON); + } + if (MPlayerTiberium) { + tiberiumbtn.Turn_On(); + tiberiumbtn.Set_Text(TXT_TIBERIUM_ON); + } + if (MPlayerGoodies) { + goodiesbtn.Turn_On(); + goodiesbtn.Set_Text(TXT_CRATES_ON); + } + if (MPlayerGhosts) { + ghostsbtn.Turn_On(); + ghostsbtn.Set_Text(TXT_AI_PLAYERS_ON); + } + if (Special.IsCaptureTheFlag) { + MPlayerGhosts = 0; + ghostsbtn.Turn_On(); + ghostsbtn.Set_Text(TXT_CAPTURE_THE_FLAG); + } + + sprintf(credbuf, "%d", MPlayerCredits); + credit_edt.Set_Text(credbuf, CREDITSBUF_MAX); + old_cred = MPlayerCredits; + + levelgauge.Set_Maximum(MPLAYER_BUILD_LEVEL_MAX - 1); + levelgauge.Set_Value(BuildLevel - 1); + + countgauge.Set_Maximum(MPlayerCountMax[MPlayerBases] - MPlayerCountMin[MPlayerBases]); + countgauge.Set_Value(MPlayerUnitCount - MPlayerCountMin[MPlayerBases]); + + /*........................................................................ + Init other scenario parameters + ........................................................................*/ + Special.IsTGrowth = MPlayerTiberium; + Special.IsTSpread = MPlayerTiberium; + transmit = 1; + + /*........................................................................ + Init scenario description list box + ........................................................................*/ + for (i = 0; i < MPlayerScenarios.Count(); i++) { + scenariolist.Add_Item (strupr(MPlayerScenarios[i])); + } + ScenarioIdx = 0; // 1st scenario is selected + + /*........................................................................ + Init random-number generator, & create a seed to be used for all random + numbers from here on out + ........................................................................*/ + randomize(); + Seed = rand(); + + /*........................................................................ + Init the message display system + ........................................................................*/ + Messages.Init (d_message_x + 2*factor, d_message_y + 2*factor, 4, MAX_MESSAGE_LENGTH, d_txt6_h); + + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + + extern char ModemRXString[]; + + if (strlen(ModemRXString) > 36) + ModemRXString[36] = 0; + + if (strlen(ModemRXString) > 0) + Messages.Add_Message (ModemRXString, CC_TAN, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, 0, 0); + + ModemRXString[0] = '\0'; + + /* + ---------------------------- Init Mono Output ---------------------------- + */ + #if(SHOW_MONO) + NullModem.Configure_Debug(sizeof (CommHeaderType),sizeof (SerialCommandType), + SerialPacketNames, 106); + NullModem.Mono_Debug_Print(1); + #endif + + /* + ---------------------------- Processing loop ----------------------------- + */ + NullModem.Reset_Response_Time(); // clear response time + theirresponsetime = 10000; // initialize to an invalid value + timingtime = lastmsgtime = lastredrawtime = TickCount.Time(); + while (Get_Mouse_State() > 0) Show_Mouse(); + + + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ +#ifdef FORCE_WINSOCK + if (Winsock.Get_Connected()){ + Draw_Caption (TXT_HOST_INTERNET_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + }else{ + Draw_Caption (TXT_HOST_SERIAL_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + } +#else + Draw_Caption (TXT_HOST_SERIAL_GAME, d_dialog_x, d_dialog_y, d_dialog_w); +#endif //FORCE_WINSOCK + + Fancy_Text_Print(TXT_YOUR_NAME, + d_name_x - 5*factor, d_name_y + 1*factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_SIDE_COLON, + d_gdi_x - 5*factor, d_gdi_y + 1*factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print (TXT_START_CREDITS_COLON, d_credits_x - 5*factor, d_credits_y + 1*factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_COLOR_COLON, + cbox_x[0] - 5*factor, d_color_y + 1*factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_SCENARIOS, + d_scenariolist_x + (d_scenariolist_w / 2), + d_scenariolist_y - d_txt6_h, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print (TXT_COUNT, d_count_x - 3*factor, d_count_y, CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_RIGHT); + + Fancy_Text_Print (TXT_LEVEL, d_level_x - 3*factor, d_level_y, CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_RIGHT); + } + + /*.................................................................. + Draw the color boxes + ..................................................................*/ + if (display >= REDRAW_COLORS) { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + LogicPage->Fill_Rect (cbox_x[i] + 1*factor, d_color_y + 1*factor, + cbox_x[i] + 1*factor + d_color_w - 2*factor, d_color_y + 1*factor + d_color_h - 2*factor, + MPlayerGColors[i]); + + if (i == MPlayerColorIdx) { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, + BOXSTYLE_GREEN_DOWN, false); + } else { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, + BOXSTYLE_GREEN_RAISED, false); + } + } + } + + /*.................................................................. + Draw the message: + - Erase an old message first + ..................................................................*/ + if (display >= REDRAW_MESSAGE) { + Draw_Box(d_message_x, d_message_y, d_message_w, d_message_h, + BOXSTYLE_GREEN_BORDER, true); + Messages.Draw(); + + LogicPage->Fill_Rect (d_dialog_x + 2*factor, + d_opponent_y, + d_dialog_x + d_dialog_w - 4*factor, + d_opponent_y + d_txt6_h, + BLACK); + + if (parms_received) { + if (oppscorescreen) { + sprintf(txt,"%s",Text_String(TXT_WAITING_FOR_OPPONENT)); + + int txtwidth = String_Pixel_Width( txt ); + + Fancy_Text_Print (txt, d_dialog_cx - (txtwidth / 2), + d_opponent_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } else { + Fancy_Text_Print (TXT_OPPONENT_COLON, d_opponent_x - 3*factor, + d_opponent_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (TheirHouse == HOUSE_GOOD) { + sprintf(txt,"%s %s",TheirName,Text_String(TXT_G_D_I)); + } else { + sprintf(txt,"%s %s",TheirName,Text_String(TXT_N_O_D)); + } + + Fancy_Text_Print (txt, d_opponent_x, + d_opponent_y, MPlayerTColors[TheirColor], TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + } + + sprintf( txt, "%d ", MPlayerUnitCount ); + Fancy_Text_Print (txt, d_count_x + d_count_w + 3*factor, + d_count_y, CC_GREEN, BLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(txt, "%d ", BuildLevel); + } else { + sprintf(txt, "**"); + } + Fancy_Text_Print (txt, d_level_x + d_level_w + 3*factor, + d_level_y, CC_GREEN, BLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + /*------------------------------------------------------------------ + User clicks on a color button + ------------------------------------------------------------------*/ + case KN_LMOUSE: + if (_Kbd->MouseQX > cbox_x[0] && + _Kbd->MouseQX < (cbox_x[MAX_MPLAYER_COLORS - 1] + d_color_w) && + _Kbd->MouseQY > d_color_y && + _Kbd->MouseQY < (d_color_y + d_color_h)) { + if (!ready_to_go){ + MPlayerPrefColor = (_Kbd->MouseQX - cbox_x[0]) / d_color_w; + MPlayerColorIdx = MPlayerPrefColor; + display = REDRAW_COLORS; + + name_edt.Set_Color (MPlayerTColors[MPlayerColorIdx]); + name_edt.Flag_To_Redraw(); + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + changed = 1; + } + } + break; + + /*------------------------------------------------------------------ + User edits the name field; retransmit new game options + ------------------------------------------------------------------*/ + case (BUTTON_NAME | KN_BUTTON): + if (!ready_to_go){ + credit_edt.Clear_Focus(); + credit_edt.Flag_To_Redraw(); + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + changed = 1; + } + break; + + /*------------------------------------------------------------------ + House Buttons: set the player's desired House + ------------------------------------------------------------------*/ + case (BUTTON_GDI | KN_BUTTON): + if (!ready_to_go){ + MPlayerHouse = HOUSE_GOOD; + gdibtn.Turn_On(); + nodbtn.Turn_Off(); + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + } + break; + + case (BUTTON_NOD | KN_BUTTON): + if (!ready_to_go){ + MPlayerHouse = HOUSE_BAD; + gdibtn.Turn_Off(); + nodbtn.Turn_On(); + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + } + break; + + /*------------------------------------------------------------------ + User edits the credits value; retransmit new game options + ------------------------------------------------------------------*/ + case (BUTTON_CREDITS | KN_BUTTON): + if (!ready_to_go){ + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + } + break; + + /*------------------------------------------------------------------ + New Scenario selected. + ------------------------------------------------------------------*/ + case (BUTTON_SCENARIOLIST | KN_BUTTON): + if (scenariolist.Current_Index() != ScenarioIdx && !ready_to_go) { + ScenarioIdx = scenariolist.Current_Index(); + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + } + break; + + /*------------------------------------------------------------------ + User adjusts max # units + ------------------------------------------------------------------*/ + case (BUTTON_COUNT | KN_BUTTON): + if (!ready_to_go){ + MPlayerUnitCount = countgauge.Get_Value() + MPlayerCountMin[MPlayerBases]; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + transmit = 1; + } + break; + + /*------------------------------------------------------------------ + User adjusts build level + ------------------------------------------------------------------*/ + case (BUTTON_LEVEL | KN_BUTTON): + if (!ready_to_go){ + BuildLevel = levelgauge.Get_Value() + 1; + if (BuildLevel > MPLAYER_BUILD_LEVEL_MAX) // if it's pegged, max it out + BuildLevel = MPLAYER_BUILD_LEVEL_MAX; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + transmit = 1; + } + break; + + /*------------------------------------------------------------------ + Toggle bases + ------------------------------------------------------------------*/ + case (BUTTON_BASES | KN_BUTTON): + if (!ready_to_go){ + if (MPlayerBases) { + MPlayerBases = 0; + basesbtn.Turn_Off(); + basesbtn.Set_Text(TXT_BASES_OFF); + MPlayerUnitCount = Fixed_To_Cardinal (MPlayerCountMax[0]-MPlayerCountMin[0], + Cardinal_To_Fixed(MPlayerCountMax[1]-MPlayerCountMin[1], + MPlayerUnitCount-MPlayerCountMin[1])) + MPlayerCountMin[0]; + } else { + MPlayerBases = 1; + basesbtn.Turn_On(); + basesbtn.Set_Text(TXT_BASES_ON); + MPlayerUnitCount = Fixed_To_Cardinal (MPlayerCountMax[1]-MPlayerCountMin[1], + Cardinal_To_Fixed(MPlayerCountMax[0]-MPlayerCountMin[0], + MPlayerUnitCount-MPlayerCountMin[0])) + MPlayerCountMin[1]; + } + MPlayerCredits = atoi(credbuf); + countgauge.Set_Maximum(MPlayerCountMax[MPlayerBases] - MPlayerCountMin[MPlayerBases]); + countgauge.Set_Value(MPlayerUnitCount - MPlayerCountMin[MPlayerBases]); + strcpy (MPlayerName, namebuf); + transmit = 1; + display = REDRAW_ALL; + } + break; + + /*------------------------------------------------------------------ + Toggle tiberium + ------------------------------------------------------------------*/ + case (BUTTON_TIBERIUM | KN_BUTTON): + if (!ready_to_go){ + if (MPlayerTiberium) { + MPlayerTiberium = 0; + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + tiberiumbtn.Turn_Off(); + tiberiumbtn.Set_Text(TXT_TIBERIUM_OFF); + } else { + MPlayerTiberium = 1; + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + tiberiumbtn.Turn_On(); + tiberiumbtn.Set_Text(TXT_TIBERIUM_ON); + } + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + } + break; + + /*------------------------------------------------------------------ + Toggle goodies + ------------------------------------------------------------------*/ + case (BUTTON_GOODIES | KN_BUTTON): + if (!ready_to_go){ + if (MPlayerGoodies) { + MPlayerGoodies = 0; + goodiesbtn.Turn_Off(); + goodiesbtn.Set_Text(TXT_CRATES_OFF); + } else { + MPlayerGoodies = 1; + goodiesbtn.Turn_On(); + goodiesbtn.Set_Text(TXT_CRATES_ON); + } + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + } + break; + + /*------------------------------------------------------------------ + Toggle ghosts + ------------------------------------------------------------------*/ + case (BUTTON_GHOSTS | KN_BUTTON): + if (!ready_to_go){ + if (!MPlayerGhosts && !Special.IsCaptureTheFlag) { // ghosts OFF => ghosts ON + MPlayerGhosts = 1; + Special.IsCaptureTheFlag = 0; + ghostsbtn.Turn_On(); + ghostsbtn.Set_Text(TXT_AI_PLAYERS_ON); + } + else if (MPlayerGhosts) { // ghosts ON => capture-flag + MPlayerGhosts = 0; + Special.IsCaptureTheFlag = 1; + ghostsbtn.Turn_On(); + ghostsbtn.Set_Text(TXT_CAPTURE_THE_FLAG); + } + else if (Special.IsCaptureTheFlag) { // capture-flag => AI OFF + MPlayerGhosts = 0; + Special.IsCaptureTheFlag = 0; + ghostsbtn.Turn_Off(); + ghostsbtn.Set_Text(TXT_AI_PLAYERS_OFF); + } + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + } + break; + + /*------------------------------------------------------------------ + OK: exit loop with true status + ------------------------------------------------------------------*/ + case (BUTTON_OK | KN_BUTTON): + if (!ready_to_go){ + // + // make sure we got a game options packet from the other player + // + if (gameoptions) { + //rc = true; + //process = false; + + // force transmitting of game options packet one last time + + + + SendPacket.Command = SERIAL_READY_TO_GO; + SendPacket.ID = ModemGameToPlay; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount.Time(); + + while ( ( NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_SENDING_TIMEOUT) )){ + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + NullModem.Service(); + Keyboard::Check(); //Make sure the message loop gets called + } + + ready_to_go = true; + ready_time.Set(120, true); + + transmit = 1; + transmittime = 0; + + } else { + CCMessageBox().Process (TXT_ONLY_ONE,TXT_OOPS,NULL); + display = REDRAW_ALL; + } + } + break; + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF, bail out with error code + ------------------------------------------------------------------*/ + case (KN_ESC): + if (!ready_to_go){ + if (Messages.Get_Edit_Buf() != NULL) { + Messages.Input(input); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + break; + } + } + case (BUTTON_CANCEL | KN_BUTTON): + if (!ready_to_go){ + process = false; + rc = false; + } + break; + + /*------------------------------------------------------------------ + Default: manage the inter-player messages + ------------------------------------------------------------------*/ + default: + if (ready_to_go) break; + + /*............................................................... + F4/SEND/'M' = send a message + ...............................................................*/ + if (Messages.Get_Edit_Buf()==NULL) { + if (input == KN_M || input==(BUTTON_SEND | KN_BUTTON) || + input == KN_F4) { + memset (txt, 0, 80); + + strcpy(txt,Text_String(TXT_MESSAGE)); // "Message:" + + Messages.Add_Edit (MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, d_message_w-70*factor); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + + credit_edt.Clear_Focus(); + credit_edt.Flag_To_Redraw(); + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + + break; + } + } else { + if ( input == (BUTTON_SEND | KN_BUTTON) ) { + input = KN_RETURN; + } + } + + /*............................................................... + Manage the message system (get rid of old messages) + ...............................................................*/ + if (Messages.Manage()) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } + + /*............................................................... + Service keyboard input for any message being edited. + ...............................................................*/ + i = Messages.Input(input); + + /*............................................................... + If 'Input' returned 1, it means refresh the message display. + ...............................................................*/ + if (i==1) { + Messages.Draw(); + } + + /*............................................................... + If 'Input' returned 2, it means redraw the message display. + ...............................................................*/ + else if (i==2) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } + + /*............................................................... + If 'input' returned 3, it means send the current message. + ...............................................................*/ + else if (i==3) { + long actual_message_size; + char *the_string; + + sent_so_far = 0; + magic_number = MESSAGE_HEAD_MAGIC_NUMBER; + message_length = strlen(Messages.Get_Edit_Buf()); + crc = (unsigned short) + (Calculate_CRC(Messages.Get_Edit_Buf(), message_length) &0xffff); + + while (sent_so_far < message_length){ + + SendPacket.Command = SERIAL_MESSAGE; + strcpy (SendPacket.Name, MPlayerName); + SendPacket.ID = Build_MPlayerID(MPlayerColorIdx, MPlayerHouse); + memcpy (SendPacket.Message, Messages.Get_Edit_Buf()+sent_so_far, COMPAT_MESSAGE_LENGTH-5); + + /* + ** Steve I's stuff for splitting message on word boundries + */ + actual_message_size = COMPAT_MESSAGE_LENGTH - 5; + + /* Start at the end of the message and find a space with 10 chars. */ + the_string = SendPacket.Message; + while ( (COMPAT_MESSAGE_LENGTH -5) -actual_message_size < 10 && + the_string[actual_message_size] != ' '){ + --actual_message_size; + } + if ( the_string[actual_message_size] == ' ' ){ + + /* Now delete the extra characters after the space (they musnt print) */ + for ( int i=0 ; i< (COMPAT_MESSAGE_LENGTH-5) - actual_message_size; i++ ){ + the_string[i + actual_message_size] = 0xff; + } + }else{ + actual_message_size = COMPAT_MESSAGE_LENGTH - 5; + } + + *(SendPacket.Message + COMPAT_MESSAGE_LENGTH-5) = 0; + *((unsigned short*)(SendPacket.Message + COMPAT_MESSAGE_LENGTH-4)) = magic_number; + *((unsigned short*)(SendPacket.Message + COMPAT_MESSAGE_LENGTH-2)) = crc; + + /*.................................................................. + Send the message + ..................................................................*/ + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + NullModem.Service(); + + /*.................................................................. + Add the message to our own screen + ..................................................................*/ + sprintf(txt, Text_String (TXT_FROM), MPlayerName, SendPacket.Message); + Messages.Add_Message (txt, MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, magic_number, crc); + + magic_number++; + sent_so_far += actual_message_size; //COMPAT_MESSAGE_LENGTH-5; + } + + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } /* end of send message */ + + } /* end of input processing */ + + /*--------------------------------------------------------------------- + Detect editing of the credits buffer, transmit new values to players + ---------------------------------------------------------------------*/ + if (atoi(credbuf) != old_cred) { + old_cred = Bound(atoi(credbuf), 0, 9999); + MPlayerCredits = old_cred; + transmit = 1; + sprintf(credbuf, "%d", MPlayerCredits); + credit_edt.Set_Text(credbuf, CREDITSBUF_MAX); + } + + /*--------------------------------------------------------------------- + Detect editing of the name buffer, transmit new values to players + ---------------------------------------------------------------------*/ + if (strcmp (namebuf, MPlayerName)) { + strcpy (MPlayerName, namebuf); + transmit = 1; + changed = 1; + } + + /*--------------------------------------------------------------------- + If our Transmit flag is set, we need to send out a game option packet. + This message requires an ACK. The first time through the loop, transmit + should be set, so we send out our default options; we'll then send + any changes we make to the defaults. + ---------------------------------------------------------------------*/ + if (transmit && (TickCount.Time() - transmittime) > PACKET_RETRANS_TIME) { + SendPacket.Command = SERIAL_GAME_OPTIONS; + strcpy (SendPacket.Name, MPlayerName); +#ifdef PATCH + if (IsV107) { + SendPacket.Version = 1; + } else { + SendPacket.Version = 2; + } +#else + SendPacket.Version = Version_Number(); +#endif + SendPacket.House = MPlayerHouse; + SendPacket.Color = MPlayerColorIdx; + + SendPacket.Scenario = MPlayerFilenum[ScenarioIdx]; + + SendPacket.Credits = MPlayerCredits; + SendPacket.IsBases = MPlayerBases; + SendPacket.IsTiberium = MPlayerTiberium; + SendPacket.IsGoodies = MPlayerGoodies; + SendPacket.IsGhosties = MPlayerGhosts; + SendPacket.BuildLevel = BuildLevel; + SendPacket.UnitCount = MPlayerUnitCount; + SendPacket.Seed = Seed; + SendPacket.Special = Special; + SendPacket.GameSpeed = Options.GameSpeed; + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + transmittime = TickCount.Time(); + transmit = 0; + + starttime = TickCount.Time(); + while ( ( NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_SENDING_TIMEOUT) )){ + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + NullModem.Service(); + Keyboard::Check(); //Make sure the message loop gets called + } + } + + // + // send a timing packet if enough time has gone by. + // + if ( (TickCount.Time() - timingtime) > PACKET_TIMING_TIMEOUT) { + SendPacket.Command = SERIAL_TIMING; + SendPacket.ResponseTime = NullModem.Response_Time(); + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 0); + timingtime = TickCount.Time(); + } + + /*--------------------------------------------------------------------- + Check for an incoming message + ---------------------------------------------------------------------*/ + while (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { +// Smart_Printf( "received packet of length %d\n", packetlen ); + + lastmsgtime = TickCount.Time(); + msg_timeout = 600; // reset timeout value to 10 seconds + // (only the 1st time through is 20 seconds) + + // are we getting our own packets back?? + + if (ReceivePacket.Command >= SERIAL_CONNECT && + ReceivePacket.Command < SERIAL_LAST_COMMAND && + ReceivePacket.Command != SERIAL_MESSAGE && + ReceivePacket.ID == ModemGameToPlay) { + + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + break; + } + + event = (EventClass *)&ReceivePacket; + if (event->Type <= EventClass::FRAMEINFO) { + if ( (TickCount.Time() - lastredrawtime) > PACKET_REDRAW_TIME) { + lastredrawtime = TickCount.Time(); + oppscorescreen = true; + +if (display != REDRAW_ALL) { + display = REDRAW_MESSAGE; +} + +// display = REDRAW_MESSAGE; + parms_received = 1; + } + } else { + switch ( ReceivePacket.Command ) { + /*.................................................................. + Sign-off: Give the other machine time to receive my ACK, display a + message, and exit. + ..................................................................*/ + case (SERIAL_SIGN_OFF): +// Smart_Printf( "received sign off\n" ); + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < 60) + NullModem.Service(); + CCMessageBox().Process(TXT_USER_SIGNED_OFF); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + recsignedoff = true; + break; + + /*.................................................................. + Game Options: Store the other machine's name, color & house; + If they've picked the same color as myself, re-transmit my settings + to force him to choose a different color. (Com_Show_Scenario_Dialog + is responsible for ensuring the colors are different.) + ..................................................................*/ + case (SERIAL_GAME_OPTIONS): +// Smart_Printf( "received game options\n" ); + oppscorescreen = false; + gameoptions = true; + strcpy (TheirName, ReceivePacket.Name); + TheirColor = ReceivePacket.Color; + TheirHouse = ReceivePacket.House; + transmit = 1; + + parms_received = 1; +if (display != REDRAW_ALL) { + display = REDRAW_MESSAGE; +} +// display = REDRAW_MESSAGE; + + /*............................................................... + Check the version number of the other system. + ...............................................................*/ +#ifdef PATCH + if (IsV107) { + version = 1; + } else { + version = 2; + } +#else + version = Version_Number(); +#endif + if (ReceivePacket.Version > version) { + CCMessageBox().Process (TXT_YOURGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + } else { + if (ReceivePacket.Version < version) { + CCMessageBox().Process (TXT_DESTGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + } + } + break; + + /*.................................................................. + Incoming message: add to our list + ..................................................................*/ + case (SERIAL_MESSAGE): +// Smart_Printf( "received serial message\n" ); + oppscorescreen = false; + sprintf(txt, Text_String (TXT_FROM), ReceivePacket.Name, + ReceivePacket.Message); + magic_number = *((unsigned short*)(ReceivePacket.Message + COMPAT_MESSAGE_LENGTH-4)); + crc = *((unsigned short*)(ReceivePacket.Message + COMPAT_MESSAGE_LENGTH-2)); + Messages.Add_Message (txt, + MPlayerTColors[MPlayerID_To_ColorIndex(ReceivePacket.ID)], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, magic_number, crc); +if (display != REDRAW_ALL) { + display = REDRAW_MESSAGE; +} +// display = REDRAW_MESSAGE; + break; + + // + // get their response time + // + case (SERIAL_TIMING): +// Smart_Printf( "received timing\n" ); + oppscorescreen = false; + theirresponsetime = ReceivePacket.ResponseTime; + + if ( !gameoptions ) { + + // retransmit of game options packet again + transmit = 1; + } + break; + + // + // print msg waiting for opponent + // + case (SERIAL_SCORE_SCREEN): +// Smart_Printf( "received score screen\n" ); + oppscorescreen = true; +if (display != REDRAW_ALL) { + display = REDRAW_MESSAGE; +} +// display = REDRAW_MESSAGE; + parms_received = 1; + break; + + default: +// Smart_Printf( "received unknown command %X\n", ReceivePacket.Command ); + break; + } + } + } + + // if we haven't received a msg for 10 seconds exit + + if ( (TickCount.Time() - lastmsgtime) > msg_timeout) { + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + } + + /*--------------------------------------------------------------------- + Service the connection + ---------------------------------------------------------------------*/ + NullModem.Service(); + + /* + ** If user has clicked 'GO' and the timeout has elapsed then quit the loop + */ + if ( ready_to_go && ready_time.Time() == 0 ){ + rc = 1; + process = false; + } + + } /* end of while */ + + /*------------------------------------------------------------------------ + Sort player ID's, so we can execute commands in the same order on both + machines. + ------------------------------------------------------------------------*/ + if (rc) { + /*..................................................................... + Set the number of players in this game, and my ID + .....................................................................*/ + MPlayerCount = 2; + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + TheirID = Build_MPlayerID (TheirColor,TheirHouse); + + /*..................................................................... + Store every player's ID in the MPlayerID[] array. This array will + determine the order of event execution, so the ID's must be stored + in the same order on all systems. + .....................................................................*/ + if (TheirID < MPlayerLocalID) { + MPlayerID[0] = TheirID; + MPlayerID[1] = MPlayerLocalID; + strcpy (MPlayerNames[0], TheirName); + strcpy (MPlayerNames[1], MPlayerName); + } else { + MPlayerID[0] = MPlayerLocalID; + MPlayerID[1] = TheirID; + strcpy (MPlayerNames[0], MPlayerName); + strcpy (MPlayerNames[1], TheirName); + } + + /*..................................................................... + Get the scenario filename + .....................................................................*/ + Scenario = MPlayerFilenum[ScenarioIdx]; + + /*..................................................................... + Send all players the GO packet. + .....................................................................*/ + SendPacket.Command = SERIAL_GO; + SendPacket.ResponseTime = NullModem.Response_Time(); + if ( theirresponsetime == 10000 ) { +// Mono_Clear_Screen(); +// Smart_Printf( "Did not receive their response time!!!!!!!\n" ); +// Get_Key(); + } else { + if (SendPacket.ResponseTime < theirresponsetime) { + SendPacket.ResponseTime = theirresponsetime; + } + } + + // + // calculated one way delay for a packet and overall delay to execute + // a packet + // + MPlayerMaxAhead = MAX( (SendPacket.ResponseTime / 8), 2); +char flip[128]; +sprintf (flip, "C&C95 - MaxAhead set to %d frames\n", MPlayerMaxAhead); +CCDebugString (flip); + + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount.Time(); + while ( ( NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_SENDING_TIMEOUT) )){ + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + NullModem.Service(); + Keyboard::Check(); //Make sure the message loop gets called + } + + // clear queue to keep from doing any resends + NullModem.Init_Send_Queue(); + + } else { + if ( !recsignedoff ) { + /*..................................................................... + Broadcast my sign-off over my network + .....................................................................*/ + SendPacket.Command = SERIAL_SIGN_OFF; + SendPacket.Color = MPlayerLocalID; // use Color for ID + SendPacket.ID = ModemGameToPlay; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount.Time(); + while ( (NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_CANCEL_TIMEOUT) )){ + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + if ( NullModem.Get_Message( &ReceivePacket, &packetlen ) > 0) { + + // are we getting our own packets back?? + + if (ReceivePacket.Command == SERIAL_SIGN_OFF + && ReceivePacket.ID == ModemGameToPlay) { + + // exit while + break; + } + } + + NullModem.Service(); + } + } + + Shutdown_Modem(); + } + + /*------------------------------------------------------------------------ + Clear all lists + ------------------------------------------------------------------------*/ + while (scenariolist.Count()) { + scenariolist.Remove_Item(scenariolist.Get_Item(0)); + } + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Show_Mouse(); + + /*------------------------------------------------------------------------ + Save any changes made to our options + ------------------------------------------------------------------------*/ + if (changed) { + Write_MultiPlayer_Settings (); + } + + return(rc); + +} /* end of Com_Scenario_Dialog */ + + +/*********************************************************************************************** + * Com_Show_Scenario_Dialog -- Serial game scenario selection dialog * + * * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Serial Game ³ * + * ³ ³ * + * ³ Your Name: __________ ³ * + * ³ House: [GDI] [NOD] ³ * + * ³ Desired Color: [ ][ ][ ][ ] ³ * + * ³ ³ * + * ³ Opponent: Name ³ * + * ³ Scenario: Description ³ * + * ³ Credits: xxxx ³ * + * ³ Bases: ON ³ * + * ³ Crates: ON ³ * + * ³ Tiberium: ON ³ * + * ³ Ghosts: ON ³ * + * ³ ³ * + * ³ [Cancel] ³ * + * ³ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ [Send Message] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = success, false = cancel * + * * + * WARNINGS: * + * MPlayerName & MPlayerGameName must contain this player's name. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +int Com_Show_Scenario_Dialog(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 306*factor; // dialog width + int d_dialog_h = 187*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6*factor+1; // ht of 6-pt text + int d_margin1 = 5*factor; // margin width/height + int d_margin2 = 2*factor; // margin width/height + + int d_name_w = 70*factor; + int d_name_h = 9*factor; + int d_name_x = d_dialog_cx; + int d_name_y = d_dialog_y + d_margin1 + d_txt6_h + d_txt6_h; + + int d_gdi_w = 30*factor; + int d_gdi_h = 9*factor; + int d_gdi_x = d_dialog_cx; + int d_gdi_y = d_name_y + d_name_h + d_margin2; + + int d_nod_w = 30*factor; + int d_nod_h = 9*factor; + int d_nod_x = d_gdi_x + d_gdi_w + d_margin2; + int d_nod_y = d_gdi_y; + + int d_color_w = 10*factor; + int d_color_h = 9*factor; + int d_color_y = d_gdi_y + d_gdi_h + d_margin2; + + int d_opponent_y = d_color_y + d_color_h + d_margin1; + int d_scenario_y = d_opponent_y + d_txt6_h; + int d_credits_y = d_scenario_y + d_txt6_h; + int d_count_y = d_credits_y + d_txt6_h; + int d_level_y = d_count_y + d_txt6_h; + int d_bases_y = d_level_y + d_txt6_h; + int d_goodies_y = d_bases_y + d_txt6_h; + int d_tiberium_y = d_goodies_y + d_txt6_h; + int d_ghosts_y = d_tiberium_y + d_txt6_h; + + int d_cancel_w = 45*factor; + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx - (d_cancel_w / 2); + int d_cancel_y = d_ghosts_y + d_txt6_h + d_margin1; + + int d_message_w = d_dialog_w - (d_margin1 * 2); + int d_message_h = 34*factor; + int d_message_x = d_dialog_x + d_margin1; + int d_message_y = d_cancel_y + d_cancel_h + d_margin1; + + int d_send_w = 80*factor; + int d_send_h = 9*factor; + int d_send_x = d_dialog_cx - (d_send_w / 2); + int d_send_y = d_message_y + d_message_h + d_margin2; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_NAME = 100, + BUTTON_GDI, + BUTTON_NOD, + BUTTON_CANCEL, + BUTTON_SEND, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + BOOL process = true; // process while true + KeyNumType input; + + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int transmit; // 1 = re-transmit new game options + int first; // 1 = no packets received yet + int cbox_x[] = { d_dialog_cx, + d_dialog_cx + d_color_w, + d_dialog_cx + (d_color_w * 2), + d_dialog_cx + (d_color_w * 3), + d_dialog_cx + (d_color_w * 4), + d_dialog_cx + (d_color_w * 5)}; + int parms_received = 0; // 1 = game options received + int changed = 0; // 1 = user has changed an option + + int rc; + int recsignedoff = 0; + int i; + int version; + char txt[80]; + unsigned long starttime; + unsigned long timingtime; + unsigned long lastmsgtime; + unsigned long lastredrawtime; + unsigned long transmittime = 0; + int packetlen; + bool oppscorescreen = false; + bool gameoptions = false; + EventClass *event; // event ptr + unsigned long msg_timeout = 1200; // init to 20 seconds + + int message_length; + int sent_so_far; + unsigned short magic_number; + unsigned short crc; + bool ready_to_go = false; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + EditClass name_edt (BUTTON_NAME, + namebuf, MPLAYER_NAME_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_name_x, d_name_y, d_name_w, d_name_h, EditClass::ALPHANUMERIC); + + TextButtonClass gdibtn(BUTTON_GDI, TXT_G_D_I, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_gdi_x, d_gdi_y, d_gdi_w, d_gdi_h); + + TextButtonClass nodbtn(BUTTON_NOD, TXT_N_O_D, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_nod_x, d_nod_y, d_nod_w, d_nod_h); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + TextButtonClass sendbtn(BUTTON_SEND, TXT_SEND_MESSAGE, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_send_x, d_send_y); +//#else + d_send_x, d_send_y, d_send_w, d_send_h); +//#endif + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &name_edt; + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + sendbtn.Add_Tail(*commands); + + /* + ----------------------------- Various Inits ------------------------------ + */ + /*........................................................................ + Init player name & house + ........................................................................*/ + MPlayerColorIdx = MPlayerPrefColor; // init my preferred color + strcpy (namebuf, MPlayerName); // set my name + name_edt.Set_Text(namebuf,MPLAYER_NAME_MAX); + name_edt.Set_Color(MPlayerTColors[MPlayerColorIdx]); + + if (MPlayerHouse==HOUSE_GOOD) { + gdibtn.Turn_On(); + } else { + nodbtn.Turn_On(); + } + + Fancy_Text_Print("", 0, 0, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + transmit = 1; + first = 1; + + /*........................................................................ + Init the message display system + ........................................................................*/ + Messages.Init (d_message_x + 2*factor, d_message_y + 2*factor, 4, MAX_MESSAGE_LENGTH, d_txt6_h); + + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + + extern char ModemRXString[]; + + if (strlen(ModemRXString) > 36) + ModemRXString[36] = 0; + + if (strlen(ModemRXString) > 0) + Messages.Add_Message (ModemRXString, CC_TAN, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, 0, 0); + + ModemRXString[0] = '\0'; + + /* + ---------------------------- Init Mono Output ---------------------------- + */ + #if(SHOW_MONO) + NullModem.Configure_Debug(sizeof (CommHeaderType),sizeof (SerialCommandType), + SerialPacketNames, 106); + NullModem.Mono_Debug_Print(1); + #endif + + /* + ---------------------------- Processing loop ----------------------------- + */ + NullModem.Reset_Response_Time(); // clear response time + timingtime = lastmsgtime = lastredrawtime = TickCount.Time(); + while (Get_Mouse_State() > 0) Show_Mouse(); + + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ +#ifdef FORCE_WINSOCK + if (Winsock.Get_Connected()){ + Draw_Caption (TXT_JOIN_INTERNET_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + }else{ + Draw_Caption (TXT_JOIN_SERIAL_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + } +#else + Draw_Caption (TXT_JOIN_SERIAL_GAME, d_dialog_x, d_dialog_y, d_dialog_w); +#endif //FORCE_WINSOCK + + Fancy_Text_Print(TXT_YOUR_NAME, + d_name_x - 5*factor, d_name_y + 1*factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_SIDE_COLON, + d_gdi_x - 5*factor, d_gdi_y + 1*factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_COLOR_COLON, + cbox_x[0] - 5*factor, d_color_y + 1*factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + } + + /*.................................................................. + Draw the color boxes + ..................................................................*/ + if (display >= REDRAW_COLORS) { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + LogicPage->Fill_Rect (cbox_x[i] + 1*factor, d_color_y + 1*factor, + cbox_x[i] + 1*factor + d_color_w - 2*factor, d_color_y + 1*factor + d_color_h - 2*factor, + MPlayerGColors[i]); + + if (i == MPlayerColorIdx) { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, + BOXSTYLE_GREEN_DOWN, false); + } else { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, + BOXSTYLE_GREEN_RAISED, false); + } + } + } + + /*.................................................................. + Draw the message: + - Erase an old message first + ..................................................................*/ + if (display >= REDRAW_MESSAGE) { + Draw_Box(d_message_x, d_message_y, d_message_w, d_message_h, + BOXSTYLE_GREEN_BORDER, true); + Messages.Draw(); + + LogicPage->Fill_Rect( d_dialog_x + 2*factor, + d_opponent_y, + d_dialog_x + d_dialog_w - 4*factor, + d_ghosts_y + d_txt6_h, + BLACK); + + if (parms_received) { + if (oppscorescreen) { + sprintf(txt,"%s",Text_String(TXT_WAITING_FOR_OPPONENT)); + + int txtwidth = String_Pixel_Width( txt ); + + Fancy_Text_Print (txt, d_dialog_cx - (txtwidth / 2), + d_opponent_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } else { + /*............................................................ + Opponent's name + ............................................................*/ + Fancy_Text_Print (TXT_OPPONENT_COLON, d_dialog_cx - 3*factor, + d_opponent_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (TheirHouse == HOUSE_GOOD) { + sprintf(txt,"%s %s",TheirName,Text_String(TXT_G_D_I)); + } else { + sprintf(txt,"%s %s",TheirName,Text_String(TXT_N_O_D)); + } + + Fancy_Text_Print (txt, d_dialog_cx, + d_opponent_y, MPlayerTColors[TheirColor], TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Scenario description + ............................................................*/ + Fancy_Text_Print (TXT_SCENARIO_COLON, d_dialog_cx - 3*factor, + d_scenario_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (ScenarioIdx != -1) { + sprintf(txt,"%s", MPlayerScenarios[ScenarioIdx]); + + Fancy_Text_Print (txt, d_dialog_cx, + d_scenario_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } else { + strcpy(txt,Text_String(TXT_NOT_FOUND)); + + Fancy_Text_Print (txt, d_dialog_cx, + d_scenario_y, RED, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + + /*............................................................ + Credits + ............................................................*/ + Fancy_Text_Print (TXT_START_CREDITS_COLON, d_dialog_cx - 3*factor, + d_credits_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + sprintf(txt,"%d",MPlayerCredits); + Fancy_Text_Print (txt, d_dialog_cx, + d_credits_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Count + ............................................................*/ + + Fancy_Text_Print (TXT_COUNT, d_dialog_cx - 3*factor, + d_count_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + sprintf( txt, "%d ", MPlayerUnitCount ); + Fancy_Text_Print (txt, d_dialog_cx, + d_count_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Level + ............................................................*/ + + Fancy_Text_Print (TXT_LEVEL, d_dialog_cx - 3*factor, + d_level_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(txt, "%d ", BuildLevel); + } else { + sprintf(txt, "**"); + } + Fancy_Text_Print (txt, d_dialog_cx, + d_level_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Bases status + ............................................................*/ + Fancy_Text_Print (TXT_BASES_COLON, d_dialog_cx - 3*factor, + d_bases_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (MPlayerBases) { + strcpy(txt,Text_String(TXT_ON)); + } else { + strcpy(txt,Text_String(TXT_OFF)); + } + Fancy_Text_Print (txt, d_dialog_cx, + d_bases_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Tiberium status + ............................................................*/ + Fancy_Text_Print (TXT_TIBERIUM_COLON, d_dialog_cx - 3*factor, + d_tiberium_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (MPlayerTiberium) { + strcpy(txt,Text_String(TXT_ON)); + } else { + strcpy(txt,Text_String(TXT_OFF)); + } + Fancy_Text_Print (txt, d_dialog_cx, + d_tiberium_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Goodies status + ............................................................*/ + Fancy_Text_Print (TXT_CRATES_COLON, d_dialog_cx - 3*factor, + d_goodies_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (MPlayerGoodies) { + strcpy(txt,Text_String(TXT_ON)); + } else { + strcpy(txt,Text_String(TXT_OFF)); + } + Fancy_Text_Print (txt, d_dialog_cx, + d_goodies_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Capture the flag or AI player ON/OFF + ............................................................*/ + if ( Special.IsCaptureTheFlag ) { + strcpy( txt, Text_String( TXT_CAPTURE_THE_FLAG ) ); + strcat( txt, ":" ); + Fancy_Text_Print (txt, d_dialog_cx - 3*factor, + d_ghosts_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + strcpy(txt,Text_String(TXT_ON)); + Fancy_Text_Print (txt, d_dialog_cx, + d_ghosts_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } else { + /*............................................................ + Ghost player status + ............................................................*/ + Fancy_Text_Print (TXT_AI_PLAYERS_COLON, d_dialog_cx - 3*factor, + d_ghosts_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (MPlayerGhosts) { + strcpy(txt,Text_String(TXT_ON)); + } else { + strcpy(txt,Text_String(TXT_OFF)); + } + Fancy_Text_Print (txt, d_dialog_cx, + d_ghosts_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + } + } + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + /*------------------------------------------------------------------ + User clicks on a color button + ------------------------------------------------------------------*/ + case KN_LMOUSE: + if (_Kbd->MouseQX > cbox_x[0] && + _Kbd->MouseQX < (cbox_x[MAX_MPLAYER_COLORS - 1] + d_color_w) && + _Kbd->MouseQY > d_color_y && + _Kbd->MouseQY < (d_color_y + d_color_h)) { + if (!ready_to_go){ + /*......................................................... + Compute my preferred color as the one I clicked on. + .........................................................*/ + MPlayerPrefColor = (_Kbd->MouseQX - cbox_x[0]) / d_color_w; + changed = 1; + /*......................................................... + If 'TheirColor' is set to the other player's color, make + sure we can't pick that color. + .........................................................*/ + if (parms_received) { + if (MPlayerPrefColor == TheirColor) + break; + } + MPlayerColorIdx = MPlayerPrefColor; + + name_edt.Set_Color(MPlayerTColors[MPlayerColorIdx]); + name_edt.Flag_To_Redraw(); + display = REDRAW_COLORS; + strcpy (MPlayerName, namebuf); + transmit = 1; + } + } + break; + + /*------------------------------------------------------------------ + House Buttons: set the player's desired House + ------------------------------------------------------------------*/ + case (BUTTON_GDI | KN_BUTTON): + if (!ready_to_go){ + MPlayerHouse = HOUSE_GOOD; + gdibtn.Turn_On(); + nodbtn.Turn_Off(); + strcpy (MPlayerName, namebuf); + transmit = 1; + } + break; + + case (BUTTON_NOD | KN_BUTTON): + if (!ready_to_go){ + MPlayerHouse = HOUSE_BAD; + gdibtn.Turn_Off(); + nodbtn.Turn_On(); + strcpy (MPlayerName, namebuf); + transmit = 1; + } + break; + + /*------------------------------------------------------------------ + User edits the name value; retransmit + ------------------------------------------------------------------*/ + case (BUTTON_NAME | KN_BUTTON): + if (!ready_to_go){ + strcpy (MPlayerName, namebuf); + transmit = 1; + changed = 1; + } + break; + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF, bail out with error code + ------------------------------------------------------------------*/ + case (KN_ESC): + if (!ready_to_go){ + if (Messages.Get_Edit_Buf() != NULL) { + Messages.Input(input); + display = REDRAW_MESSAGE; + break; + } + } + case (BUTTON_CANCEL | KN_BUTTON): + if (!ready_to_go){ + process = false; + rc = false; + } + break; + + /*------------------------------------------------------------------ + Default: manage the inter-player messages + ------------------------------------------------------------------*/ + default: + if (!ready_to_go){ + + /*............................................................... + F4/SEND/'M' = send a message + ...............................................................*/ + if (Messages.Get_Edit_Buf()==NULL) { + if (input == KN_M || input==(BUTTON_SEND | KN_BUTTON) || + input == KN_F4) { + memset (txt, 0, 80); + + strcpy(txt,Text_String(TXT_MESSAGE)); // "Message:" + + Messages.Add_Edit (MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, d_message_w-70*factor); + display = REDRAW_MESSAGE; + + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + + break; + } + } else { + if ( input == (BUTTON_SEND | KN_BUTTON) ) { + input = KN_RETURN; + } + } + + /*............................................................... + Manage the message system (get rid of old messages) + ...............................................................*/ + if (Messages.Manage()) { + display = REDRAW_MESSAGE; + } + + /*............................................................... + Re-draw the messages & service keyboard input for any message + being edited. + ...............................................................*/ + i = Messages.Input(input); + + /*............................................................... + If 'Input' returned 1, it means refresh the message display. + ...............................................................*/ + if (i==1) { + Messages.Draw(); + } else { + + /*............................................................... + If 'Input' returned 2, it means redraw the message display. + ...............................................................*/ + if (i==2) { + display = REDRAW_MESSAGE; + } else { + + /*............................................................... + If 'input' returned 3, it means send the current message. + ...............................................................*/ + if (i==3) { + long actual_message_size; + char *the_string; + + sent_so_far = 0; + magic_number = MESSAGE_HEAD_MAGIC_NUMBER; + message_length = strlen(Messages.Get_Edit_Buf()); + crc = (unsigned short) + (Calculate_CRC(Messages.Get_Edit_Buf(), message_length) &0xffff); + + while (sent_so_far < message_length){ + SendPacket.Command = SERIAL_MESSAGE; + strcpy (SendPacket.Name, MPlayerName); + SendPacket.ID = Build_MPlayerID(MPlayerColorIdx, MPlayerHouse); + memcpy (SendPacket.Message, Messages.Get_Edit_Buf()+sent_so_far, COMPAT_MESSAGE_LENGTH-5); + + /* + ** Steve I's stuff for splitting message on word boundries + */ + actual_message_size = COMPAT_MESSAGE_LENGTH - 5; + + /* Start at the end of the message and find a space with 10 chars. */ + the_string = GPacket.Message.Buf; + while ( (COMPAT_MESSAGE_LENGTH -5) -actual_message_size < 10 && + the_string[actual_message_size] != ' '){ + --actual_message_size; + } + if ( the_string[actual_message_size] == ' ' ){ + + /* Now delete the extra characters after the space (they musnt print) */ + for ( int i=0 ; i< (COMPAT_MESSAGE_LENGTH-5) - actual_message_size; i++ ){ + the_string[i + actual_message_size] = 0xff; + } + }else{ + actual_message_size = COMPAT_MESSAGE_LENGTH - 5; + } + + *(SendPacket.Message + COMPAT_MESSAGE_LENGTH-5) = 0; + *((unsigned short*)(SendPacket.Message + COMPAT_MESSAGE_LENGTH-4)) = magic_number; + *((unsigned short*)(SendPacket.Message + COMPAT_MESSAGE_LENGTH-2)) = crc; + + /*.................................................................. + Send the message + ..................................................................*/ + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + NullModem.Service(); + + /*.................................................................. + Add the message to our own screen + ..................................................................*/ + sprintf(txt, Text_String (TXT_FROM), MPlayerName, SendPacket.Message); + Messages.Add_Message (txt, MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, magic_number, crc); + + magic_number++; + sent_so_far += actual_message_size; //COMPAT_MESSAGE_LENGTH-5; + } + display = REDRAW_MESSAGE; + } + } + } + } + break; + } + + /*--------------------------------------------------------------------- + Detect editing of the name buffer, transmit new values to players + ---------------------------------------------------------------------*/ + if (strcmp (namebuf, MPlayerName)) { + strcpy (MPlayerName, namebuf); + transmit = 1; + changed = 1; + } + + /*--------------------------------------------------------------------- + If our Transmit flag is set, we need to send out a game option packet + ---------------------------------------------------------------------*/ + if (transmit && (TickCount.Time() - transmittime) > PACKET_RETRANS_TIME) { + SendPacket.Command = SERIAL_GAME_OPTIONS; + strcpy (SendPacket.Name, MPlayerName); +#ifdef PATCH + if (IsV107) { + SendPacket.Version = 1; + } else { + SendPacket.Version = 2; + } +#else + SendPacket.Version = Version_Number(); +#endif + SendPacket.House = MPlayerHouse; + SendPacket.Color = MPlayerColorIdx; + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + transmittime = TickCount.Time(); + transmit = 0; + } + + // + // send a timing packet if enough time has gone by. + // + if ( (TickCount.Time() - timingtime) > PACKET_TIMING_TIMEOUT) { + SendPacket.Command = SERIAL_TIMING; + SendPacket.ResponseTime = NullModem.Response_Time(); + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 0); + timingtime = TickCount.Time(); + } + + /*--------------------------------------------------------------------- + Check for an incoming message + ---------------------------------------------------------------------*/ + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { +// Smart_Printf( "received packet of length %d\n", packetlen ); + + lastmsgtime = TickCount.Time(); + + msg_timeout = 600; + + // are we getting our own packets back?? + + if (ReceivePacket.Command >= SERIAL_CONNECT && + ReceivePacket.Command < SERIAL_LAST_COMMAND && + ReceivePacket.Command != SERIAL_MESSAGE && + ReceivePacket.ID == ModemGameToPlay) { + + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + break; + } + + event = (EventClass *)&ReceivePacket; + if (event->Type <= EventClass::FRAMEINFO) { + if ( (TickCount.Time() - lastredrawtime) > PACKET_REDRAW_TIME) { + lastredrawtime = TickCount.Time(); + oppscorescreen = true; + display = REDRAW_MESSAGE; + parms_received = 1; + } + } else { + switch ( ReceivePacket.Command ) { + + /* + ** Once the host is ready to go, we can no longer change game options. + */ + case SERIAL_READY_TO_GO: + ready_to_go = true; + break; + + /*.................................................................. + Other system signs off: Give it time to receive my ACK, then show + a message. + ..................................................................*/ + case (SERIAL_SIGN_OFF): + starttime = TickCount.Time(); + while ( (TickCount.Time() - starttime) < 60) + NullModem.Service(); + CCMessageBox().Process(TXT_USER_SIGNED_OFF); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + recsignedoff = true; + break; + + /*.................................................................. + Game Options: Store all options; check my color & game version. + ..................................................................*/ + case (SERIAL_GAME_OPTIONS): + oppscorescreen = false; + gameoptions = true; + display = REDRAW_MESSAGE; + parms_received = 1; + + strcpy (TheirName, ReceivePacket.Name); + TheirColor = ReceivePacket.Color; + TheirHouse = ReceivePacket.House; + + /*............................................................... + Make sure I don't have the same color as the other guy. + ...............................................................*/ + if (MPlayerColorIdx == TheirColor) { + + // force transmitting of game options packet + + transmit = 1; + transmittime = 0; + + MPlayerColorIdx = TheirColor + 1; + if (MPlayerColorIdx >= 6) + MPlayerColorIdx = 0; + name_edt.Set_Color(MPlayerTColors[MPlayerColorIdx]); + name_edt.Flag_To_Redraw(); + display = REDRAW_COLORS; + } + + /*............................................................... + Save scenario settings. + ...............................................................*/ + MPlayerCredits = ReceivePacket.Credits; + MPlayerBases = ReceivePacket.IsBases; + MPlayerTiberium = ReceivePacket.IsTiberium; + MPlayerGoodies = ReceivePacket.IsGoodies; + MPlayerGhosts = ReceivePacket.IsGhosties; + BuildLevel = ReceivePacket.BuildLevel; + MPlayerUnitCount = ReceivePacket.UnitCount; + Seed = ReceivePacket.Seed; + Special = ReceivePacket.Special; + Options.GameSpeed = ReceivePacket.GameSpeed; + + if (MPlayerTiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + + /*............................................................... + Find the index of the scenario number; if it's not found, leave + it at -1. + ...............................................................*/ + ScenarioIdx = -1; + for (i = 0; i < MPlayerFilenum.Count(); i++) { + if (ReceivePacket.Scenario == MPlayerFilenum[i]) + ScenarioIdx = i; + } + + /*............................................................... + Check our version numbers; if they're incompatible, sign off. + ...............................................................*/ +#ifdef PATCH + if (IsV107) { + version = 1; + } else { + version = 2; + } +#else + version = Version_Number(); +#endif + if (ReceivePacket.Version > version) { + CCMessageBox().Process (TXT_YOURGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + } else { + if (ReceivePacket.Version < version) { + CCMessageBox().Process (TXT_DESTGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + } + } + + /*............................................................... + If this is the first game-options packet we've received, transmit + our options to him. + ...............................................................*/ + if (first) { + first = 0; + + // force transmitting of game options packet + + transmit = 1; + transmittime = 0; + } + break; + + /*.................................................................. + GO: Exit this routine with a success code. + ..................................................................*/ + case (SERIAL_GO): + // + // calculated one way delay for a packet and overall delay + // to execute a packet + // + MPlayerMaxAhead = MAX( (ReceivePacket.ResponseTime / 8), 2); +char flip[128]; +sprintf (flip, "C&C95 - MaxAhead set to %d frames\n", MPlayerMaxAhead); +CCDebugString (flip); + + process = false; + rc = true; + break; + + /*.................................................................. + Incoming message: add to our list + ..................................................................*/ + case (SERIAL_MESSAGE): + oppscorescreen = false; + sprintf(txt, Text_String (TXT_FROM), ReceivePacket.Name, + ReceivePacket.Message); + magic_number = *((unsigned short*)(ReceivePacket.Message + COMPAT_MESSAGE_LENGTH-4)); + crc = *((unsigned short*)(ReceivePacket.Message + COMPAT_MESSAGE_LENGTH-2)); + Messages.Add_Message (txt, MPlayerTColors[MPlayerID_To_ColorIndex(ReceivePacket.ID)], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, magic_number, crc); + display = REDRAW_MESSAGE; + break; + + // + // throw away timing packet + // + case (SERIAL_TIMING): + oppscorescreen = false; + break; + + // + // print msg waiting for opponent + // + case (SERIAL_SCORE_SCREEN): +// Smart_Printf( "received score screen\n" ); + oppscorescreen = true; + display = REDRAW_MESSAGE; + parms_received = 1; + break; + + default: + break; + } + } + } + + // if we haven't received a msg for 10 seconds exit + + //if ( ((TickCount.Time() - lastmsgtime) > msg_timeout) || + //(Winsock.Get_Connected() && Winsock.Get_Connection_Status == TcpipManagerClass::CONNECTION_LOST)) { + + if ( (TickCount.Time() - lastmsgtime) > msg_timeout) { + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + } + + + + + /*--------------------------------------------------------------------- + Service the connection + ---------------------------------------------------------------------*/ + NullModem.Service(); + + } /* end of while */ + + /*------------------------------------------------------------------------ + Sort player ID's, so we can execute commands in the same order on both + machines. + ------------------------------------------------------------------------*/ + if (rc) { + /*..................................................................... + Set the number of players in this game, and my ID + .....................................................................*/ + MPlayerCount = 2; + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + TheirID = Build_MPlayerID (TheirColor,TheirHouse); + + /*..................................................................... + Store every player's ID in the MPlayerID[] array. This array will + determine the order of event execution, so the ID's must be stored + in the same order on all systems. + .....................................................................*/ + if (TheirID < MPlayerLocalID) { + MPlayerID[0] = TheirID; + MPlayerID[1] = MPlayerLocalID; + strcpy (MPlayerNames[0], TheirName); + strcpy (MPlayerNames[1], MPlayerName); + } else { + MPlayerID[0] = MPlayerLocalID; + MPlayerID[1] = TheirID; + strcpy (MPlayerNames[0], MPlayerName); + strcpy (MPlayerNames[1], TheirName); + } + + /*..................................................................... + Get the scenario filename + .....................................................................*/ + Scenario = MPlayerFilenum[ScenarioIdx]; + + starttime = TickCount.Time(); + while ( ( NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_SENDING_TIMEOUT) )){ + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + NullModem.Service(); + Keyboard::Check(); //Make sure the message loop gets called + } + + // clear queue to keep from doing any resends + NullModem.Init_Send_Queue(); + + } else { + if ( !recsignedoff ) { + /*..................................................................... + Broadcast my sign-off over my network + .....................................................................*/ + SendPacket.Command = SERIAL_SIGN_OFF; + SendPacket.Color = MPlayerLocalID; // use Color for ID + SendPacket.ID = ModemGameToPlay; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount.Time(); + while ( (NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_CANCEL_TIMEOUT) )){ + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + if ( NullModem.Get_Message( &ReceivePacket, &packetlen ) > 0) { + + // are we getting our own packets back?? + + if (ReceivePacket.Command == SERIAL_SIGN_OFF + && ReceivePacket.ID == ModemGameToPlay) { + + // exit while + break; + } + } + + NullModem.Service(); + } + } + + Shutdown_Modem(); + } + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Show_Mouse(); + + /*------------------------------------------------------------------------ + Save any changes made to our options + ------------------------------------------------------------------------*/ + if (changed) { + Write_MultiPlayer_Settings (); + } + + return(rc); + +} /* end of Com_Show_Scenario_Dialog */ + + + +/*************************************************************************** + * Phone_Dialog -- Lets user edit phone directory & dial * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = dial the current phone book entry, false = cancel. * + * * + * WARNINGS: * + * Serial options must have been read from CC.INI. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static int Phone_Dialog (void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 280 *factor; // dialog width + int d_dialog_h = 150 *factor; // dialog height + int d_dialog_x = ((320 *factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200 *factor- d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 11 *factor; // ht of 6-pt text + int d_margin = 7 *factor; // margin width/height + + int d_phonelist_w = 268 *factor; + int d_phonelist_h = 87 *factor; + int d_phonelist_x = d_dialog_cx - (d_phonelist_w / 2); + int d_phonelist_y = d_dialog_y + d_margin + d_txt6_h + 11*factor; + + int d_add_w = 45*factor; + int d_add_h = 9*factor; + int d_add_x = d_dialog_cx - (d_add_w / 2) - d_margin - d_add_w; + int d_add_y = d_phonelist_y + d_phonelist_h + d_margin; + + int d_edit_w = 45*factor; + int d_edit_h = 9*factor; + int d_edit_x = d_dialog_cx - (d_edit_w / 2); + int d_edit_y = d_phonelist_y + d_phonelist_h + d_margin; + + int d_delete_w = 45*factor; + int d_delete_h = 9*factor; + int d_delete_x = d_dialog_cx + (d_delete_w / 2) + d_margin; + int d_delete_y = d_phonelist_y + d_phonelist_h + d_margin; + + int d_numedit_w = ( (PhoneEntryClass::PHONE_MAX_NUM - 1) * 6*factor) + 3*factor; + int d_numedit_h = 9*factor; + int d_numedit_x = d_dialog_cx - (d_numedit_w / 2); + int d_numedit_y = d_add_y + d_add_h + d_margin; + + int d_dial_w = 45*factor; + int d_dial_h = 9*factor; + int d_dial_x = d_dialog_cx - (d_numedit_w / 2) - d_margin - d_dial_w; + int d_dial_y = d_add_y + d_add_h + d_margin; + + int d_cancel_w = 45*factor; + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx + (d_numedit_w / 2) + d_margin; + int d_cancel_y = d_add_y + d_add_h + d_margin; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_PHONELIST = 100, + BUTTON_ADD, + BUTTON_EDIT, + BUTTON_DELETE, + BUTTON_DIAL, + BUTTON_CANCEL, + BUTTON_NUMEDIT, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + BOOL process = true; // process while true + KeyNumType input; + + char phone_num[ PhoneEntryClass::PHONE_MAX_NUM ] = { 0 }; // buffer for editing phone # + int rc; + int i; + int tabs[] = {123*factor, 207*factor}; // tabs for list box + char *item; // for removing items from list box + PhoneEntryClass *p_entry; // for creating / editing phonebook entries + int changed = 0; // 1 = save changes to INI file + int firsttime = 0; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + + ListClass phonelist(BUTTON_PHONELIST, + d_phonelist_x, d_phonelist_y, d_phonelist_w, d_phonelist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + TextButtonClass addbtn(BUTTON_ADD, TXT_ADD, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#ifdef FRENCH +// d_add_x-4, d_add_y); +//#else + d_add_x, d_add_y, d_add_w, d_add_h); +//#endif + + TextButtonClass editbtn(BUTTON_EDIT, TXT_EDIT, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_edit_x, d_edit_y, d_edit_w, d_edit_h); + + TextButtonClass deletebtn(BUTTON_DELETE, TXT_DELETE_BUTTON, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#ifdef FRENCH +// d_delete_x, d_delete_y); +//#else + d_delete_x, d_delete_y, d_delete_w, d_delete_h); +//#endif + + TextButtonClass dialbtn(BUTTON_DIAL, TXT_DIAL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +/* ###Change collision detected! C:\PROJECTS\CODE\NULLDLG.CPP... */ + d_dial_x, d_dial_y, d_dial_w, d_dial_h); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + EditClass numedit (BUTTON_NUMEDIT, + phone_num, PhoneEntryClass::PHONE_MAX_NUM, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_numedit_x, d_numedit_y, d_numedit_w, d_numedit_h, EditClass::ALPHANUMERIC); + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &phonelist; + addbtn.Add_Tail(*commands); + editbtn.Add_Tail(*commands); + deletebtn.Add_Tail(*commands); + dialbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + numedit.Add_Tail(*commands); + dialbtn.Turn_On(); + + /* + ----------------------------- Various Inits ------------------------------ + */ + /*........................................................................ + Fill in the phone directory list box + ........................................................................*/ + phonelist.Set_Tabs(tabs); + Build_Phone_Listbox(&phonelist, &numedit, phone_num); + + if (CurPhoneIdx == -1) { + firsttime = 1; + } + + /* + ---------------------------- Processing loop ----------------------------- + */ + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_PHONE_LIST, d_dialog_x, d_dialog_y, d_dialog_w); + } + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + phonelist.Flag_To_Redraw(); + addbtn.Flag_To_Redraw(); + editbtn.Flag_To_Redraw(); + deletebtn.Flag_To_Redraw(); + dialbtn.Flag_To_Redraw(); + cancelbtn.Flag_To_Redraw(); + numedit.Flag_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + if ( firsttime ) { + numedit.Set_Focus(); + numedit.Flag_To_Redraw(); + input = commands->Input(); + firsttime = 0; + } + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + /*------------------------------------------------------------------ + New phone listing selected. + ------------------------------------------------------------------*/ + case (BUTTON_PHONELIST | KN_BUTTON): + /*............................................................... + Detect a change in the selected item; update CurPhoneIdx, and + the edit box buffer. + ...............................................................*/ + if (phonelist.Current_Index() != CurPhoneIdx) { + CurPhoneIdx = phonelist.Current_Index(); + strcpy (phone_num, PhoneBook[CurPhoneIdx]->Number); + numedit.Set_Text (phone_num, PhoneEntryClass::PHONE_MAX_NUM ); + changed = 1; + } + break; + + /*------------------------------------------------------------------ + Add a new entry + ------------------------------------------------------------------*/ + case (BUTTON_ADD | KN_BUTTON): + + /*............................................................... + Allocate a new phone book entry + ...............................................................*/ + p_entry = new PhoneEntryClass(); + p_entry->Name[0] = 0; + p_entry->Number[0] = 0; + p_entry->Settings.Port = 0; + p_entry->Settings.IRQ = -1; + p_entry->Settings.Baud = -1; + p_entry->Settings.DialMethod = DIAL_TOUCH_TONE; + p_entry->Settings.InitStringIndex = 0; + p_entry->Settings.CallWaitStringIndex = CALL_WAIT_CUSTOM; + p_entry->Settings.CallWaitString[0] = 0; + + /*............................................................... + Invoke the entry editor; if user clicks Save, add the new entry + to the list, and rebuild the list box. + ...............................................................*/ + if ( Edit_Phone_Dialog( p_entry ) ) { + PhoneBook.Add (p_entry); + Build_Phone_Listbox( &phonelist, &numedit, phone_num ); + /*............................................................ + Set the current listbox index to the newly-added item. + ............................................................*/ + for (i = 0; i < PhoneBook.Count(); i++) { + if (p_entry == PhoneBook[i]) { + CurPhoneIdx = i; + strcpy (phone_num, PhoneBook[CurPhoneIdx]->Number); + numedit.Set_Text (phone_num, PhoneEntryClass::PHONE_MAX_NUM ); + phonelist.Set_Selected_Index( CurPhoneIdx ); + } + } + changed = 1; + } else { + + /*............................................................... + If the user clicked Cancel, delete the entry & keep looping. + ...............................................................*/ + delete p_entry; + } + display = REDRAW_ALL; + break; + + /*------------------------------------------------------------------ + Edit the current entry + ------------------------------------------------------------------*/ + case (BUTTON_EDIT | KN_BUTTON): + + /*............................................................... + Do nothing if no entry is selected. + ...............................................................*/ + if (CurPhoneIdx==-1) + break; + + /*............................................................... + Allocate a new entry & copy the currently-selected entry into it + ...............................................................*/ + p_entry = new PhoneEntryClass(); + (*p_entry) = (*PhoneBook[CurPhoneIdx]); + + /*............................................................... + Pass the new entry to the entry editor; if the user selects OK, + copy the data back into our phone book. Rebuild the list so + the changes show up in the list box. + ...............................................................*/ + if ( Edit_Phone_Dialog( p_entry ) ) { + (*PhoneBook[CurPhoneIdx]) = (*p_entry); + Build_Phone_Listbox(&phonelist, &numedit, phone_num); + /*............................................................ + Set the current listbox index to the newly-added item. + ............................................................*/ + for (i = 0; i < PhoneBook.Count(); i++) { + if (PhoneBook[CurPhoneIdx] == PhoneBook[i]) { + CurPhoneIdx = i; + strcpy (phone_num, PhoneBook[CurPhoneIdx]->Number); + numedit.Set_Text (phone_num, PhoneEntryClass::PHONE_MAX_NUM ); + phonelist.Set_Selected_Index( CurPhoneIdx ); + } + } + changed = 1; + } + delete p_entry; + display = REDRAW_ALL; + break; + + /*------------------------------------------------------------------ + Delete the current entry + ------------------------------------------------------------------*/ + case (BUTTON_DELETE | KN_BUTTON): + + /*............................................................... + Do nothing if no entry is selected. + ...............................................................*/ + if (CurPhoneIdx == -1) + break; + + /*............................................................... + Delete the current item & rebuild the phone listbox + ...............................................................*/ + PhoneBook.Delete (CurPhoneIdx); + Build_Phone_Listbox(&phonelist, &numedit, phone_num); + + if (CurPhoneIdx == -1) { + *phone_num = 0; + numedit.Set_Text (phone_num, PhoneEntryClass::PHONE_MAX_NUM ); + } + changed = 1; + break; + + /*------------------------------------------------------------------ + Dial the current number + ------------------------------------------------------------------*/ + case (KN_RETURN): + dialbtn.IsPressed = true; + dialbtn.Draw_Me(true); + // fall thru + + case (BUTTON_DIAL | KN_BUTTON): + + /*............................................................... + If no item is selected, just dial the number in the phone # + edit box: + - Create a new phone entry + - Copy the phone number into it + - Set settings to defaults + ...............................................................*/ + if (CurPhoneIdx == -1 || + strcmp( PhoneBook[CurPhoneIdx]->Number, phone_num) ) { + + if ( strlen(phone_num) == 0) { // do not dial + dialbtn.IsPressed = false; + dialbtn.Flag_To_Redraw(); + break; + } + + p_entry = new PhoneEntryClass(); + strcpy( p_entry->Name, "NONAME" ); + strcpy( p_entry->Number, phone_num); + p_entry->Settings.Port = 0; + p_entry->Settings.IRQ = -1; + p_entry->Settings.Baud = -1; + p_entry->Settings.DialMethod = DIAL_TOUCH_TONE; + p_entry->Settings.InitStringIndex = 0; + p_entry->Settings.CallWaitStringIndex = CALL_WAIT_CUSTOM; + p_entry->Settings.CallWaitString[0] = 0; + + PhoneBook.Add (p_entry); + Build_Phone_Listbox(&phonelist, &numedit, phone_num); + /*............................................................ + Set the current listbox index to the newly-added item. + ............................................................*/ + for (i = 0; i < PhoneBook.Count(); i++) { + if (p_entry == PhoneBook[i]) { + CurPhoneIdx = i; + } + } + changed = 1; + } + + process = false; + rc = true; + break; + + /*------------------------------------------------------------------ + CANCEL: bail out + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + } + + } /* end of while */ + + /*------------------------------------------------------------------------ + Save any changes we've made to the phone list or settings + ------------------------------------------------------------------------*/ + if (changed) { + Write_MultiPlayer_Settings (); + } + + /*------------------------------------------------------------------------ + Clear the list box + ------------------------------------------------------------------------*/ + while (phonelist.Count()) { + item = (char *)phonelist.Get_Item(0); + phonelist.Remove_Item(item); + delete [] item; + } + + return(rc); + +} /* end of Phone_Dialog */ + + +/*************************************************************************** + * Build_Phone_Listbox -- [re]builds the phone entry listbox * + * * + * This routine rebuilds the phone list box from scratch; it also updates * + * the contents of the phone # edit field. * + * * + * INPUT: * + * list ptr to list box * + * edit ptr to edit box * + * buf ptr to buffer for phone # * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static void Build_Phone_Listbox (ListClass *list, EditClass *edit, char *buf) +{ + int i; + char *item; + char phonename[21]; + char phonenum[15]; + + /*........................................................................ + Clear the list + ........................................................................*/ + while (list->Count()) { + item = (char *)(list->Get_Item(0)); + list->Remove_Item(item); + delete [] item; + } + + /* + ** Now sort the phone list by name then number + */ + qsort ((void *)(&PhoneBook[0]), PhoneBook.Count(), sizeof(class PhoneEntryClass *), Phone_Compare); + + /*........................................................................ + Build the list + ........................................................................*/ + for (i = 0; i < PhoneBook.Count(); i++) { + item = new char[80]; + if ( !(strlen( PhoneBook[i]->Name )) ) { + strcpy( phonename, " " ); + } else { + strncpy( phonename, PhoneBook[i]->Name, 20 ); + phonename[21] = 0; + } + + if ( !(strlen( PhoneBook[i]->Number )) ) { + strcpy( phonenum, " " ); + } else { + if ( strlen( PhoneBook[i]->Number ) < 14) { + strcpy( phonenum, PhoneBook[i]->Number ); + } else { + strncpy( phonenum, PhoneBook[i]->Number, 12 ); + phonenum[12] = 0; + strcat( phonenum, "..." ); + } + } + + if (PhoneBook[i]->Settings.Baud != -1) { + sprintf(item,"%s\t%s\t%d", phonename, phonenum, + PhoneBook[i]->Settings.Baud); + } else { + sprintf(item,"%s\t%s\t[%s]", phonename, phonenum, + Text_String(TXT_DEFAULT)); + } + list->Add_Item(item); + } + list->Flag_To_Redraw(); + + /*........................................................................ + Init the current phone book index + ........................................................................*/ + if (list->Count() == 0 || CurPhoneIdx < -1) { + CurPhoneIdx = -1; + } else { + if (CurPhoneIdx >= list->Count() ) { + CurPhoneIdx = 0; + } + } + + /*........................................................................ + Fill in phone number edit buffer + ........................................................................*/ + if (CurPhoneIdx > -1) { + strcpy (buf, PhoneBook[CurPhoneIdx]->Number); + edit->Set_Text (buf, PhoneEntryClass::PHONE_MAX_NUM ); + list->Set_Selected_Index( CurPhoneIdx ); + } +} + + +/*************************************************************************** + * Phone_Compare -- for qsort * + * * + * INPUT: * + * p1,p2 ptrs to elements to compare * + * * + * OUTPUT: * + * 0 = same, -1 = (*p1) goes BEFORE (*p2), 1 = (*p1) goes AFTER (*p2) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=========================================================================*/ +static int Phone_Compare (const void *p1, const void *p2) +{ + class PhoneEntryClass *pe1,*pe2; + int result; + + pe1 = *((class PhoneEntryClass **)p1); + pe2 = *((class PhoneEntryClass **)p2); + + result = strcmp( pe1->Name, pe2->Name ); + + // if strings are equal then check the phone number + + if ( !result ) { + result = strcmp( pe1->Number, pe2->Number ); + } + + return(result); +} + + +/*************************************************************************** + * Edit_Phone_Dialog -- lets user edit a phone book entry * + * * + * INPUT: * + * phone entry to edit * + * * + * OUTPUT: * + * true = OK, false = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static int Edit_Phone_Dialog (PhoneEntryClass *phone) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 230 *factor; // dialog width + int d_dialog_h = 105*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord +// D_DIALOG_Y = ((200 - D_DIALOG_H) / 2); // dialog y-coord + int d_dialog_y = ((136*factor - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 11 *factor; // ht of 6-pt text + int d_margin = 7 *factor; // margin width/height + + int d_name_w = ( (PhoneEntryClass::PHONE_MAX_NAME - 1) * 6) + 3 *factor; + int d_name_h = 9 *factor; + int d_name_x = d_dialog_x + (((d_dialog_w - d_name_w) * 3) / 4) - 5 *factor; + int d_name_y = d_dialog_y + 25 *factor; + + int d_number_w = ( (PhoneEntryClass::PHONE_MAX_NUM - 1) * 6) + 3 *factor; + int d_number_h = 9 *factor; + int d_number_x = d_dialog_x + (((d_dialog_w - d_number_w) * 3) / 4) - 5 *factor; + int d_number_y = d_name_y + d_name_h + d_margin; + +#if (GERMAN | FRENCH) + int d_default_w = 130 *factor; +#else + int d_default_w = 104 *factor; +#endif + int d_default_h = 9 *factor; + int d_default_x = d_dialog_cx - (d_default_w / 2); + int d_default_y = d_number_y + d_number_h + d_margin; + +#if (GERMAN | FRENCH) + int d_custom_w = 130 *factor; +#else + int d_custom_w = 100 *factor; +#endif + int d_custom_h = 9 *factor; + int d_custom_x = d_dialog_cx - (d_default_w / 2); + int d_custom_y = d_default_y + d_default_h + d_margin; + +#if (GERMAN | FRENCH) + int d_save_w = 55 *factor; +#else + int d_save_w = 45 *factor; +#endif + int d_save_h = 9 *factor; + int d_save_x = d_dialog_cx - d_margin - d_save_w; + int d_save_y = d_dialog_y + d_dialog_h - d_margin - d_save_h; + +#if (GERMAN | FRENCH) + int d_cancel_w = 55 *factor; +#else + int d_cancel_w = 45 *factor; +#endif + int d_cancel_h = 9 *factor; + int d_cancel_x = d_dialog_cx + d_margin; + int d_cancel_y = d_dialog_y + d_dialog_h - d_margin - d_cancel_h; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_NAME = 100, + BUTTON_NUMBER, + BUTTON_DEFAULT, + BUTTON_CUSTOM, + BUTTON_SAVE, + BUTTON_CANCEL, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + BOOL process = true; // process while true + KeyNumType input; + + char namebuf[PhoneEntryClass::PHONE_MAX_NAME] = { 0 }; // buffer for editing name + char numbuf[PhoneEntryClass::PHONE_MAX_NUM] = { 0 }; // buffer for editing phone # + int rc; + SerialSettingsType settings; + int custom = 0; + int firsttime = 1; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + EditClass nameedit (BUTTON_NAME, + namebuf, PhoneEntryClass::PHONE_MAX_NAME, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_name_x, d_name_y, d_name_w, d_name_h, EditClass::ALPHANUMERIC); + + EditClass numedit (BUTTON_NUMBER, + numbuf, PhoneEntryClass::PHONE_MAX_NUM, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_number_x, d_number_y, d_number_w, d_number_h, EditClass::ALPHANUMERIC); + + TextButtonClass defaultbtn(BUTTON_DEFAULT, TXT_DEFAULT_SETTINGS, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_default_x, d_default_y, d_default_w, d_default_h); + + TextButtonClass custombtn(BUTTON_CUSTOM, TXT_CUSTOM_SETTINGS, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_custom_x, d_custom_y, d_custom_w, d_custom_h); + + TextButtonClass savebtn(BUTTON_SAVE, TXT_SAVE_BUTTON, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_save_x, d_save_y, d_save_w, d_save_h); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &nameedit; + numedit.Add_Tail(*commands); + defaultbtn.Add_Tail(*commands); + custombtn.Add_Tail(*commands); + savebtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + + /* + ----------------------------- Various Inits ------------------------------ + */ + /*........................................................................ + Init the settings; if the phone entry is set to use defaults, init our + settings to sensible values (in case we invoke the setting editor); + otherwise, copy the entry's settings. + ........................................................................*/ + if (phone->Settings.Port == 0 || phone->Settings.IRQ == -1 || + phone->Settings.Baud == -1) { + settings = SerialDefaults; + defaultbtn.Turn_On(); + custom = false; + } else { + settings = phone->Settings; + custombtn.Turn_On(); + custom = true; + } + + strcpy (namebuf, phone->Name); + nameedit.Set_Text (namebuf, PhoneEntryClass::PHONE_MAX_NAME); + + strcpy (numbuf, phone->Number); + numedit.Set_Text (numbuf, PhoneEntryClass::PHONE_MAX_NUM); + + /* + ---------------------------- Processing loop ----------------------------- + */ + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_PHONE_LISTING, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print (TXT_NAME_COLON, + d_name_x - 5, d_name_y + 1, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print (TXT_NUMBER_COLON, + d_number_x - 5, d_number_y + 1, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + if ( firsttime ) { + nameedit.Set_Focus(); + nameedit.Flag_To_Redraw(); + input = commands->Input(); + firsttime = 0; + } + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + case (BUTTON_NAME | KN_BUTTON): + numedit.Set_Focus(); + numedit.Flag_To_Redraw(); + break; + +// case (BUTTON_NUMBER | KN_BUTTON): +// nameedit.Clear_Focus(); +// nameedit.Flag_To_Redraw(); +// break; + + /*------------------------------------------------------------------ + Use Default Serial Settings + ------------------------------------------------------------------*/ + case (BUTTON_DEFAULT | KN_BUTTON): + custombtn.Turn_Off(); + defaultbtn.Turn_On(); + custom = 0; + break; + + /*------------------------------------------------------------------ + Use Custom Serial Settings + ------------------------------------------------------------------*/ + case (BUTTON_CUSTOM | KN_BUTTON): + if (Com_Settings_Dialog (&settings)) { + custombtn.Turn_On(); + defaultbtn.Turn_Off(); + } + custom = 1; + display = REDRAW_ALL; + break; + + /*------------------------------------------------------------------ + CANCEL: bail out + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + + /*------------------------------------------------------------------ + Save: save changes + ------------------------------------------------------------------*/ + case (KN_RETURN): + case (BUTTON_SAVE | KN_BUTTON): + process = false; + rc = true; + break; + } + + } /* end of while */ + + /*------------------------------------------------------------------------ + If 'Save', save all current settings + ------------------------------------------------------------------------*/ + if (rc) { + strcpy( phone->Name, strupr( namebuf ) ); + + // if nothing was entered then make if NONAME + + if ( !(phone->Name[0]) ) { + strcpy( phone->Name, "NONAME" ); + } + + strcpy( phone->Number, strupr( numbuf ) ); + + if (custom) { + phone->Settings = settings; + } else { + phone->Settings.Port = 0; + phone->Settings.IRQ = -1; + phone->Settings.Baud = -1; + phone->Settings.DialMethod = DIAL_TOUCH_TONE; + phone->Settings.InitStringIndex = 0; + phone->Settings.CallWaitStringIndex = CALL_WAIT_CUSTOM; + phone->Settings.CallWaitString[0] = 0; + } + } + + return(rc); + + +} /* end of Edit_Phone_Dialog */ + + +static bool Dial_Modem( SerialSettingsType *settings, bool reconnect ) +{ + bool connected = false; + DialStatusType dialstatus; + int modemstatus; + + /* + ** Turn modem servicing off in the callback routine. + */ + ModemService = false; + + // save for later to reconnect + + DialSettings = settings; + + modemstatus = NullModem.Get_Modem_Status(); + if (reconnect) { + if ( (modemstatus & CD_SET) ) { + connected = true; + ModemService = true; + return( connected ); + } + } else { + if ( (modemstatus & CD_SET) ) { + NullModem.Hangup_Modem(); + ModemService = false; + } + } + + NullModem.Setup_Modem_Echo( Modem_Echo ); + + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + + /* + ** If our first attempt to detect the modem failed, and we're at + ** 14400 or 28800, bump up to the next baud rate & try again. + */ + switch (settings->Baud) { + + case 14400: + settings->Baud = 19200; + Shutdown_Modem(); + Init_Null_Modem(settings); + NullModem.Setup_Modem_Echo( Modem_Echo ); + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + CCMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + ModemService = true; + return( connected ); + } + break; + + case 28800: + settings->Baud = 38400; + Shutdown_Modem(); + Init_Null_Modem(settings); + NullModem.Setup_Modem_Echo( Modem_Echo ); + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + CCMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + ModemService = true; + return( connected ); + } + break; + + default: + CCMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + ModemService = true; + return( connected ); + + } + } + else if ( modemstatus == -1 ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + CCMessageBox().Process( TXT_ERROR_IN_INITSTRING ); + ModemService = true; + return( connected ); + } + + + /* + ** Completely disable audio. This is required for MWave devices + */ + ThemeType old_theme = THEME_NONE; + if (SoundOn){ + old_theme = Theme.What_Is_Playing(); + Theme.Stop(); + CountDownTimerClass wait; + Call_Back(); + wait.Set (60,true); + while ( wait.Time() ) { + Call_Back(); + } + Sound_End(); + Call_Back(); + wait.Set (60,true); + while ( wait.Time() ) { + Call_Back(); + } + SoundOn = 0; + } + + dialstatus = NullModem.Dial_Modem( DialString, settings->DialMethod, reconnect ); + + if (reconnect) { + /* + --------------------------- Redraw the display --------------------------- + */ + HiddenPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + } + + switch ( dialstatus ) { + case DIAL_CONNECTED: + connected = true; + break; + + case DIAL_NO_CARRIER: + CCMessageBox().Process(TXT_NO_CARRIER); + connected = false; + break; + + case DIAL_BUSY: + CCMessageBox().Process(TXT_LINE_BUSY); + connected = false; + break; + + case DIAL_ERROR: + CCMessageBox().Process(TXT_NUMBER_INVALID); + connected = false; + break; + + case DIAL_NO_DIAL_TONE: + CCMessageBox().Process(TXT_NO_DIAL_TONE); + connected = false; + break; + + case DIAL_CANCELED: + NullModem.Hangup_Modem(); + ModemService = false; + CCMessageBox().Process(TXT_DIALING_CANCELED); + connected = false; + break; + } + + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + + /* + ** Restore audio capability + */ + SoundOn = Audio_Init ( MainWindow , 16 , false , 11025*2 , 0 ); + if (SoundOn){ + Theme.Play_Song (old_theme); + } + + ModemService = true; + return( connected ); + +} /* end of Dial_Modem */ + + +static bool Answer_Modem( SerialSettingsType *settings, bool reconnect ) +{ + bool connected = false; + DialStatusType dialstatus; + int modemstatus; + +/* ###Change collision detected! C:\PROJECTS\CODE\NULLDLG.CPP... */ + /* + ** Turn modem servicing off in the callback routine. + */ + ModemService = false; + + // save for later to reconnect + + DialSettings = settings; + + modemstatus = NullModem.Get_Modem_Status(); + if (reconnect) { + if ( (modemstatus & CD_SET) ) { + connected = true; + ModemService = true; + return( connected ); + } + } else { + if ( (modemstatus & CD_SET) ) { + NullModem.Hangup_Modem(); + ModemService = false; + } + } + + NullModem.Setup_Modem_Echo( Modem_Echo ); + + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + + /* + ** If our first attempt to detect the modem failed, and we're at + ** 14400 or 28800, bump up to the next baud rate & try again. + */ + switch (settings->Baud) { + + case 14400: + settings->Baud = 19200; + Shutdown_Modem(); + Init_Null_Modem(settings); + NullModem.Setup_Modem_Echo( Modem_Echo ); + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + CCMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + ModemService = true; + return( connected ); + } + break; + + case 28800: + settings->Baud = 38400; + Shutdown_Modem(); + Init_Null_Modem(settings); + NullModem.Setup_Modem_Echo( Modem_Echo ); + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + CCMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + ModemService = true; + return( connected ); + } + break; + + default: + CCMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + ModemService = true; + return( connected ); + } + + } + else if ( modemstatus == -1 ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + CCMessageBox().Process( TXT_ERROR_IN_INITSTRING ); + ModemService = true; + return( connected ); + } + + /* + ** Completely disable audio. This is required for MWave devices + */ + ThemeType old_theme = THEME_NONE; + if (SoundOn){ + old_theme = Theme.What_Is_Playing(); + Theme.Stop(); + CountDownTimerClass wait; + Call_Back(); + wait.Set (60,true); + while ( wait.Time() ) { + Call_Back(); + } + Sound_End(); + Call_Back(); + wait.Set (60,true); + while ( wait.Time() ) { + Call_Back(); + } + SoundOn = 0; + } + + dialstatus = NullModem.Answer_Modem( reconnect ); + + switch ( dialstatus ) { + case DIAL_CONNECTED: + connected = true; + break; + + case DIAL_NO_CARRIER: + CCMessageBox().Process(TXT_NO_CARRIER); + connected = false; + break; + + case DIAL_BUSY: + CCMessageBox().Process(TXT_LINE_BUSY); + connected = false; + break; + + case DIAL_ERROR: + CCMessageBox().Process(TXT_NUMBER_INVALID); + connected = false; + break; + + case DIAL_NO_DIAL_TONE: + CCMessageBox().Process(TXT_NO_DIAL_TONE); + connected = false; + break; + + case DIAL_CANCELED: + CCMessageBox().Process(TXT_ANSWERING_CANCELED); + connected = false; + break; + } + + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + + /* + ** Restore audio capability + */ + SoundOn = Audio_Init ( MainWindow , 16 , false , 11025*2 , 0 ); + if (SoundOn){ + Theme.Play_Song (old_theme); + } + + ModemService = true; + return( connected ); + +} /* end of Answer_Modem */ + + +static void Modem_Echo( char c ) +{ + if (NullModem.EchoCount < (NullModem.EchoSize - 1) ) { + *(NullModem.EchoBuf + NullModem.EchoCount) = c; + *(NullModem.EchoBuf + NullModem.EchoCount + 1) = 0; + NullModem.EchoCount++; + } else { + //Smart_Printf( "Echo buffer full!!!\n" ); + } + +} /* end of Modem_Echo */ + + +void Smart_Printf( char *format, ... ) +{ + va_list arglist; + char buf[501]; + + + va_start(arglist,format); + vsprintf(buf,format,arglist); + va_end(arglist); + + if (Debug_Smart_Print) { + if (Special.IsMonoEnabled) { +// Mono_Set_Cursor(0,0); + Mono_Printf("%s",buf); + } else { +// Mono_Printf("%s",buf); + printf("%s",buf); + } + } else { + if (Debug_Heap_Dump) { + printf("%s",buf); + } + } +} + + +void Hex_Dump_Data( char *buffer, int length ) +{ + int i; + int offset = 0; + char buff[10]; + char ptr[16]; + char c; + + + while (length >= 16) { + memcpy( ptr, (buffer + offset), 16); + + Smart_Printf("%05lX ", offset); + + for (i = 0; i < 16; i++) { + + c = ptr[i]; + itoh(c, buff); + + if (!(i & 0x3) && i) { + Smart_Printf("³ "); + } + + Smart_Printf("%s ", buff); + } + + Smart_Printf(" "); + + for (i = 0; i < 16; i++) { + c = ptr[i]; + + if (c && ((c < 7) || (c > 11)) && (c != 13)) { + Smart_Printf("%c", c); + } else { + Smart_Printf("."); + } + } + + Smart_Printf("\n"); + + offset += 16; + length -= 16; + } + + if (length) { + memcpy( ptr, (buffer + offset), 16); + + Smart_Printf("%05lX ", offset); + + for (i = 0; i < 16; i++) { + if (i < length) { + c = ptr[i]; + itoh(c, buff); + if (!(i & 0x3) && i) { + Smart_Printf("³ "); + } + Smart_Printf("%s ", buff); + } else { + if (!(i & 0x3) && i) { + Smart_Printf(" "); + } + Smart_Printf(" "); + } + } + + Smart_Printf(" "); + + for (i = 0; i < length; i++) { + + c = ptr[i]; + + if (c && ((c < 7) || (c > 11)) && (c != 13)) { + Smart_Printf("%c", c); + } else { + Smart_Printf("."); + } + } + + Smart_Printf("\n"); + } + +} /* end of Hex_Dump_Data */ + + +void itoh( int i, char *s) +{ + + int nibble, loop; + +// *s++ = '0'; +// *s++ = 'x'; + + if (i == 0) { + *s++ = '0'; + *s++ = '0'; + } else { + for (loop = 1; loop >= 0; loop--) { + nibble = (i >> (loop << 2)) & 0x000F; + + /* decimal range */ + if (nibble < 10) { + *s++ = '0' + nibble; + } else { + *s++ = 'A' + (nibble - 10); + } + } + } + *s = 0; /* null terminate it */ +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#if (0) + +int Com_Fake_Scenario_Dialog(void) +{ + bool display = true; // redraw level + bool process = true; // process while true + + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int transmit; // 1 = re-transmit new game options + int parms_received = 1; // 1 = game options received + int changed = 0; // 1 = user has changed an option + + int rc; + int recsignedoff = false; + int version; + unsigned long starttime; + unsigned long timingtime; + unsigned long lastmsgtime; + unsigned long lastredrawtime; + unsigned long transmittime = 0; + unsigned long theirresponsetime; + int packetlen; + bool oppscorescreen = false; + bool gameoptions = false; + EventClass *event; // event ptr + unsigned long msg_timeout = 60*60; // init to 60 seconds + + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_CANCEL = 100, + }; + + /*........................................................................ + Dialog variables + ........................................................................*/ + TextButtonClass * buttons = 0; + + KeyNumType input; + + int x,y; + int width=0; + int height=0; + char text_buffer[80*3]; + + const char *current_status_string = Text_String(TXT_WINSOCK_CONNECTING); + strcpy(text_buffer, current_status_string); + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + + Format_Window_String(text_buffer, SeenBuff.Get_Height(), width, height); + + width = MAX(width, 50*factor); + width += 40*factor; + height += 60*factor; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8*factor)) >> 1), + y + height - (FontHeight + FontYSpacing + 2*factor) - 5*factor); + + Set_Logic_Page(SeenBuff); + buttons = &cancelbtn; + + buttons->Flag_List_To_Redraw(); + process = true; + + /*........................................................................ + Init other scenario parameters + ........................................................................*/ + Special.IsTGrowth = MPlayerTiberium; + Special.IsTSpread = MPlayerTiberium; + transmit = 1; + + /*........................................................................ + Init random-number generator, & create a seed to be used for all random + numbers from here on out + ........................................................................*/ + randomize(); + //Seed = rand(); + + /* + ---------------------------- Processing loop ----------------------------- + */ + NullModem.Reset_Response_Time(); // clear response time + theirresponsetime = 10000; // initialize to an invalid value + timingtime = lastmsgtime = lastredrawtime = TickCount.Time(); + + + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=true; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + /* + ..................... Draw the background ....................... + */ + Dialog_Box(x, y, width, height); + /* + ....................... Draw the labels ......................... + */ + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(text_buffer, x + 20*factor, y + 25*factor, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + buttons->Draw_All(); + Show_Mouse(); + display = false; + } + + /* + ............................ Process input ............................ + */ + input = buttons->Input(); + switch (input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + + default: + break; + } + + + /*--------------------------------------------------------------------- + If our Transmit flag is set, we need to send out a game option packet. + This message requires an ACK. The first time through the loop, transmit + should be set, so we send out our default options; we'll then send + any changes we make to the defaults. + ---------------------------------------------------------------------*/ + if (transmit && (TickCount.Time() - transmittime) > PACKET_RETRANS_TIME) { + SendPacket.Command = SERIAL_GAME_OPTIONS; + strcpy (SendPacket.Name, MPlayerName); +#ifdef PATCH + if (IsV107) { + SendPacket.Version = 1; + } else { + SendPacket.Version = 2; + } +#else + SendPacket.Version = Version_Number(); +#endif + SendPacket.House = MPlayerHouse; + SendPacket.Color = MPlayerColorIdx; + + SendPacket.Scenario = MPlayerFilenum[ScenarioIdx]; + + SendPacket.Credits = MPlayerCredits; + SendPacket.IsBases = MPlayerBases; + SendPacket.IsTiberium = MPlayerTiberium; + SendPacket.IsGoodies = MPlayerGoodies; + SendPacket.IsGhosties = MPlayerGhosts; + SendPacket.BuildLevel = BuildLevel; + SendPacket.UnitCount = MPlayerUnitCount; + SendPacket.Seed = Seed; + SendPacket.Special = Special; + SendPacket.GameSpeed = Options.GameSpeed; + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + transmittime = TickCount.Time(); + transmit = 0; + } + + // + // send a timing packet if enough time has gone by. + // + if ( (TickCount.Time() - timingtime) > PACKET_TIMING_TIMEOUT) { + SendPacket.Command = SERIAL_TIMING; + SendPacket.ResponseTime = NullModem.Response_Time(); + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 0); + timingtime = TickCount.Time(); + } + + /*--------------------------------------------------------------------- + Check for an incoming message + ---------------------------------------------------------------------*/ + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + + lastmsgtime = TickCount.Time(); + msg_timeout = 600; // reset timeout value to 10 seconds + // (only the 1st time through is 20 seconds) + + + if (ReceivePacket.Command >= SERIAL_CONNECT && + ReceivePacket.Command < SERIAL_LAST_COMMAND && + ReceivePacket.Command != SERIAL_MESSAGE && + ReceivePacket.ID == ModemGameToPlay) { + + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + break; + } + + event = (EventClass *)&ReceivePacket; + if (event->Type <= EventClass::FRAMEINFO) { + if ( (TickCount.Time() - lastredrawtime) > PACKET_REDRAW_TIME) { + lastredrawtime = TickCount.Time(); + oppscorescreen = true; + parms_received = 1; + } + } else { + switch ( ReceivePacket.Command ) { + /*.................................................................. + Sign-off: Give the other machine time to receive my ACK, display a + message, and exit. + ..................................................................*/ + case (SERIAL_SIGN_OFF): + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < 60) + NullModem.Service(); + CCMessageBox().Process(TXT_USER_SIGNED_OFF); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + recsignedoff = true; + break; + + /*.................................................................. + Game Options: Store the other machine's name, color & house; + If they've picked the same color as myself, re-transmit my settings + to force him to choose a different color. (Com_Show_Scenario_Dialog + is responsible for ensuring the colors are different.) + ..................................................................*/ + case (SERIAL_GAME_OPTIONS): + oppscorescreen = false; + gameoptions = true; + strcpy (TheirName, ReceivePacket.Name); + TheirColor = ReceivePacket.Color; + TheirHouse = ReceivePacket.House; + transmit = 1; + + parms_received = 1; + + /*............................................................... + Check the version number of the other system. + ...............................................................*/ +#ifdef PATCH + if (IsV107) { + version = 1; + } else { + version = 2; + } +#else + version = Version_Number(); +#endif + if (ReceivePacket.Version > version) { + CCMessageBox().Process (TXT_YOURGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + } else { + if (ReceivePacket.Version < version) { + CCMessageBox().Process (TXT_DESTGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + } + } + break; + + /*.................................................................. + Incoming message: add to our list + ..................................................................*/ + + // + // get their response time + // + case (SERIAL_TIMING): + oppscorescreen = false; + theirresponsetime = ReceivePacket.ResponseTime; + + if ( !gameoptions ) { + transmit = 1; + }else{ + process = false; + rc = true; + } + break; + + // + // print msg waiting for opponent + // + case (SERIAL_SCORE_SCREEN): + oppscorescreen = true; + parms_received = 1; + break; + + default: + break; + } + } + } + + // if we haven't received a msg for 10 seconds exit + + if ( (TickCount.Time() - lastmsgtime) > msg_timeout) { + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + } + + /*--------------------------------------------------------------------- + Service the connection + ---------------------------------------------------------------------*/ + NullModem.Service(); + + } /* end of while */ + + /*------------------------------------------------------------------------ + Sort player ID's, so we can execute commands in the same order on both + machines. + ------------------------------------------------------------------------*/ + if (rc) { + /*..................................................................... + Set the number of players in this game, and my ID + .....................................................................*/ + MPlayerCount = 2; + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + TheirID = Build_MPlayerID (TheirColor,TheirHouse); + + /*..................................................................... + Store every player's ID in the MPlayerID[] array. This array will + determine the order of event execution, so the ID's must be stored + in the same order on all systems. + .....................................................................*/ + if (TheirID < MPlayerLocalID) { + MPlayerID[0] = TheirID; + MPlayerID[1] = MPlayerLocalID; + strcpy (MPlayerNames[0], TheirName); + strcpy (MPlayerNames[1], MPlayerName); + } else { + MPlayerID[0] = MPlayerLocalID; + MPlayerID[1] = TheirID; + strcpy (MPlayerNames[0], MPlayerName); + strcpy (MPlayerNames[1], TheirName); + } + + /*..................................................................... + Get the scenario filename + .....................................................................*/ + Scenario = MPlayerFilenum[ScenarioIdx]; + + /*..................................................................... + Send all players the GO packet. + .....................................................................*/ + SendPacket.Command = SERIAL_GO; + SendPacket.ResponseTime = NullModem.Response_Time(); + if ( theirresponsetime == 10000 ) { +// Mono_Clear_Screen(); +// Smart_Printf( "Did not receive their response time!!!!!!!\n" ); +// Get_Key(); + } else { + if (SendPacket.ResponseTime < theirresponsetime) { + SendPacket.ResponseTime = theirresponsetime; + } + } + + // + // calculated one way delay for a packet and overall delay to execute + // a packet + // + ////////MPlayerMaxAhead = MAX( (SendPacket.ResponseTime / 8), 2); + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount.Time(); + while ( ( NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_SENDING_TIMEOUT) ) + || ((TickCount.Time() - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + NullModem.Service(); + Keyboard::Check(); //Make sure the message loop gets called + } + + // clear queue to keep from doing any resends + NullModem.Init_Send_Queue(); + + } else { + if ( !recsignedoff ) { + /*..................................................................... + Broadcast my sign-off over my network + .....................................................................*/ + SendPacket.Command = SERIAL_SIGN_OFF; + SendPacket.Color = MPlayerLocalID; // use Color for ID + SendPacket.ID = ModemGameToPlay; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount.Time(); + while ( (NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_CANCEL_TIMEOUT) ) + || ((TickCount.Time() - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + if ( NullModem.Get_Message( &ReceivePacket, &packetlen ) > 0) { + + // are we getting our own packets back?? + + if (ReceivePacket.Command == SERIAL_SIGN_OFF + && ReceivePacket.ID == ModemGameToPlay) { + + // exit while + break; + } + } + + NullModem.Service(); + } + } + + Shutdown_Modem(); + } + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Show_Mouse(); + + /*------------------------------------------------------------------------ + Save any changes made to our options + ------------------------------------------------------------------------*/ + if (changed) { + Write_MultiPlayer_Settings (); + } + + return(rc); + +} /* end of Com_Scenario_Dialog */ + + + + + + + + + + + + + + + + + + + + + + + + + + +int Com_Show_Fake_Scenario_Dialog(void) +{ + /*........................................................................ + Dialog variables + ........................................................................*/ + bool display = true; // redraw level + BOOL process = true; // process while true + + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int transmit; // 1 = re-transmit new game options + int first; // 1 = no packets received yet + int parms_received = 0; // 1 = game options received + int changed = 0; // 1 = user has changed an option + + int rc; + int recsignedoff = 0; + int i; + int version; + unsigned long starttime; + unsigned long timingtime; + unsigned long lastmsgtime; + unsigned long lastredrawtime; + unsigned long transmittime = 0; + int packetlen; + bool oppscorescreen = false; + bool gameoptions = false; + EventClass *event; // event ptr + unsigned long msg_timeout = 60*60; // init to 60 seconds + + + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_CANCEL = 100, + }; + + /*........................................................................ + Dialog variables + ........................................................................*/ + TextButtonClass * buttons = 0; + + KeyNumType input; + + int x,y; + int width=0; + int height=0; + char text_buffer[80*3]; + + const char *current_status_string = Text_String(TXT_WINSOCK_CONNECTING); + strcpy(text_buffer, current_status_string); + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + + Format_Window_String(text_buffer, SeenBuff.Get_Height(), width, height); + + width = MAX(width, 50*factor); + width += 40*factor; + height += 60*factor; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8*factor)) >> 1), + y + height - (FontHeight + FontYSpacing + 2*factor) - 5*factor); + + Set_Logic_Page(SeenBuff); + buttons = &cancelbtn; + + buttons->Flag_List_To_Redraw(); + + + transmit = 1; + first = 1; + + + /* + ---------------------------- Processing loop ----------------------------- + */ + NullModem.Reset_Response_Time(); // clear response time + timingtime = lastmsgtime = lastredrawtime = TickCount.Time(); + + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=true; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + /* + ..................... Draw the background ....................... + */ + Dialog_Box(x, y, width, height); + /* + ....................... Draw the labels ......................... + */ + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(text_buffer, x + 20*factor, y + 25*factor, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + buttons->Draw_All(); + Show_Mouse(); + display = false; + } + + + /* + ............................ Process input ............................ + */ + input = buttons->Input(); + switch (input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + + default: + break; + } + + + + /*--------------------------------------------------------------------- + If our Transmit flag is set, we need to send out a game option packet + ---------------------------------------------------------------------*/ + if (transmit && (TickCount.Time() - transmittime) > PACKET_RETRANS_TIME) { + SendPacket.Command = SERIAL_GAME_OPTIONS; + strcpy (SendPacket.Name, MPlayerName); +#ifdef PATCH + if (IsV107) { + SendPacket.Version = 1; + } else { + SendPacket.Version = 2; + } +#else + SendPacket.Version = Version_Number(); +#endif + SendPacket.House = MPlayerHouse; + SendPacket.Color = MPlayerColorIdx; + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + transmittime = TickCount.Time(); + transmit = 0; + } + + // + // send a timing packet if enough time has gone by. + // + if ( (TickCount.Time() - timingtime) > PACKET_TIMING_TIMEOUT) { + SendPacket.Command = SERIAL_TIMING; + SendPacket.ResponseTime = NullModem.Response_Time(); + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 0); + timingtime = TickCount.Time(); + } + + /*--------------------------------------------------------------------- + Check for an incoming message + ---------------------------------------------------------------------*/ + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + + lastmsgtime = TickCount.Time(); + + msg_timeout = 600; + + // are we getting our own packets back?? + + if (ReceivePacket.Command >= SERIAL_CONNECT && + ReceivePacket.Command < SERIAL_LAST_COMMAND && + ReceivePacket.Command != SERIAL_MESSAGE && + ReceivePacket.ID == ModemGameToPlay) { + + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + break; + } + + event = (EventClass *)&ReceivePacket; + if (event->Type <= EventClass::FRAMEINFO) { + if ( (TickCount.Time() - lastredrawtime) > PACKET_REDRAW_TIME) { + lastredrawtime = TickCount.Time(); + oppscorescreen = true; + display = false; + parms_received = 1; + } + } else { + switch ( ReceivePacket.Command ) { + /*.................................................................. + Other system signs off: Give it time to receive my ACK, then show + a message. + ..................................................................*/ + case (SERIAL_SIGN_OFF): + starttime = TickCount.Time(); + while ( (TickCount.Time() - starttime) < 60) + NullModem.Service(); + CCMessageBox().Process(TXT_USER_SIGNED_OFF); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + recsignedoff = true; + break; + + /*.................................................................. + Game Options: Store all options; check my color & game version. + ..................................................................*/ + case (SERIAL_GAME_OPTIONS): + oppscorescreen = false; + gameoptions = true; + display = false; + parms_received = 1; + + strcpy (TheirName, ReceivePacket.Name); + TheirColor = ReceivePacket.Color; + TheirHouse = ReceivePacket.House; + + /*............................................................... + Save scenario settings. + ...............................................................*/ + MPlayerCredits = ReceivePacket.Credits; + MPlayerBases = ReceivePacket.IsBases; + MPlayerTiberium = ReceivePacket.IsTiberium; + MPlayerGoodies = ReceivePacket.IsGoodies; + MPlayerGhosts = ReceivePacket.IsGhosties; + BuildLevel = ReceivePacket.BuildLevel; + MPlayerUnitCount = ReceivePacket.UnitCount; + Seed = ReceivePacket.Seed; + Special = ReceivePacket.Special; + Options.GameSpeed = ReceivePacket.GameSpeed; + + if (MPlayerTiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + + /*............................................................... + Find the index of the scenario number; if it's not found, leave + it at -1. + ...............................................................*/ + ScenarioIdx = -1; + for (i = 0; i < MPlayerFilenum.Count(); i++) { + if (ReceivePacket.Scenario == MPlayerFilenum[i]) + ScenarioIdx = i; + } + + /*............................................................... + Check our version numbers; if they're incompatible, sign off. + ...............................................................*/ +#ifdef PATCH + if (IsV107) { + version = 1; + } else { + version = 2; + } +#else + version = Version_Number(); +#endif + if (ReceivePacket.Version > version) { + CCMessageBox().Process (TXT_YOURGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + } else { + if (ReceivePacket.Version < version) { + CCMessageBox().Process (TXT_DESTGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + } + } + + /*............................................................... + If this is the first game-options packet we've received, transmit + our options to him. + ...............................................................*/ + if (first) { + first = 0; + + // force transmitting of game options packet + + transmit = 1; + transmittime = 0; + } + break; + + /*.................................................................. + GO: Exit this routine with a success code. + ..................................................................*/ + case (SERIAL_GO): + + // + // calculated one way delay for a packet and overall delay + // to execute a packet + // + ////////MPlayerMaxAhead = MAX( (ReceivePacket.ResponseTime / 8), 2); + + process = false; + rc = true; + break; + + // + // throw away timing packet + // + case (SERIAL_TIMING): + oppscorescreen = false; + break; + + // + // print msg waiting for opponent + // + case (SERIAL_SCORE_SCREEN): +// Smart_Printf( "received score screen\n" ); + oppscorescreen = true; + display = false; + parms_received = 1; + break; + + default: + break; + } + } + } + + // if we haven't received a msg for 10 seconds exit + + if ( (TickCount.Time() - lastmsgtime) > msg_timeout) { + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + } + + + /*--------------------------------------------------------------------- + Service the connection + ---------------------------------------------------------------------*/ + NullModem.Service(); + + } /* end of while */ + + /*------------------------------------------------------------------------ + Sort player ID's, so we can execute commands in the same order on both + machines. + ------------------------------------------------------------------------*/ + if (rc) { + /*..................................................................... + Set the number of players in this game, and my ID + .....................................................................*/ + MPlayerCount = 2; + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + TheirID = Build_MPlayerID (TheirColor,TheirHouse); + + /*..................................................................... + Store every player's ID in the MPlayerID[] array. This array will + determine the order of event execution, so the ID's must be stored + in the same order on all systems. + .....................................................................*/ + if (TheirID < MPlayerLocalID) { + MPlayerID[0] = TheirID; + MPlayerID[1] = MPlayerLocalID; + strcpy (MPlayerNames[0], TheirName); + strcpy (MPlayerNames[1], MPlayerName); + } else { + MPlayerID[0] = MPlayerLocalID; + MPlayerID[1] = TheirID; + strcpy (MPlayerNames[0], MPlayerName); + strcpy (MPlayerNames[1], TheirName); + } + + /*..................................................................... + Get the scenario filename + .....................................................................*/ + Scenario = MPlayerFilenum[ScenarioIdx]; + + starttime = TickCount.Time(); + while ( ( NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_SENDING_TIMEOUT) ) + || ((TickCount.Time() - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + NullModem.Service(); + Keyboard::Check(); //Make sure the message loop gets called + } + + // clear queue to keep from doing any resends + NullModem.Init_Send_Queue(); + + } else { + if ( !recsignedoff ) { + /*..................................................................... + Broadcast my sign-off over my network + .....................................................................*/ + SendPacket.Command = SERIAL_SIGN_OFF; + SendPacket.Color = MPlayerLocalID; // use Color for ID + SendPacket.ID = ModemGameToPlay; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount.Time(); + while ( (NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_CANCEL_TIMEOUT) ) + || ((TickCount.Time() - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + if ( NullModem.Get_Message( &ReceivePacket, &packetlen ) > 0) { + + // are we getting our own packets back?? + + if (ReceivePacket.Command == SERIAL_SIGN_OFF + && ReceivePacket.ID == ModemGameToPlay) { + + // exit while + break; + } + } + + NullModem.Service(); + } + } + + Shutdown_Modem(); + } + + /*------------------------------------------------------------------------ + Save any changes made to our options + ------------------------------------------------------------------------*/ + if (changed) { + Write_MultiPlayer_Settings (); + } + + return(rc); + +} /* end of Com_Show_Scenario_Dialog */ + + +#endif //(0) + + + + + + + + + + + diff --git a/NULLDLG.CPP.BAK b/NULLDLG.CPP.BAK new file mode 100644 index 0000000..e2aff58 --- /dev/null +++ b/NULLDLG.CPP.BAK @@ -0,0 +1,7580 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\nulldlg.cpv 1.9 16 Oct 1995 16:52:12 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NULLDLG.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 04/29/95 * + * * + * Last Update : April 29, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Init_Null_Modem -- Initializes Null Modem communications * + * Shutdown_Modem -- Shuts down modem/null-modem communications * + * Test_Null_Modem -- Null-Modem test routine * + * Reconnect_Null_Modem -- allows user to reconnect * + * Destroy_Null_Connection -- destroys the given connection * + * Select_Serial_Dialog -- Serial Communications menu dialog * + * Com_Settings_Dialog -- Lets user select serial port settings * + * Com_Scenario_Dialog -- Serial game scenario selection dialog * + * Phone_Dialog -- Lets user edit phone directory & dial * + * Build_InitString_Listbox -- [re]builds the initstring entry listbox * + * Init_String_Compare -- for qsort * + * Build_Phone_Listbox -- [re]builds the phone entry listbox * + * Phone_Compare -- for qsort * + * Edit_Phone_Dialog -- lets user edit a phone book entry * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "function.h" +#include "wincomm.h" +#include "tcpip.h" + +// +// how much time (ticks) to go by before thinking other system +// is not responding. +// +#define PACKET_SENDING_TIMEOUT 1800 +#define PACKET_CANCEL_TIMEOUT 900 + +// +// how much time (ticks) to go by before sending another packet +// of game options or serial connect. +// +#define PACKET_RETRANS_TIME 30 +#define PACKET_REDRAW_TIME 60 + + +static int Reconnect_Null_Modem( void ); +static int Com_Settings_Dialog( SerialSettingsType *settings ); +static int Phone_Dialog (void); +static void Build_Init_String_Listbox (ListClass *list, EditClass *edit, char *buf, int *index); +static int Init_String_Compare (const void *p1, const void *p2); +static void Build_Phone_Listbox (ListClass *list, EditClass *edit, char *buf); +static int Phone_Compare (const void *p1, const void *p2); +static int Edit_Phone_Dialog (PhoneEntryClass *phone); +static bool Dial_Modem( SerialSettingsType *settings, bool reconnect ); +static bool Answer_Modem( SerialSettingsType *settings, bool reconnect ); +static void Modem_Echo( char c ); + +void Smart_Printf( char *format, ... ); +void Hex_Dump_Data( char *buffer, int length ); +void itoh( int i, char *s); + + +static SerialPacketType SendPacket; +static SerialPacketType ReceivePacket; +char TheirName[MPLAYER_NAME_MAX]; +unsigned char TheirColor; +HousesType TheirHouse; +unsigned char TheirID; +static char DialString[ CWAITSTRBUF_MAX + PhoneEntryClass::PHONE_MAX_NUM - 1 ]; +static SerialSettingsType *DialSettings; + +#define SHOW_MONO 0 + +/*************************************************************************** + * Init_Null_Modem -- Initializes Null Modem communications * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = OK, false = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +int Init_Null_Modem( SerialSettingsType *settings ) +{ + + if ( NullModem.Init( settings->Port, settings->IRQ, + settings->Baud, 0, 8, 1 ) ) { +// settings->Baud, 'N', 8, 1 ) ) { + +// NullModem.Change_IRQ_Priority( settings->IRQ ); + + return(true); + } else { + return(false); + } +} + + +/*************************************************************************** + * Shutdown_Modem -- Shuts down modem/null-modem communications * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +void Shutdown_Modem( void ) +{ + if (!PlaybackGame) { + if (GameToPlay == GAME_MODEM) { + NullModem.Hangup_Modem(); + } + } + + //NullModem.Change_IRQ_Priority( 0 ); // reset priority of interrupts + + // + // close port + // + NullModem.Shutdown(); +} + + +/*************************************************************************** + * Modem_Signoff -- sends EXIT event * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/03/1995 DRD : Created. * + *=========================================================================*/ +void Modem_Signoff( void ) +{ + unsigned long starttime; + EventClass event; + + if (!PlaybackGame) { + /*------------------------------------------------------------------------ + Send a sign-off packet + ------------------------------------------------------------------------*/ + event.Type = EventClass::EXIT; + NullModem.Send_Message (&event,sizeof(EventClass),0); + NullModem.Send_Message (&event,sizeof(EventClass),0); + + starttime = TickCount.Time(); + while( (TickCount.Time() - starttime) > 30) { + NullModem.Service(); + } + } +} + + +/*************************************************************************** + * Test_Null_Modem -- Null-Modem test routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = failure to connect; 1 = I'm the game owner, 2 = I'm not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +int Test_Null_Modem( void ) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_CANCEL = 100, + }; + + /*........................................................................ + Dialog variables + ........................................................................*/ + bool process = true; // process while true + KeyNumType input; + + int retval; + unsigned long starttime; + int packetlen; + + int x,y,width,height; // dialog dimensions + char buffer[80*3]; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + strcpy( buffer, Text_String( TXT_WAITING_CONNECT ) ); + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(buffer, 200*factor, width, height); + + width = MAX(width, 50*factor); + width += 40*factor; + height += 60*factor; + + x = (320*factor - width) / 2; + y = (200*factor - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8 *factor)) >> 1), + y + height - (FontHeight + FontYSpacing + 2*factor) - 5*factor); + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + process = true; + + /* + ............................ Create the list ............................. + */ + commands = &cancelbtn; + + commands->Flag_List_To_Redraw(); + + /* + ............................ Draw the dialog ............................. + */ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(buffer, x + 20*factor, y + 25*factor, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + commands->Draw_All(); + while (Get_Mouse_State() > 0) Show_Mouse(); + + /* + ** This is supposed to be a direct connection so hang up any modem on this port + ** just to annoy British Telecom + */ + /* + ** Go into break mode + */ + SetCommBreak(SerialPort->Get_Port_Handle()); + + /* + ** Send hangup command + */ + SerialPort->Write_To_Serial_Port ((unsigned char*)"ATH\r", strlen("ATH\r")); + CountDownTimerClass time; + time.Set(2*60); + while (time.Time()){} + + /* + ** Back out of break mode + */ + ClearCommBreak(SerialPort->Get_Port_Handle()); + + /* + ** Drop DTR as well - just in case the modem still hasnt got the message + */ + EscapeCommFunction(SerialPort->Get_Port_Handle(), CLRDTR); + + + /*------------------------------------------------------------------------ + Check for a packet. If we detect one, the other system has already been + started. Wait 1/2 sec for him to receive my ACK, then exit with success. + Note: The initial time must be a little longer than the resend delay. + Just in case we just missed the packet. + ------------------------------------------------------------------------*/ + starttime = TickCount.Time(); + while ( TickCount.Time() - starttime < 80) { + NullModem.Service(); + if ( NullModem.Get_Message( &ReceivePacket, &packetlen ) > 0) { + if (ReceivePacket.Command == SERIAL_CONNECT) { + //Smart_Printf( "Received SERIAL_CONNECT %d, ID %d \n", ReceivePacket.Seed, ReceivePacket.ID ); + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < 30) + NullModem.Service(); + process = false; + retval = 2; + break; + } + } + } + + /*------------------------------------------------------------------------ + Send a packet across. As long as Num_Send() is non-zero, the other system + hasn't received it yet. + ------------------------------------------------------------------------*/ + if (process) { + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_CONNECT; + // + // put time from start of game for determining the host in case of tie. + // + SendPacket.Seed = TickCount.Time(); + SendPacket.ID = (int) buffer; // address of buffer for more uniqueness. + + //Smart_Printf( "Sending SERIAL_CONNECT %d, ID %d \n", SendPacket.Seed, SendPacket.ID ); + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < 80) { + NullModem.Service(); + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + if (ReceivePacket.Command == SERIAL_CONNECT) { + //Smart_Printf( "Received2 SERIAL_CONNECT %d, ID %d \n", ReceivePacket.Seed, ReceivePacket.ID ); + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < 30) + NullModem.Service(); + + // + // whoever has the highest time is the host + // + if (ReceivePacket.Seed > SendPacket.Seed) { + process = false; + retval = 2; + } else { + if (ReceivePacket.Seed == SendPacket.Seed) { + if (ReceivePacket.ID > SendPacket.ID) { + process = false; + retval = 2; + } else + // + // if they are equal then it's a loopback cable or a modem + // + if (ReceivePacket.ID == SendPacket.ID) { + process = false; + retval = 3; + } + } + } + + break; + } + } + } + } + + starttime = TickCount.Time(); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + commands->Draw_All(); + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + //Smart_Printf( "Canceled waiting for SERIAL_CONNECT\n" ); + retval = 0; + process = false; + break; + + default: + break; + } + /*..................................................................... + Service the connection. + .....................................................................*/ + NullModem.Service(); + if (NullModem.Num_Send() == 0) { + //Smart_Printf( "No more messages to send.\n" ); + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + if (ReceivePacket.Command == SERIAL_CONNECT) { + //Smart_Printf( "Received3 SERIAL_CONNECT %d, ID %d \n", ReceivePacket.Seed, ReceivePacket.ID ); + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < 30) + NullModem.Service(); + + // + // whoever has the highest time is the host + // + if (ReceivePacket.Seed > SendPacket.Seed) { + process = false; + retval = 2; + } else { + + if (ReceivePacket.Seed == SendPacket.Seed) { + if (ReceivePacket.ID > SendPacket.ID) { + process = false; + retval = 2; + } else { + + // + // if they are equal then it's a loopback cable or a modem + // + if (ReceivePacket.ID == SendPacket.ID) { + process = false; + retval = 3; + } + } + } + } + + } else { + retval = 0; + process = false; + } + } else { + retval = 1; + process = false; + } + } + + if (TickCount.Time() - starttime > 3600) { // only wait 1 minute + retval = 0; + process = false; + } + } /* end of while */ + + return( retval ); + +} + + +/*************************************************************************** + * Reconnect_Modem -- allows user to reconnect * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = failure to connect; 1 = connect OK * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +int Reconnect_Modem( void ) +{ + int status; + int modemstatus; + + + switch (ModemGameToPlay) { + case (MODEM_NULL_HOST): + case (MODEM_NULL_JOIN): + status = Reconnect_Null_Modem(); + break; + + case (MODEM_DIALER): + modemstatus = NullModem.Get_Modem_Status(); + if ( (modemstatus & CD_SET) ) { + //Smart_Printf( "Dial Modem connection error! Attempting reconnect....\n" ); + status = Reconnect_Null_Modem(); + } else { + status = Dial_Modem( DialSettings, true ); + } + break; + + case (MODEM_ANSWERER): + modemstatus = NullModem.Get_Modem_Status(); + if ( (modemstatus & CD_SET) ) { + //Smart_Printf( "Answer Modem connection error! Attempting reconnect....\n" ); + status = Reconnect_Null_Modem(); + } else { + status = Answer_Modem( DialSettings, true ); + } + break; + } + + return( status ); +} + + +/*************************************************************************** + * Reconnect_Null_Modem -- allows user to reconnect * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = failure to connect; 1 = connect OK * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static int Reconnect_Null_Modem( void ) +{ + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_CANCEL = 100, + }; + + /*........................................................................ + Dialog variables + ........................................................................*/ + bool process = true; // process while true + KeyNumType input; + + int retval; + unsigned long starttime; + unsigned long lastmsgtime; + int packetlen; + + int x,y,width,height; // dialog dimensions + char buffer[80*3]; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + strcpy( buffer, Text_String( TXT_NULL_CONNERR_CHECK_CABLES ) ); + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(buffer, 200, width, height); + + width = MAX(width, 50); + width += 40; + height += 60; + + x = (320 - width) / 2; + y = (200 - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8)) >> 1), + y + height - (FontHeight + FontYSpacing + 2) - 5); + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + process = true; + + /* + ............................ Create the list ............................. + */ + commands = &cancelbtn; + + commands->Flag_List_To_Redraw(); + + /* + ............................ Draw the dialog ............................. + */ + Hide_Mouse(); + + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(buffer, x + 20, y + 25, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + commands->Draw_All(); + Show_Mouse(); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + starttime = lastmsgtime = TickCount.Time(); + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + commands->Draw_All(); + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + retval = false; + process = false; + break; + + default: + break; + } + /*..................................................................... + Service the connection. + .....................................................................*/ + NullModem.Service(); + + /*..................................................................... + Resend our message if it's time + .....................................................................*/ + if (TickCount.Time() - starttime > PACKET_RETRANS_TIME) { + starttime = TickCount.Time(); + SendPacket.Command = SERIAL_CONNECT; + SendPacket.ID = MPlayerLocalID; + //Smart_Printf( "Sending a SERIAL_CONNECT packet !!!!!!!!\n" ); + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 0); + } + + /*..................................................................... + Check for an incoming message + .....................................................................*/ + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + + lastmsgtime = TickCount.Time(); + + if (ReceivePacket.Command == SERIAL_CONNECT) { + //Smart_Printf( "Received a SERIAL_CONNECT packet !!!!!!!!\n" ); + + // are we getting our own packets back?? + + if (ReceivePacket.ID == MPlayerLocalID) { + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + retval = false; + process = false; + break; + } + + /*............................................................... + OK, we got our message; now we have to make certain the other + guy gets his, so send him one with an ACK required. + ...............................................................*/ + SendPacket.Command = SERIAL_CONNECT; + SendPacket.ID = MPlayerLocalID; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < 60) + NullModem.Service(); + retval = true; + process = false; + } + } + + // + // timeout if we do not get any packets + // + if (TickCount.Time() - lastmsgtime > PACKET_CANCEL_TIMEOUT) { + retval = false; + process = false; + } + + } /* end of while */ + + return( retval ); + +} + + +/*********************************************************************************************** + * Destroy_Null_Connection -- destroys the given connection * + * * + * Call this routine when a connection goes bad, or another player signs off. * + * * + * INPUT: * + * id connection ID to destroy * + * error 0 = user signed off; 1 = connection error * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/31/1995 DRD : Created. * + *=============================================================================================*/ +void Destroy_Null_Connection(int id, int error) +{ + int i,j,idx; + HousesType house; + HouseClass *housep; + char txt[80]; + + + if ( MPlayerCount == 1 ) { + return; + } + + // find index for id + + idx = -1; + for (i = 0; i < MPlayerCount; i++) { + if (MPlayerID[i] == (unsigned char)id) { + idx = i; + break; + } + } + + if (idx == -1) { + return; + } + + /*------------------------------------------------------------------------ + Create a message to display to the user + ------------------------------------------------------------------------*/ + txt[0] = '\0'; + if (error == 1) { + sprintf(txt,Text_String(TXT_CONNECTION_LOST), MPlayerNames[idx] ); + } + else if (error == 0) { + sprintf(txt,Text_String(TXT_LEFT_GAME), MPlayerNames[idx] ); + } + else if (error == -1) { + NullModem.Delete_Connection(); + } + + if (strlen(txt)) { + Messages.Add_Message (txt, + MPlayerTColors[MPlayerID_To_ColorIndex((unsigned char)id)], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, 0, 0); + Map.Flag_To_Redraw(false); + } + + for (i = 0; i < MPlayerCount; i++) { + if (MPlayerID[i] == (unsigned char)id) { + /*.................................................................. + Turn the player's house over to the computer's AI + ..................................................................*/ + house = MPlayerHouses[i]; + housep = HouseClass::As_Pointer (house); + housep->IsHuman = false; + + /*.................................................................. + Move arrays back by one + ..................................................................*/ + for (j = i; j < MPlayerCount - 1; j++) { + MPlayerID[j] = MPlayerID[j + 1]; + MPlayerHouses[j] = MPlayerHouses[j + 1]; + strcpy (MPlayerNames[j], MPlayerNames[j+1]); + TheirProcessTime[j] = TheirProcessTime[j+1]; + } + } + } + + MPlayerCount--; + + /*------------------------------------------------------------------------ + If we're the last player left, tell the user. + ------------------------------------------------------------------------*/ + if (MPlayerCount == 1) { + sprintf(txt,"%s",Text_String(TXT_JUST_YOU_AND_ME)); + Messages.Add_Message (txt, + MPlayerTColors[MPlayerID_To_ColorIndex((unsigned char)id)], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, 0, 0); + Map.Flag_To_Redraw(false); + } + +} /* end of Destroy_Null_Connection */ + + +/*************************************************************************** + * Select_Serial_Dialog -- Serial Communications menu dialog * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * GAME_MODEM user wants to play a modem game * + * GAME_NULL_MODEM user wants to play a null-modem game * + * GAME_NORMAL user hit Cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +GameType Select_Serial_Dialog( void ) +{ + int rc; +// int value, i; + int com = -1, baud = -1; + int error = 0; + + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 160 *factor; // dialog width + int d_dialog_h = 94 *factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord +// D_DIALOG_Y = ((200 - D_DIALOG_H) / 2), // dialog y-coord + int d_dialog_y = ((136*factor - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 11*factor; // ht of 6-pt text + int d_margin = 7; // margin width/height + + int d_dial_w = 90 *factor; + int d_dial_h = 9 *factor; + int d_dial_x = d_dialog_cx - d_dial_w / 2; + int d_dial_y = d_dialog_y + d_margin + d_txt6_h + d_margin; + + int d_answer_w = 90 *factor; + int d_answer_h = 9 *factor; + int d_answer_x = d_dialog_cx - d_answer_w / 2; + int d_answer_y = d_dial_y + d_dial_h + 2; + + int d_nullmodem_w = 90 *factor; + int d_nullmodem_h = 9 *factor; + int d_nullmodem_x = d_dialog_cx - d_nullmodem_w / 2; + int d_nullmodem_y = d_answer_y + d_answer_h + 2; + + int d_settings_w = 90 *factor; + int d_settings_h = 9 *factor; + int d_settings_x = d_dialog_cx - d_settings_w / 2; + int d_settings_y = d_nullmodem_y + d_nullmodem_h + 2; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50 *factor; +#else + int d_cancel_w = 40 *factor; +#endif + int d_cancel_h = 9 *factor; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_settings_y + d_settings_h + d_margin; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_DIAL = 100, + BUTTON_ANSWER, + BUTTON_NULLMODEM, + BUTTON_SETTINGS, + BUTTON_CANCEL, + + NUM_OF_BUTTONS = 5, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int tabs[] = {77}; // tabs for player list box + GameType retval; // return value + + int selection; + bool pressed; + int curbutton; + TextButtonClass *buttons[NUM_OF_BUTTONS]; + + SerialSettingsType *settings; + bool selectsettings = false; + + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + TextButtonClass dialbtn(BUTTON_DIAL, TXT_DIAL_MODEM, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_dial_x, d_dial_y, d_dial_w, d_dial_h); + + TextButtonClass answerbtn(BUTTON_ANSWER, TXT_ANSWER_MODEM, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_answer_x, d_answer_y, d_answer_w, d_answer_h); + + TextButtonClass nullmodembtn(BUTTON_NULLMODEM, TXT_NULL_MODEM, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_nullmodem_x, d_nullmodem_y, d_nullmodem_w, d_nullmodem_h); + + TextButtonClass settingsbtn(BUTTON_SETTINGS, TXT_SETTINGS, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_settings_x, d_settings_y, d_settings_w, d_settings_h); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + d_cancel_x, d_cancel_y); +#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +#endif + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + /*........................................................................ + Read the CC.INI file to extract default serial settings, scenario numbers + & descriptions, and the phone list. + ........................................................................*/ + Read_MultiPlayer_Settings (); + + if (SerialDefaults.Port == 0 || + SerialDefaults.IRQ == -1 || + SerialDefaults.Baud == -1) { + selectsettings = true; + } else { + if ( NullModem.Detect_Port( &SerialDefaults ) != PORT_VALID ) { + selectsettings = true; + } + } + + /* + ............................ Create the list ............................. + */ + commands = &dialbtn; + answerbtn.Add_Tail(*commands); + nullmodembtn.Add_Tail(*commands); + settingsbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + + /* + ......................... Fill array of button ptrs ...................... + */ + curbutton = 0; + buttons[0] = &dialbtn; + buttons[1] = &answerbtn; + buttons[2] = &nullmodembtn; + buttons[3] = &settingsbtn; + buttons[4] = &cancelbtn; + buttons[curbutton]->Turn_On(); + + Keyboard::Clear(); + + Fancy_Text_Print(TXT_NONE, 0, 0, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + +Debug_Smart_Print = true; + + MPlayerLocalID = 0xff; // set to invalid value + + /* + -------------------------- Main Processing Loop -------------------------- + */ + display = REDRAW_ALL; + process = true; + pressed = false; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + /* + ..................... Refresh the backdrop ...................... + */ + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + /* + ..................... Draw the background ....................... + */ + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /* + ..................... Redraw the buttons ....................... + */ + commands->Draw_All(); + /* + ....................... Draw the labels ......................... + */ + Draw_Caption (TXT_SELECT_SERIAL_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ............................ Process input ............................ + */ + switch (input) { + case (BUTTON_DIAL | KN_BUTTON): + selection = BUTTON_DIAL; + pressed = true; + break; + + case (BUTTON_ANSWER | KN_BUTTON): + selection = BUTTON_ANSWER; + pressed = true; + break; + + case (BUTTON_NULLMODEM | KN_BUTTON): + selection = BUTTON_NULLMODEM; + pressed = true; + break; + + case (BUTTON_SETTINGS | KN_BUTTON): + selection = BUTTON_SETTINGS; + pressed = true; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + selection = BUTTON_CANCEL; + pressed = true; + break; + + case (KN_UP): + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton--; + if (curbutton < 0) + curbutton = (NUM_OF_BUTTONS - 1); + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case (KN_DOWN): + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton++; + if (curbutton > (NUM_OF_BUTTONS - 1) ) + curbutton = 0; + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_DIAL; + pressed = true; + break; + + default: + break; + } + + if (pressed) { + // + // to make sure the selection is correct in case they used the mouse + // + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton = selection - BUTTON_DIAL; + buttons[curbutton]->Turn_On(); + buttons[curbutton]->IsPressed = true; + buttons[curbutton]->Draw_Me(true); + + switch (selection) { + case (BUTTON_DIAL): + + if (selectsettings) { + CCMessageBox().Process(TXT_SELECT_SETTINGS); + } + + /* + ** Remote-connect + */ + else if ( Phone_Dialog() ) { + if (PhoneBook[CurPhoneIdx]->Settings.Port == 0) { + settings = &SerialDefaults; + } else { + settings = &(PhoneBook[CurPhoneIdx]->Settings); + } + + if (SerialPort){ + delete SerialPort; + } + SerialPort = new WinModemClass; + + if ( Init_Null_Modem( settings ) ) { + + if (settings->CallWaitStringIndex == CALL_WAIT_CUSTOM) { + strcpy( DialString, settings->CallWaitString ); + } else { + strcpy( DialString, + CallWaitStrings[ settings->CallWaitStringIndex ] ); + } + strcat( DialString, PhoneBook[ CurPhoneIdx ]->Number ); + + if ( Dial_Modem( settings, false ) ) { + ModemGameToPlay = MODEM_DIALER; + if ( Com_Scenario_Dialog() ) { + retval = GAME_MODEM; + process = false; + } + } + + if (process) { // restore to default + NullModem.Change_IRQ_Priority( 0 ); + } + } else { + CCMessageBox().Process(TXT_SELECT_SETTINGS); + } + } + + if (process) { + buttons[curbutton]->IsPressed = false; + buttons[curbutton]->Flag_To_Redraw(); + } + + display = REDRAW_ALL; + break; + + case (BUTTON_ANSWER): + + if (selectsettings) { + CCMessageBox().Process(TXT_SELECT_SETTINGS); + } else { + /* + ** Remote-connect + */ + settings = &SerialDefaults; + + if (SerialPort){ + delete SerialPort; + } + SerialPort = new WinModemClass; + + if ( Init_Null_Modem( settings ) ) { + if ( Answer_Modem( settings, false ) ) { + ModemGameToPlay = MODEM_ANSWERER; + if ( Com_Show_Scenario_Dialog() ) { + retval = GAME_MODEM; + process = false; + } + } + + if (process) { // restore to default + NullModem.Change_IRQ_Priority( 0 ); + } + } else { + CCMessageBox().Process(TXT_SELECT_SETTINGS); + } + } + + if (process) { + buttons[curbutton]->IsPressed = false; + buttons[curbutton]->Flag_To_Redraw(); + } + + display = REDRAW_ALL; + break; + + case (BUTTON_NULLMODEM): + + if (selectsettings) { + CCMessageBox().Process(TXT_SELECT_SETTINGS); + } else { + /* + ** Otherwise, remote-connect; save values if we're recording + */ + + if (SerialPort){ + delete SerialPort; + } + SerialPort = new WinNullModemClass; + + if ( Init_Null_Modem( &SerialDefaults ) ) { + rc = Test_Null_Modem(); + switch (rc) { + case (1): + ModemGameToPlay = MODEM_NULL_HOST; + if ( Com_Scenario_Dialog() ) { + retval = GAME_NULL_MODEM; + process = false; + } + break; + + case (2): + ModemGameToPlay = MODEM_NULL_JOIN; + if ( Com_Show_Scenario_Dialog() ) { + retval = GAME_NULL_MODEM; + process = false; + } + break; + + case (3): + CCMessageBox().Process( TXT_MODEM_OR_LOOPBACK ); + break; + } + + if (process) { // restore to default + NullModem.Change_IRQ_Priority( 0 ); + } + } else { + CCMessageBox().Process(TXT_SELECT_SETTINGS); + } + } + + if (process) { + buttons[curbutton]->IsPressed = false; + buttons[curbutton]->Flag_To_Redraw(); + } + + display = REDRAW_ALL; + break; + + case (BUTTON_SETTINGS): + if ( Com_Settings_Dialog( &SerialDefaults ) ) { + Write_MultiPlayer_Settings (); + + selectsettings = true; + + if (SerialDefaults.Port != 0 && + SerialDefaults.IRQ != -1 && + SerialDefaults.Baud != -1) { + if ( NullModem.Detect_Port( &SerialDefaults ) == PORT_VALID) { + selectsettings = false; + } + } + } + + buttons[curbutton]->IsPressed = false; + buttons[curbutton]->Flag_To_Redraw(); + display = REDRAW_ALL; + break; + + case (BUTTON_CANCEL): + retval = GAME_NORMAL; + process = false; + break; + } + + pressed = false; + } + } /* end of while */ + +#if 0 + if (retval == GAME_NORMAL) { + Debug_Smart_Print = false; + } +#endif + +Debug_Smart_Print = false; + + return( retval ); +} + + +/*************************************************************************** + * Com_Settings_Dialog -- Lets user select serial port settings * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Settings ³ * + * ³ ³ * + * ³ Port:____ IRQ:__ Baud:______ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ³ * + * ³ Initialization: [Add] [Delete] ³ * + * ³ _____________________________ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ³ * + * ³ Call Waiting: ³ * + * ³ _______________ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ [Tone Dialing] ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ [Pulse Dialing] ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * settings ptr to SerialSettingsType structure * + * * + * OUTPUT: * + * true = OK, false = Cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static int Com_Settings_Dialog( SerialSettingsType *settings ) +{ +/* ###Change collision detected! C:\PROJECTS\CODE\NULLDLG.CPP... */ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 301 *factor; // dialog width + int d_dialog_h = 200 *factor; // dialog height + int d_dialog_x = ((320 *factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200 *factor - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6 *factor +1; // ht of 6-pt text + int d_margin = 5 *factor; // margin width/height + +#ifdef EDIT_IRQ + int d_portlist_w = 80 *factor; + int d_portlist_h = 35 *factor; + int d_portlist_x = d_dialog_x + (d_dialog_w / 6) - (d_portlist_w / 2); + int d_portlist_y = d_dialog_y + ((d_margin + d_txt6_h) * 2) + d_margin + 10 *factor; + + int d_port_w = ((PORTBUF_MAX - 1) * 6 *factor) + 4 *factor; + int d_port_h = 9 *factor; + int d_port_x = d_portlist_x + 31 *factor; + int d_port_y = d_portlist_y - d_margin - d_txt6_h; + + + int d_irqlist_w = 80 *factor; + int d_irqlist_h = 35 *factor; + int d_irqlist_x = d_dialog_x + (d_dialog_w / 2) - (d_irqlist_w / 2); + int d_irqlist_y = d_portlist_y; + + int d_irq_w = ((IRQBUF_MAX - 1) * 6 *factor) + 3 *factor; + int d_irq_h = 9 *factor; + int d_irq_x = d_irqlist_x + 25 *factor; + int d_irq_y = d_irqlist_y - d_margin - d_txt6_h; + + int d_baudlist_w = 80 *factor; + int d_baudlist_h = 35 *factor; + int d_baudlist_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_baudlist_w / 2); + int d_baudlist_y = d_portlist_y; + + int d_baud_w = ((BAUDBUF_MAX - 1) * 6 *factor) + 3 *factor; + int d_baud_h = 9 *factor; + int d_baud_x = d_baudlist_x + 31 *factor; + int d_baud_y = d_baudlist_y - d_margin - d_txt6_h; + +#endif //EDIT_IRQ + int d_initstrlist_w = ((INITSTRBUF_MAX - 1) * 6 *factor) + 8 + 3 *factor; + int d_initstrlist_h = 21 *factor; + int d_initstrlist_x = d_dialog_cx - (d_initstrlist_w / 2); + int d_initstrlist_y = d_dialog_y + ((d_margin + d_txt6_h) * 2) + d_margin + 10 *factor + + 35*factor + + ((d_margin + d_txt6_h) * 2) + d_margin + 4 *factor; + + int d_initstr_w = ((INITSTRBUF_MAX - 1) * 6 *factor) + 3 *factor; + int d_initstr_h = 9 *factor; + int d_initstr_x = d_initstrlist_x; + int d_initstr_y = d_initstrlist_y - d_margin - d_txt6_h; + +#ifndef EDIT_IRQ + int d_portlist_w = 80 *factor; + int d_portlist_h = 35 *factor; + int d_portlist_x = d_initstrlist_x; + int d_portlist_y = d_dialog_y + ((d_margin + d_txt6_h) * 2) + d_margin + 10 *factor; + + int d_port_w = ((PORTBUF_MAX - 1) * 6 *factor) + 4 *factor; + int d_port_h = 9 *factor; + int d_port_x = d_portlist_x + 31 *factor; + int d_port_y = d_portlist_y - d_margin - d_txt6_h; + + int d_baudlist_w = 80 *factor; + int d_baudlist_h = 35 *factor; + int d_baudlist_x = d_portlist_x + d_portlist_w + 20 * factor; + int d_baudlist_y = d_portlist_y; + + int d_baud_w = ((BAUDBUF_MAX - 1) * 6 *factor) + 3 *factor; + int d_baud_h = 9 *factor; + int d_baud_x = d_baudlist_x + 31 *factor; + int d_baud_y = d_baudlist_y - d_margin - d_txt6_h; + + int d_inittype_w = 30*factor; + int d_inittype_h = 9*factor; + int d_inittype_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_inittype_w / 2); + int d_inittype_y = d_baud_y + 20*factor; + +#endif //EDIT_IRQ + + int d_add_w = 45 *factor; + int d_add_h = 9 *factor; +#ifdef FRENCH + int d_add_x = (d_dialog_cx - (d_add_w / 2))+34*factor; +#else + int d_add_x = d_dialog_cx - (d_add_w / 2); +#endif + int d_add_y = d_initstr_y - d_add_h - 3*factor; + + int d_delete_w = 45 *factor; + int d_delete_h = 9 *factor; + +#ifdef FRENCH + int d_delete_x = 14*factor + d_dialog_x + ((d_dialog_w * 3) / 4) - (d_delete_w / 2); +#else + int d_delete_x = d_dialog_x + ((d_dialog_w * 3) / 4) - (d_delete_w / 2); +#endif + int d_delete_y = d_initstr_y - d_add_h - 3 *factor; + + int d_cwaitstrlist_w = (((CWAITSTRBUF_MAX - 1) + 9) * 6 *factor) + 3 *factor; + int d_cwaitstrlist_h = 27 *factor; + int d_cwaitstrlist_x = d_initstrlist_x; + int d_cwaitstrlist_y = d_initstrlist_y + d_initstrlist_h + ((d_margin + d_txt6_h) * 2) + 2 *factor; + + int d_cwaitstr_w = ((CWAITSTRBUF_MAX - 1) * 6 *factor) + 3 *factor; + int d_cwaitstr_h = 9 *factor; + int d_cwaitstr_x = d_cwaitstrlist_x; + int d_cwaitstr_y = d_cwaitstrlist_y - d_margin - d_txt6_h; + + int d_tone_w = 80 *factor; + int d_tone_h = 9 *factor; + int d_tone_x = d_dialog_x + ((d_dialog_w * 3) / 4) - (d_tone_w / 2); + int d_tone_y = d_cwaitstrlist_y; + + int d_pulse_w = 80 *factor; + int d_pulse_h = 9 *factor; + int d_pulse_x = d_dialog_x + ((d_dialog_w * 3) / 4) - (d_pulse_w / 2); + int d_pulse_y = d_tone_y + d_tone_h + d_margin; + + int d_save_w = 40 *factor; + int d_save_h = 9 *factor; + int d_save_x = d_dialog_x + (d_dialog_w / 3) - (d_save_w / 2); + int d_save_y = d_dialog_y + d_dialog_h - d_save_h - d_margin - 2 *factor; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50 *factor; +#else + int d_cancel_w = 40 *factor; +#endif + int d_cancel_h = 9 *factor; + int d_cancel_x = d_dialog_x + ((d_dialog_w * 2) / 3) - (d_cancel_w / 2); + int d_cancel_y = d_dialog_y + d_dialog_h - d_cancel_h - d_margin - 2 *factor; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_PORT = 100, + BUTTON_PORTLIST, + BUTTON_IRQ, + BUTTON_IRQLIST, + BUTTON_BAUD, + BUTTON_BAUDLIST, + BUTTON_INITSTR, + BUTTON_INITSTRLIST, + BUTTON_ADD, + BUTTON_DELETE, + BUTTON_CWAITSTR, + BUTTON_CWAITSTRLIST, + BUTTON_TONE, + BUTTON_PULSE, + BUTTON_SAVE, + BUTTON_INITTYPE, + BUTTON_CANCEL, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + static char *portname[5] = { + "COM1 - 3F8", + "COM2 - 2F8", + "COM3 - 3E8", + "COM4 - 2E8", + "CUSTOM - ????" + }; + +#ifdef EDIT_IRQ + static char *irqname[5] = { + "2 / 9", + "3 - [COM2 & 4]", + "4 - [COM1 & 3]", + "5", + "CUSTOM - ??" + }; + + static int _irqidx[4] = { + 2, + 1, + 2, + 1 + }; +#endif // EDIT_IRQ + + static char *baudname[5] = { + "14400", + "19200", + "28800", + "38400", + "57600", + }; + + static char *init_types[2] = { + "Normal", + "Full", + }; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + BOOL process = true; // process while true + KeyNumType input; + char * item; // general-purpose string + char * temp; // general-purpose string + + char portbuf[ PORTBUF_MAX ] = {0}; // buffer for port +#ifdef EDIT_IRQ + char irqbuf[ IRQBUF_MAX ] = {0}; // buffer for irq +#endif //EDIT_IRQ + char baudbuf[ BAUDBUF_MAX ] = {0}; // buffer for baud + char initstrbuf[ INITSTRBUF_MAX ] = {0}; // buffer for init string + char cwaitstrbuf[ CWAITSTRBUF_MAX ] = {0}; // buffer for call waiting string + + int port_index = 1; // index of currently-selected port (default = com2) +#ifdef EDIT_IRQ + int irq_index = 1; // index of currently-selected irq (default = 3) +#endif //EDIT_IRQ + int baud_index = 1; // index of currently-selected baud (default = 19200) + int initstr_index = 0; // index of currently-selected modem init (default = "ATZ") + int cwaitstr_index = CALL_WAIT_CUSTOM; // index of currently-selected call waiting (default = "") + int rc = 0; // -1 = user cancelled, 1 = New + int i; // loop counter + int pos; + int len; + int firsttime = 1; + SerialSettingsType tempsettings; + DetectPortType dpstatus; + char init_text[32]; + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + EditClass port_edt (BUTTON_PORT, + portbuf, PORTBUF_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_port_x, d_port_y, d_port_w, d_port_h, EditClass::ALPHANUMERIC); + + ListClass portlist(BUTTON_PORTLIST, + d_portlist_x, d_portlist_y, d_portlist_w, d_portlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + +#ifdef EDIT_IRQ + EditClass irq_edt (BUTTON_IRQ, + irqbuf, IRQBUF_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_irq_x, d_irq_y, d_irq_w, d_irq_h, EditClass::NUMERIC); + + ListClass irqlist(BUTTON_IRQLIST, + d_irqlist_x, d_irqlist_y, d_irqlist_w, d_irqlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); +#endif //EDIT_IRQ + + EditClass baud_edt (BUTTON_BAUD, + baudbuf, BAUDBUF_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_baud_x, d_baud_y, d_baud_w, d_baud_h, EditClass::NUMERIC); + + ListClass baudlist(BUTTON_BAUDLIST, + d_baudlist_x, d_baudlist_y, d_baudlist_w, d_baudlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + EditClass initstr_edt (BUTTON_INITSTR, + initstrbuf, INITSTRBUF_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_initstr_x, d_initstr_y, d_initstr_w, d_initstr_h, EditClass::ALPHANUMERIC); + + ListClass initstrlist(BUTTON_INITSTRLIST, + d_initstrlist_x, d_initstrlist_y, d_initstrlist_w, d_initstrlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + TextButtonClass addbtn(BUTTON_ADD, TXT_ADD, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#ifdef FRENCH + d_add_x, d_add_y); +#else + d_add_x, d_add_y, d_add_w, d_add_h); +#endif + + TextButtonClass deletebtn(BUTTON_DELETE, TXT_DELETE_BUTTON, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#ifdef FRENCH + d_delete_x, d_delete_y); +#else + d_delete_x, d_delete_y, d_delete_w, d_delete_h); +#endif + + EditClass cwaitstr_edt (BUTTON_CWAITSTR, + cwaitstrbuf, CWAITSTRBUF_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_cwaitstr_x, d_cwaitstr_y, d_cwaitstr_w, d_cwaitstr_h, EditClass::ALPHANUMERIC); + + ListClass cwaitstrlist(BUTTON_CWAITSTRLIST, + d_cwaitstrlist_x, d_cwaitstrlist_y, d_cwaitstrlist_w, d_cwaitstrlist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + TextButtonClass tonebtn(BUTTON_TONE, TXT_TONE_BUTTON, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_tone_x, d_tone_y, d_tone_w, d_tone_h); + + TextButtonClass pulsebtn(BUTTON_PULSE, TXT_PULSE_BUTTON, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_pulse_x, d_pulse_y, d_pulse_w, d_pulse_h); + + TextButtonClass savebtn(BUTTON_SAVE, TXT_SAVE_BUTTON, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + d_save_x, d_save_y); +#else + d_save_x, d_save_y, d_save_w, d_save_h); +#endif + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + d_cancel_x, d_cancel_y); +#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +#endif +#if (0) + TextButtonClass inittypebutton(BUTTON_INITTYPE, init_text, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_inittype_x, d_inittype_y, d_inittype_w, d_inittype_h); +#endif //(0) + + /* + ----------------------------- Various Inits ------------------------------ + */ + memcpy( &tempsettings, settings, sizeof(SerialSettingsType) ); + + strcpy (init_text, init_types[tempsettings.Init]); + + if (tempsettings.Port == 0) { + tempsettings.Port = 0x2f8; + } + + if (tempsettings.IRQ == -1) { + tempsettings.IRQ = 3; + } + + if (tempsettings.Baud == -1) { + tempsettings.Baud = 19200; + } + + /*........................................................................ + Set the current indices + ........................................................................*/ + switch ( tempsettings.Port ) { + case ( 0x3f8 ): + port_index = 0; + strcpy (portbuf, "COM1"); + break; + + case ( 0x2f8 ): + port_index = 1; + strcpy (portbuf, "COM2"); + break; + + case ( 0x3e8 ): + port_index = 2; + strcpy (portbuf, "COM3"); + break; + + case ( 0x2e8 ): + port_index = 3; + strcpy (portbuf, "COM4"); + break; + + default: + port_index = 4; + sprintf (portbuf, "%x", tempsettings.Port); + temp = strchr( portname[4], '-' ); + if ( temp ) { + pos = (int)(temp - portname[4]) + 2; + len = strlen( portbuf ); + strncpy( portname[4] + pos, portbuf, len ); + *(portname[4] + pos + len) = 0; + } + break; + } + +#ifdef EDIT_IRQ + switch ( tempsettings.IRQ ) { + case ( 2 ): + irq_index = 0; + strcpy (irqbuf, "2"); + break; + + case ( 3 ): + irq_index = 1; + strcpy (irqbuf, "3"); + break; + + case ( 4 ): + irq_index = 2; + strcpy (irqbuf, "4"); + break; + + case ( 5 ): + irq_index = 3; + strcpy (irqbuf, "5"); + break; + + default: + irq_index = 4; + sprintf (irqbuf, "%d", tempsettings.IRQ); + temp = strchr( irqname[4], '-' ); + if ( temp ) { + pos = (int)(temp - irqname[4]) + 2; + len = strlen( irqbuf ); + strncpy( irqname[4] + pos, irqbuf, len ); + *(irqname[4] + pos + len) = 0; + } + break; + } +#endif //EDIT_IRQ + + if (tempsettings.Baud == 14400) { + baud_index = 0; + } else { + if (tempsettings.Baud == 19200) { + baud_index = 1; + } else { + if (tempsettings.Baud == 28800) { + baud_index = 2; + } else { + if (tempsettings.Baud == 38400) { + baud_index = 3; + } else { + baud_index = 4; + } + } + } + } + + sprintf (baudbuf, "%d", tempsettings.Baud); + + /*........................................................................ + Set up the port list box & edit box + ........................................................................*/ + for (i = 0; i < 5; i++) { + portlist.Add_Item( portname[ i ] ); + } + + portlist.Set_Selected_Index( port_index ); + port_edt.Set_Text( portbuf, PORTBUF_MAX ); + + /*........................................................................ + Set up the IRQ list box & edit box + ........................................................................*/ +#ifdef EDIT_IRQ + for (i = 0; i < 5; i++) { + irqlist.Add_Item( irqname[ i ] ); + } + + irqlist.Set_Selected_Index( irq_index ); + irq_edt.Set_Text( irqbuf, IRQBUF_MAX ); +#endif //EDIT_IRQ + + /*........................................................................ + Set up the baud rate list box & edit box + ........................................................................*/ + for (i = 0; i < 5; i++) { + baudlist.Add_Item( baudname[ i ] ); + } + + baudlist.Set_Selected_Index( baud_index ); + baud_edt.Set_Text( baudbuf, BAUDBUF_MAX ); + + initstr_index = tempsettings.InitStringIndex; + Build_Init_String_Listbox(&initstrlist, &initstr_edt, initstrbuf, &initstr_index); + + /*........................................................................ + Set up the cwait rate list box & edit box + ........................................................................*/ + + cwaitstr_index = tempsettings.CallWaitStringIndex; + for (i = 0; i < CALL_WAIT_STRINGS_NUM; i++) { + if ( i == CALL_WAIT_CUSTOM ) { + item = CallWaitStrings[ i ]; + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + len = strlen( tempsettings.CallWaitString ); + strncpy( item + pos, tempsettings.CallWaitString, len ); + *(item + pos + len) = 0; + if (i == cwaitstr_index) { + strncpy( cwaitstrbuf, item + pos, CWAITSTRBUF_MAX ); + } + } + } else { + if (i == cwaitstr_index) { + strncpy( cwaitstrbuf, CallWaitStrings[ i ], CWAITSTRBUF_MAX ); + } + } + cwaitstrlist.Add_Item( CallWaitStrings[ i ] ); + } + + cwaitstrlist.Set_Selected_Index( cwaitstr_index ); + cwaitstr_edt.Set_Text( cwaitstrbuf, CWAITSTRBUF_MAX ); + + /*........................................................................ + Build the button list + ........................................................................*/ + commands = &cancelbtn; + port_edt.Add_Tail(*commands); + portlist.Add_Tail(*commands); +#ifdef EDIT_IRQ + irq_edt.Add_Tail(*commands); + irqlist.Add_Tail(*commands); +#endif // EDIT_IRQ + baud_edt.Add_Tail(*commands); + baudlist.Add_Tail(*commands); + //inittypebutton.Add_Tail(*commands); + initstr_edt.Add_Tail(*commands); + initstrlist.Add_Tail(*commands); + addbtn.Add_Tail(*commands); + deletebtn.Add_Tail(*commands); + cwaitstr_edt.Add_Tail(*commands); + cwaitstrlist.Add_Tail(*commands); + tonebtn.Add_Tail(*commands); + pulsebtn.Add_Tail(*commands); + savebtn.Add_Tail(*commands); + + if (tempsettings.DialMethod == DIAL_TOUCH_TONE) { + tonebtn.Turn_On(); + } else { + pulsebtn.Turn_On(); + } + /* + ---------------------------- Processing loop ----------------------------- + */ + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_SETTINGS, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print( TXT_PORT_COLON, + d_port_x - 3, d_port_y + 1 *factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + +#ifdef EDIT_IRQ + Fancy_Text_Print( TXT_IRQ_COLON, + d_irq_x - 3, d_irq_y + 1 *factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); +#endif //EDIT_IRQ + + Fancy_Text_Print( TXT_BAUD_COLON, + d_baud_x - 3, d_baud_y + 1 *factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print( TXT_INIT_STRING, + d_initstr_x, d_initstr_y - d_txt6_h - 3 *factor, + CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print( TXT_CWAIT_STRING, + d_cwaitstr_x, d_cwaitstr_y - d_txt6_h - 3 *factor, + CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); +#if (0) + Fancy_Text_Print ( "Modem Init", + d_inittype_x, d_inittype_y - d_txt6_h - d_margin, + CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); +#endif //(0) + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + cancelbtn.Flag_To_Redraw(); + port_edt.Flag_To_Redraw(); + portlist.Flag_To_Redraw(); +#ifdef EDIT_IRQ + irq_edt.Flag_To_Redraw(); + irqlist.Flag_To_Redraw(); +#endif // EDIT_IRQ + baud_edt.Flag_To_Redraw(); + baudlist.Flag_To_Redraw(); + //inittypebutton.Flag_To_Redraw(); + initstr_edt.Flag_To_Redraw(); + initstrlist.Flag_To_Redraw(); + addbtn.Flag_To_Redraw(); + deletebtn.Flag_To_Redraw(); + cwaitstr_edt.Flag_To_Redraw(); + cwaitstrlist.Flag_To_Redraw(); + tonebtn.Flag_To_Redraw(); + pulsebtn.Flag_To_Redraw(); + savebtn.Flag_To_Redraw(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + if ( firsttime ) { + port_edt.Set_Focus(); + port_edt.Flag_To_Redraw(); + input = commands->Input(); + firsttime = 0; + } + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { +#if (0) + case (BUTTON_INITTYPE | KN_BUTTON): + tempsettings.Init = !tempsettings.Init; + strcpy (init_text, init_types[tempsettings.Init]); + inittypebutton.Flag_To_Redraw(); + break; +#endif //(0) + case (BUTTON_PORT | KN_BUTTON): + item = (char *)portlist.Current_Item(); + if (port_index < 4) { + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( portbuf, item, PORTBUF_MAX ); + } else { + pos = (int)(temp - item); + strncpy( portbuf, item, pos ); + portbuf[pos] = 0; + } + port_edt.Set_Text( portbuf, PORTBUF_MAX ); + port_edt.Flag_To_Redraw(); +#ifdef EDIT_IRQ + irq_edt.Set_Focus(); + irq_edt.Flag_To_Redraw(); +#endif //EDIT_IRQ + } else { + strupr( portbuf ); + if ( stricmp(portbuf, "3F8") == 0 ) { + port_index = 0; + portlist.Set_Selected_Index( port_index ); + strcpy (portbuf, "COM1"); + display = REDRAW_BUTTONS; + } + else if ( stricmp(portbuf, "2F8") == 0 ) { + port_index = 1; + portlist.Set_Selected_Index( port_index ); + strcpy (portbuf, "COM2"); + display = REDRAW_BUTTONS; + } + else if ( stricmp(portbuf, "3E8") == 0 ) { + port_index = 2; + portlist.Set_Selected_Index( port_index ); + strcpy (portbuf, "COM3"); + display = REDRAW_BUTTONS; + } + else if ( stricmp(portbuf, "2E8") == 0 ) { + port_index = 3; + portlist.Set_Selected_Index( port_index ); + strcpy (portbuf, "COM4"); + display = REDRAW_BUTTONS; + } + else if ( strncmp(portbuf, "COM", 3) == 0 ) { + display = REDRAW_BUTTONS; + + switch ( (portbuf[3] - '0') ) { + case 1: + port_index = 0; + break; + + case 2: + port_index = 1; + break; + + case 3: + port_index = 2; + break; + + case 4: + port_index = 3; + break; + + default: + CCMessageBox().Process( TXT_INVALID_PORT_ADDRESS ); + port_edt.Set_Focus(); + display = REDRAW_ALL; + break; + } + + portlist.Set_Selected_Index( port_index ); + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + len = strlen( portbuf ); + strncpy( item + pos, portbuf, len ); + *(item + pos + len) = 0; + display = REDRAW_BUTTONS; + } + } + +#ifdef EDIT_IRQ + if (display == REDRAW_BUTTONS) { + irq_edt.Set_Focus(); + irq_edt.Flag_To_Redraw(); + } +#endif //EDIT_IRQ + } + break; + + case (BUTTON_PORTLIST | KN_BUTTON): + if (portlist.Current_Index() != port_index) { + port_index = portlist.Current_Index(); + item = (char *)portlist.Current_Item(); + if (port_index < 4) { + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( portbuf, item, PORTBUF_MAX ); + } else { + pos = (int)(temp - item); + strncpy( portbuf, item, pos ); + portbuf[pos] = 0; + } + port_edt.Clear_Focus(); + + // auto select the irq for port + +#ifdef EDIT_IRQ + irq_index = _irqidx[ port_index ]; + irqlist.Set_Selected_Index( irq_index ); + item = (char *)irqlist.Current_Item(); + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( irqbuf, item, 2 ); + } else { + pos = (int)(temp - item); + strncpy( irqbuf, item, pos ); + irqbuf[pos] = 0; + } + irq_edt.Clear_Focus(); +#endif //EDIT_IRQ + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + if ( *(item + pos) == '?' ) { + portbuf[0] = 0; + } else { + strncpy( portbuf, item + pos, PORTBUF_MAX ); + } + } + port_edt.Set_Focus(); + } + port_edt.Set_Text( portbuf, PORTBUF_MAX ); + display = REDRAW_BUTTONS; + } else { + if (port_index < 4) { + port_edt.Clear_Focus(); + } else { + port_edt.Set_Focus(); + } + display = REDRAW_BUTTONS; + } + break; + +#ifdef EDIT_IRQ + case (BUTTON_IRQ | KN_BUTTON): + item = (char *)irqlist.Current_Item(); + if (irq_index < 4) { + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( irqbuf, item, IRQBUF_MAX ); + } else { + pos = (int)(temp - item); + strncpy( irqbuf, item, pos ); + irqbuf[pos] = 0; + } + irq_edt.Set_Text( irqbuf, IRQBUF_MAX ); + irq_edt.Flag_To_Redraw(); + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + len = strlen( irqbuf ); + strncpy( item + pos, irqbuf, len ); + *(item + pos + len) = 0; + display = REDRAW_BUTTONS; + } + } + baud_edt.Set_Focus(); + baud_edt.Flag_To_Redraw(); + break; + + case (BUTTON_IRQLIST | KN_BUTTON): + if (irqlist.Current_Index() != irq_index) { + irq_index = irqlist.Current_Index(); + item = (char *)irqlist.Current_Item(); + if (irq_index < 4) { + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( irqbuf, item, IRQBUF_MAX ); + } else { + pos = (int)(temp - item); + strncpy( irqbuf, item, pos ); + irqbuf[pos] = 0; + } + irq_edt.Clear_Focus(); + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + if ( *(item + pos) == '?' ) { + irqbuf[0] = 0; + } else { + strncpy( irqbuf, item + pos, IRQBUF_MAX ); + } + } + irq_edt.Set_Focus(); + } + irq_edt.Set_Text( irqbuf, IRQBUF_MAX ); + } else { + if (irq_index < 4) { + irq_edt.Clear_Focus(); + } else { + irq_edt.Set_Focus(); + } + } + display = REDRAW_BUTTONS; + break; +#endif //EDIT_IRQ + + case (BUTTON_BAUD | KN_BUTTON): + item = (char *)baudlist.Current_Item(); + strncpy( baudbuf, item, BAUDBUF_MAX ); + baud_edt.Set_Text( baudbuf, BAUDBUF_MAX ); + initstr_edt.Set_Focus(); + initstr_edt.Flag_To_Redraw(); + display = REDRAW_BUTTONS; + break; + + case (BUTTON_BAUDLIST | KN_BUTTON): + if (baudlist.Current_Index() != baud_index) { + baud_index = baudlist.Current_Index(); + item = (char *)baudlist.Current_Item(); + strncpy( baudbuf, item, BAUDBUF_MAX ); + baud_edt.Set_Text( baudbuf, BAUDBUF_MAX ); + baud_edt.Clear_Focus(); + display = REDRAW_BUTTONS; + } + break; + +#if 0 + case (BUTTON_INITSTR | KN_BUTTON): + strupr( initstrbuf ); + strncpy( InitStrings[ initstr_index ], initstrbuf, INITSTRBUF_MAX ); + Build_Init_String_Listbox(&initstrlist, &initstr_edt, initstrbuf, + &initstr_index); + cwaitstr_edt.Set_Focus(); + cwaitstr_edt.Flag_To_Redraw(); + display = REDRAW_BUTTONS; + break; +#endif + + case (BUTTON_INITSTRLIST | KN_BUTTON): + if (initstrlist.Current_Index() != initstr_index) { + initstr_index = initstrlist.Current_Index(); + item = (char *)initstrlist.Current_Item(); + strncpy( initstrbuf, item, INITSTRBUF_MAX ); + initstr_edt.Set_Text( initstrbuf, INITSTRBUF_MAX ); + } + initstr_edt.Set_Focus(); + initstr_edt.Flag_To_Redraw(); + display = REDRAW_BUTTONS; + break; + + /*------------------------------------------------------------------ + Add a new InitString entry + ------------------------------------------------------------------*/ + case (BUTTON_ADD | KN_BUTTON): + + item = new char[ INITSTRBUF_MAX ]; + memset (item, 0, INITSTRBUF_MAX); + + strupr ( initstrbuf ); + strncpy ( item, initstrbuf, INITSTRBUF_MAX-1 ); + + InitStrings.Add ( item ); + Build_Init_String_Listbox (&initstrlist, &initstr_edt, initstrbuf, + &initstr_index); + /*............................................................ + Set the current listbox index to the newly-added item. + ............................................................*/ + for (i = 0; i < InitStrings.Count(); i++) { + if (item == InitStrings[i]) { + initstr_index = i; + strcpy( initstrbuf, InitStrings[ initstr_index ] ); + initstr_edt.Set_Text( initstrbuf, INITSTRBUF_MAX ); + initstrlist.Set_Selected_Index( initstr_index ); + } + } + initstr_edt.Set_Focus(); + initstr_edt.Flag_To_Redraw(); + display = REDRAW_BUTTONS; + break; + + /*------------------------------------------------------------------ + Delete the current InitString entry + ------------------------------------------------------------------*/ + case (BUTTON_DELETE | KN_BUTTON): + + if ( InitStrings.Count() && initstr_index != -1) { + InitStrings.Delete( initstr_index ); + Build_Init_String_Listbox(&initstrlist, &initstr_edt, initstrbuf, + &initstr_index); + } + break; + + case (BUTTON_CWAITSTR | KN_BUTTON): + item = (char *)cwaitstrlist.Current_Item(); + if (cwaitstr_index < 3) { + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + len = strlen( cwaitstrbuf ); + strncpy( item + pos, cwaitstrbuf, len ); + *(item + pos + len) = 0; + display = REDRAW_BUTTONS; + } + } + break; + + case (BUTTON_CWAITSTRLIST | KN_BUTTON): + if (cwaitstrlist.Current_Index() != cwaitstr_index) { + cwaitstr_index = cwaitstrlist.Current_Index(); + item = (char *)cwaitstrlist.Current_Item(); + if (cwaitstr_index < 3) { + strncpy( cwaitstrbuf, item, CWAITSTRBUF_MAX ); + cwaitstr_edt.Clear_Focus(); + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + strncpy( cwaitstrbuf, item + pos, CWAITSTRBUF_MAX ); + } + cwaitstr_edt.Set_Focus(); + } + cwaitstr_edt.Set_Text( cwaitstrbuf, CWAITSTRBUF_MAX ); + } else { + if (cwaitstr_index < 3) { + cwaitstr_edt.Clear_Focus(); + } else { + cwaitstr_edt.Set_Focus(); + } + } + display = REDRAW_BUTTONS; + break; + + case (BUTTON_TONE | KN_BUTTON): + tempsettings.DialMethod = DIAL_TOUCH_TONE; + tonebtn.Turn_On(); + pulsebtn.Turn_Off(); + break; + + case (BUTTON_PULSE | KN_BUTTON): + tempsettings.DialMethod = DIAL_PULSE; + tonebtn.Turn_Off(); + pulsebtn.Turn_On(); + break; + + /*------------------------------------------------------------------ + SAVE: save the com settings + ------------------------------------------------------------------*/ + case (KN_RETURN): + case (BUTTON_SAVE | KN_BUTTON): + switch (port_index) { + case ( 0 ): + tempsettings.Port = 0x3f8; + break; + + case ( 1 ): + tempsettings.Port = 0x2f8; + break; + + case ( 2 ): + tempsettings.Port = 0x3e8; + break; + + case ( 3 ): + tempsettings.Port = 0x2e8; + break; + + default: + sscanf( portbuf, "%x", &tempsettings.Port ); + break; + } + +#ifdef EDIT_IRQ + switch (irq_index) { + case ( 0 ): + tempsettings.IRQ = 2; + break; + + case ( 1 ): + tempsettings.IRQ = 3; + break; + + case ( 2 ): + tempsettings.IRQ = 4; + break; + + case ( 3 ): + tempsettings.IRQ = 5; + break; + + default: + sscanf( irqbuf, "%d", &tempsettings.IRQ ); + break; + } +#endif //EDIT_IRQ + + sscanf( baudbuf, "%d", &tempsettings.Baud ); + + tempsettings.InitStringIndex = initstr_index; + tempsettings.CallWaitStringIndex = cwaitstr_index; + + item = CallWaitStrings[ CALL_WAIT_CUSTOM ]; + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + strncpy( cwaitstrbuf, item + pos, CWAITSTRBUF_MAX ); + } else { + cwaitstrbuf[ 0 ] = 0; + } + + strncpy( tempsettings.CallWaitString, cwaitstrbuf, CWAITSTRBUF_MAX ); + + dpstatus = NullModem.Detect_Port( &tempsettings ); + + if (dpstatus == PORT_VALID) { + process = false; + rc = true; + } + else if (dpstatus == PORT_INVALID) { + CCMessageBox().Process( TXT_INVALID_SETTINGS ); + firsttime = 1; + display = REDRAW_ALL; + } + else if (dpstatus == PORT_IRQ_INUSE) { + CCMessageBox().Process( TXT_IRQ_ALREADY_IN_USE ); + firsttime = 1; + display = REDRAW_ALL; + } + break; + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF, bail out with error code + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + } + + } /* end of while */ + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Show_Mouse(); + + /*------------------------------------------------------------------------ + Save values into the Settings structure + ------------------------------------------------------------------------*/ + if (rc) { + memcpy( settings, &tempsettings, sizeof(SerialSettingsType) ); + } + + return(rc); + +} /* end of Com_Settings_Dialog */ + + +/*************************************************************************** + * Build_Init_String_Listbox -- [re]builds the initstring listbox * + * * + * This routine rebuilds the initstring list box from scratch; it also * + * updates the contents of the initstring edit field. * + * * + * INPUT: * + * list ptr to list box * + * edit ptr to edit box * + * buf ptr to buffer for initstring * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/08/1995 DRD : Created. * + *=========================================================================*/ +static void Build_Init_String_Listbox (ListClass *list, EditClass *edit, char *buf, int *index) +{ + int i, curidx; + char *item; + + + curidx = *index; + + /*........................................................................ + Clear the list + ........................................................................*/ + while (list->Count()) { + item = (char *)(list->Get_Item(0)); + list->Remove_Item(item); + delete [] item; + } + + /* + ** Now sort the init string list by name then number + */ + qsort ((void *)(&InitStrings[0]), InitStrings.Count(), sizeof(char *), Init_String_Compare); + + /*........................................................................ + Build the list + ........................................................................*/ + for (i = 0; i < InitStrings.Count(); i++) { + item = new char[ INITSTRBUF_MAX ]; + strcpy( item, InitStrings[i] ); + list->Add_Item(item); + } + list->Flag_To_Redraw(); + + /*........................................................................ + Init the current phone book index + ........................................................................*/ + if (list->Count() == 0 || curidx < -1) { + curidx = -1; + } else { + if (curidx >= list->Count() ) { + curidx = 0; + } + } + + /*........................................................................ + Fill in initstring edit buffer + ........................................................................*/ + if (curidx > -1) { + strcpy (buf, InitStrings[ curidx ]); + edit->Set_Text (buf, INITSTRBUF_MAX ); + list->Set_Selected_Index( curidx ); + } + + *index = curidx; +} + + +/*************************************************************************** + * Init_String_Compare -- for qsort * + * * + * INPUT: * + * p1,p2 ptrs to elements to compare * + * * + * OUTPUT: * + * 0 = same, -1 = (*p1) goes BEFORE (*p2), 1 = (*p1) goes AFTER (*p2) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/08/1995 DRD : Created. * + *=========================================================================*/ +static int Init_String_Compare (const void *p1, const void *p2) +{ + return( strcmp( *((char **)p1), *((char **)p2) ) ); +} + + +/*********************************************************************************************** + * Com_Scenario_Dialog -- Serial game scenario selection dialog * + * * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Serial Game ³ * + * ³ ³ * + * ³ Your Name: __________ House: [GDI] [NOD] ³ * + * ³ Credits: ______ Desired Color: [ ][ ][ ][ ] ³ * + * ³ Opponent: Name ³ * + * ³ ³ * + * ³ Scenario ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³ Hell's Kitchen ³³ ³ * + * ³ ³ Heaven's Gate ÃÄ´ ³ * + * ³ ³ ... ³ ³ ³ * + * ³ ³ ÃÄ´ ³ * + * ³ ³ ³³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ [ Bases ] [ Crates ] ³ * + * ³ [ Tiberium ] [ AI Players ] ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ [Send Message] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = success, false = cancel * + * * + * WARNINGS: * + * MPlayerName & MPlayerGameName must contain this player's name. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +#define TXT_HOST_INTERNET_GAME 4567+1 +#define TXT_JOIN_INTERNET_GAME 4567+2 +int Com_Scenario_Dialog(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 290*factor; // dialog width + int d_dialog_h = 190*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6*factor+1; // ht of 6-pt text + int d_margin1 = 5*factor; // margin width/height + int d_margin2 = 2*factor; // margin width/height + + int d_name_w = 70*factor; + int d_name_h = 9*factor; + int d_name_x = d_dialog_x + 108*factor; + int d_name_y = d_dialog_y + d_margin1 + d_txt6_h + d_txt6_h + d_margin1; + + int d_credits_w = ((CREDITSBUF_MAX - 1) * 6*factor) + 3*factor; + int d_credits_h = 9*factor; + int d_credits_x = d_name_x; + int d_credits_y = d_name_y + d_name_h + d_margin2; + + int d_gdi_w = 30*factor; + int d_gdi_h = 9*factor; + int d_gdi_x = d_dialog_cx + (d_dialog_w / 4); + int d_gdi_y = d_dialog_y + d_margin1 + d_txt6_h + d_txt6_h + d_margin1; + + int d_nod_w = 30*factor; + int d_nod_h = 9*factor; + int d_nod_x = d_gdi_x + d_gdi_w + (d_margin1 / 2); + int d_nod_y = d_gdi_y; + + int d_color_w = 10*factor; + int d_color_h = 9*factor; + int d_color_y = d_gdi_y + d_gdi_h + d_margin2; + + int d_opponent_x = d_name_x; + int d_opponent_y = d_color_y + d_color_h + d_margin2; + + int d_scenariolist_w = 182*factor; + int d_scenariolist_h = 27*factor; + int d_scenariolist_x = d_dialog_cx - (d_scenariolist_w / 2); + int d_scenariolist_y = d_opponent_y + d_txt6_h + 3*factor + d_txt6_h; + + // d_count_x is calculated below after other enums + int d_count_w = 25*factor; + int d_count_h = 7*factor; + int d_count_y = d_scenariolist_y + d_scenariolist_h + d_margin2; + + // d_level_x is calculated below after other enums + int d_level_w = 25*factor; + int d_level_h = 7*factor; + int d_level_y = d_count_y; + +#if (GERMAN | FRENCH) + int d_bases_w = 120*factor;//BGA:100; +#else + int d_bases_w = 110*factor; +#endif + int d_bases_h = 9*factor; + int d_bases_x = d_dialog_cx - d_bases_w - d_margin2; + int d_bases_y = d_count_y + d_count_h + d_margin2; + +#if (GERMAN | FRENCH) + int d_goodies_w = 120*factor; +#else + int d_goodies_w = 110*factor; +#endif + int d_goodies_h = 9*factor; + int d_goodies_x = d_dialog_cx + d_margin2; + int d_goodies_y = d_bases_y; + + int d_count_x = d_dialog_cx - d_count_w - ((2 * 6*factor) + 3*factor) + - ((d_bases_w - ((13 * 6*factor) + 3*factor + d_count_w)) / 2) - d_margin2; + + int d_level_x = d_dialog_cx + (11 * 6*factor) + + ((d_goodies_w - ((13 * 6*factor) + 3*factor + d_level_w)) / 2) + d_margin2; + +#if (GERMAN | FRENCH) + int d_tiberium_w = 120*factor; +#else + int d_tiberium_w = 110*factor; +#endif + int d_tiberium_h = 9*factor; + int d_tiberium_x = d_dialog_cx - d_bases_w - d_margin2; + int d_tiberium_y = d_bases_y + d_bases_h + d_margin2; + +#if (GERMAN | FRENCH) + int d_ghosts_w = 120*factor; +#else + int d_ghosts_w = 110*factor; +#endif + int d_ghosts_h = 9*factor; + int d_ghosts_x = d_dialog_cx + d_margin2; + int d_ghosts_y = d_tiberium_y; + + int d_ok_w = 45*factor; + int d_ok_h = 9*factor; + int d_ok_x = d_tiberium_x + (d_tiberium_w / 2) - (d_ok_w / 2); + int d_ok_y = d_tiberium_y + d_tiberium_h + d_margin1; + + int d_cancel_w = 45*factor; + int d_cancel_h = 9*factor; + int d_cancel_x = d_ghosts_x + (d_ghosts_w / 2) - (d_cancel_w / 2); + int d_cancel_y = d_tiberium_y + d_tiberium_h + d_margin1; + + int d_message_w = d_dialog_w - (d_margin1 * 2); + int d_message_h = 34*factor; + int d_message_x = d_dialog_x + d_margin1; + int d_message_y = d_cancel_y + d_cancel_h + d_margin1; + + int d_send_w = 80*factor; + int d_send_h = 9*factor; + int d_send_x = d_dialog_cx - (d_send_w / 2); + int d_send_y = d_message_y + d_message_h + d_margin2; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_NAME = 100, + BUTTON_GDI, + BUTTON_NOD, + BUTTON_CREDITS, + BUTTON_SCENARIOLIST, + BUTTON_COUNT, + BUTTON_LEVEL, + BUTTON_BASES, + BUTTON_TIBERIUM, + BUTTON_GOODIES, + BUTTON_GHOSTS, + BUTTON_OK, + BUTTON_CANCEL, + BUTTON_SEND, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + char credbuf[CREDITSBUF_MAX]; // for credit edit box + int old_cred; // old value in credits buffer + int transmit; // 1 = re-transmit new game options + int cbox_x[] = { d_gdi_x, + d_gdi_x + d_color_w, + d_gdi_x + (d_color_w * 2), + d_gdi_x + (d_color_w * 3), + d_gdi_x + (d_color_w * 4), + d_gdi_x + (d_color_w * 5)}; + int parms_received = 0; // 1 = game options received + int changed = 0; // 1 = user has changed an option + + int rc; + int recsignedoff = false; + int i; + int version; + char txt[80]; + unsigned long starttime; + unsigned long timingtime; + unsigned long lastmsgtime; + unsigned long lastredrawtime; + unsigned long transmittime = 0; + unsigned long theirresponsetime; + int packetlen; + static int first_time = 1; + bool oppscorescreen = false; + bool gameoptions = false; + EventClass *event; // event ptr + unsigned long msg_timeout = 1200; // init to 20 seconds + + int message_length; + int sent_so_far; + unsigned short magic_number; + unsigned short crc; + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + EditClass name_edt (BUTTON_NAME, + namebuf, MPLAYER_NAME_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_name_x, d_name_y, d_name_w, d_name_h, EditClass::ALPHANUMERIC); + + TextButtonClass gdibtn(BUTTON_GDI, TXT_G_D_I, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_gdi_x, d_gdi_y, d_gdi_w, d_gdi_h); + + TextButtonClass nodbtn(BUTTON_NOD, TXT_N_O_D, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_nod_x, d_nod_y, d_nod_w, d_nod_h); + + EditClass credit_edt (BUTTON_CREDITS, + credbuf, CREDITSBUF_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_credits_x, d_credits_y, d_credits_w, d_credits_h, EditClass::ALPHANUMERIC); + + ListClass scenariolist(BUTTON_SCENARIOLIST, + d_scenariolist_x, d_scenariolist_y, d_scenariolist_w, d_scenariolist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + GaugeClass countgauge (BUTTON_COUNT, + d_count_x, d_count_y, d_count_w, d_count_h); + + GaugeClass levelgauge (BUTTON_LEVEL, + d_level_x, d_level_y, d_level_w, d_level_h); + + TextButtonClass basesbtn(BUTTON_BASES, TXT_BASES_OFF, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_bases_x, d_bases_y, d_bases_w, d_bases_h); + + TextButtonClass tiberiumbtn(BUTTON_TIBERIUM, TXT_TIBERIUM_OFF, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_tiberium_x, d_tiberium_y, d_tiberium_w, d_tiberium_h); + + TextButtonClass goodiesbtn(BUTTON_GOODIES, TXT_CRATES_OFF, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_goodies_x, d_goodies_y, d_goodies_w, d_goodies_h); + + TextButtonClass ghostsbtn(BUTTON_GHOSTS, TXT_AI_PLAYERS_OFF, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_ghosts_x, d_ghosts_y, d_ghosts_w, d_ghosts_h); + + TextButtonClass okbtn(BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_ok_x, d_ok_y, d_ok_w, d_ok_h); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + d_cancel_x, d_cancel_y); +#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +#endif + + TextButtonClass sendbtn(BUTTON_SEND, TXT_SEND_MESSAGE, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + d_send_x, d_send_y); +#else + d_send_x, d_send_y, d_send_w, d_send_h); +#endif + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &name_edt; + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); + credit_edt.Add_Tail(*commands); + scenariolist.Add_Tail(*commands); + countgauge.Add_Tail(*commands); + levelgauge.Add_Tail(*commands); + basesbtn.Add_Tail(*commands); + tiberiumbtn.Add_Tail(*commands); + goodiesbtn.Add_Tail(*commands); + ghostsbtn.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + sendbtn.Add_Tail(*commands); + + /* + ----------------------------- Various Inits ------------------------------ + */ + /*........................................................................ + Init player name & house + ........................................................................*/ + MPlayerColorIdx = MPlayerPrefColor; // init my preferred color + strcpy (namebuf, MPlayerName); // set my name + name_edt.Set_Text(namebuf,MPLAYER_NAME_MAX); + name_edt.Set_Color(MPlayerTColors[MPlayerColorIdx]); + + if (MPlayerHouse==HOUSE_GOOD) { + gdibtn.Turn_On(); + } else { + nodbtn.Turn_On(); + } + + /*........................................................................ + Init scenario values, only the first time through + ........................................................................*/ + if (first_time) { + MPlayerCredits = 3000; // init credits & credit buffer + MPlayerBases = 1; // init scenario parameters + MPlayerTiberium = 0; + MPlayerGoodies = 0; + MPlayerGhosts = 0; + Special.IsCaptureTheFlag = 0; + MPlayerUnitCount = (MPlayerCountMax[MPlayerBases] + MPlayerCountMin[MPlayerBases]) / 2; + first_time = 0; + } + + /*........................................................................ + Init button states + ........................................................................*/ + if (MPlayerBases) { + basesbtn.Turn_On(); + basesbtn.Set_Text(TXT_BASES_ON); + } + if (MPlayerTiberium) { + tiberiumbtn.Turn_On(); + tiberiumbtn.Set_Text(TXT_TIBERIUM_ON); + } + if (MPlayerGoodies) { + goodiesbtn.Turn_On(); + goodiesbtn.Set_Text(TXT_CRATES_ON); + } + if (MPlayerGhosts) { + ghostsbtn.Turn_On(); + ghostsbtn.Set_Text(TXT_AI_PLAYERS_ON); + } + if (Special.IsCaptureTheFlag) { + MPlayerGhosts = 0; + ghostsbtn.Turn_On(); + ghostsbtn.Set_Text(TXT_CAPTURE_THE_FLAG); + } + + sprintf(credbuf, "%d", MPlayerCredits); + credit_edt.Set_Text(credbuf, CREDITSBUF_MAX); + old_cred = MPlayerCredits; + + levelgauge.Set_Maximum(MPLAYER_BUILD_LEVEL_MAX - 1); + levelgauge.Set_Value(BuildLevel - 1); + + countgauge.Set_Maximum(MPlayerCountMax[MPlayerBases] - MPlayerCountMin[MPlayerBases]); + countgauge.Set_Value(MPlayerUnitCount - MPlayerCountMin[MPlayerBases]); + + /*........................................................................ + Init other scenario parameters + ........................................................................*/ + Special.IsTGrowth = MPlayerTiberium; + Special.IsTSpread = MPlayerTiberium; + transmit = 1; + + /*........................................................................ + Init scenario description list box + ........................................................................*/ + for (i = 0; i < MPlayerScenarios.Count(); i++) { + scenariolist.Add_Item (strupr(MPlayerScenarios[i])); + } + ScenarioIdx = 0; // 1st scenario is selected + + /*........................................................................ + Init random-number generator, & create a seed to be used for all random + numbers from here on out + ........................................................................*/ + randomize(); + Seed = rand(); + + /*........................................................................ + Init the message display system + ........................................................................*/ + Messages.Init (d_message_x + 2*factor, d_message_y + 2*factor, 4, MAX_MESSAGE_LENGTH, d_txt6_h); + + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + + extern char ModemRXString[]; + + if (strlen(ModemRXString) > 36) + ModemRXString[36] = 0; + + if (strlen(ModemRXString) > 0) + Messages.Add_Message (ModemRXString, CC_TAN, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, 0, 0); + + ModemRXString[0] = '\0'; + + /* + ---------------------------- Init Mono Output ---------------------------- + */ + #if(SHOW_MONO) + NullModem.Configure_Debug(sizeof (CommHeaderType),sizeof (SerialCommandType), + SerialPacketNames, 106); + NullModem.Mono_Debug_Print(1); + #endif + + /* + ---------------------------- Processing loop ----------------------------- + */ + NullModem.Reset_Response_Time(); // clear response time + theirresponsetime = 10000; // initialize to an invalid value + timingtime = lastmsgtime = lastredrawtime = TickCount.Time(); + while (Get_Mouse_State() > 0) Show_Mouse(); + + + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ +#ifdef FORCE_WINSOCK + if (Winsock.Get_Connected()){ + Draw_Caption (TXT_HOST_INTERNET_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + }else{ + Draw_Caption (TXT_HOST_SERIAL_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + } +#else + Draw_Caption (TXT_HOST_SERIAL_GAME, d_dialog_x, d_dialog_y, d_dialog_w); +#endif //FORCE_WINSOCK + + Fancy_Text_Print(TXT_YOUR_NAME, + d_name_x - 5*factor, d_name_y + 1*factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_SIDE_COLON, + d_gdi_x - 5*factor, d_gdi_y + 1*factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print (TXT_START_CREDITS_COLON, d_credits_x - 5*factor, d_credits_y + 1*factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_COLOR_COLON, + cbox_x[0] - 5*factor, d_color_y + 1*factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_SCENARIOS, + d_scenariolist_x + (d_scenariolist_w / 2), + d_scenariolist_y - d_txt6_h, + CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print (TXT_COUNT, d_count_x - 3*factor, d_count_y, CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_RIGHT); + + Fancy_Text_Print (TXT_LEVEL, d_level_x - 3*factor, d_level_y, CC_GREEN, TBLACK, + TPF_NOSHADOW | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_RIGHT); + } + + /*.................................................................. + Draw the color boxes + ..................................................................*/ + if (display >= REDRAW_COLORS) { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + LogicPage->Fill_Rect (cbox_x[i] + 1*factor, d_color_y + 1*factor, + cbox_x[i] + 1*factor + d_color_w - 2*factor, d_color_y + 1*factor + d_color_h - 2*factor, + MPlayerGColors[i]); + + if (i == MPlayerColorIdx) { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, + BOXSTYLE_GREEN_DOWN, false); + } else { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, + BOXSTYLE_GREEN_RAISED, false); + } + } + } + + /*.................................................................. + Draw the message: + - Erase an old message first + ..................................................................*/ + if (display >= REDRAW_MESSAGE) { + Draw_Box(d_message_x, d_message_y, d_message_w, d_message_h, + BOXSTYLE_GREEN_BORDER, true); + Messages.Draw(); + + LogicPage->Fill_Rect (d_dialog_x + 2*factor, + d_opponent_y, + d_dialog_x + d_dialog_w - 4*factor, + d_opponent_y + d_txt6_h, + BLACK); + + if (parms_received) { + if (oppscorescreen) { + sprintf(txt,"%s",Text_String(TXT_WAITING_FOR_OPPONENT)); + + int txtwidth = String_Pixel_Width( txt ); + + Fancy_Text_Print (txt, d_dialog_cx - (txtwidth / 2), + d_opponent_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } else { + Fancy_Text_Print (TXT_OPPONENT_COLON, d_opponent_x - 3*factor, + d_opponent_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (TheirHouse == HOUSE_GOOD) { + sprintf(txt,"%s %s",TheirName,Text_String(TXT_G_D_I)); + } else { + sprintf(txt,"%s %s",TheirName,Text_String(TXT_N_O_D)); + } + + Fancy_Text_Print (txt, d_opponent_x, + d_opponent_y, MPlayerTColors[TheirColor], TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + } + + sprintf( txt, "%d ", MPlayerUnitCount ); + Fancy_Text_Print (txt, d_count_x + d_count_w + 3*factor, + d_count_y, CC_GREEN, BLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(txt, "%d ", BuildLevel); + } else { + sprintf(txt, "**"); + } + Fancy_Text_Print (txt, d_level_x + d_level_w + 3*factor, + d_level_y, CC_GREEN, BLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + /*------------------------------------------------------------------ + User clicks on a color button + ------------------------------------------------------------------*/ + case KN_LMOUSE: + if (_Kbd->MouseQX > cbox_x[0] && + _Kbd->MouseQX < (cbox_x[MAX_MPLAYER_COLORS - 1] + d_color_w) && + _Kbd->MouseQY > d_color_y && + _Kbd->MouseQY < (d_color_y + d_color_h)) { + MPlayerPrefColor = (_Kbd->MouseQX - cbox_x[0]) / d_color_w; + MPlayerColorIdx = MPlayerPrefColor; + display = REDRAW_COLORS; + + name_edt.Set_Color (MPlayerTColors[MPlayerColorIdx]); + name_edt.Flag_To_Redraw(); + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + changed = 1; + } + break; + + /*------------------------------------------------------------------ + User edits the name field; retransmit new game options + ------------------------------------------------------------------*/ + case (BUTTON_NAME | KN_BUTTON): + credit_edt.Clear_Focus(); + credit_edt.Flag_To_Redraw(); + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + changed = 1; + break; + + /*------------------------------------------------------------------ + House Buttons: set the player's desired House + ------------------------------------------------------------------*/ + case (BUTTON_GDI | KN_BUTTON): + MPlayerHouse = HOUSE_GOOD; + gdibtn.Turn_On(); + nodbtn.Turn_Off(); + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + break; + + case (BUTTON_NOD | KN_BUTTON): + MPlayerHouse = HOUSE_BAD; + gdibtn.Turn_Off(); + nodbtn.Turn_On(); + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + break; + + /*------------------------------------------------------------------ + User edits the credits value; retransmit new game options + ------------------------------------------------------------------*/ + case (BUTTON_CREDITS | KN_BUTTON): + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + break; + + /*------------------------------------------------------------------ + New Scenario selected. + ------------------------------------------------------------------*/ + case (BUTTON_SCENARIOLIST | KN_BUTTON): + if (scenariolist.Current_Index() != ScenarioIdx) { + ScenarioIdx = scenariolist.Current_Index(); + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + } + break; + + /*------------------------------------------------------------------ + User adjusts max # units + ------------------------------------------------------------------*/ + case (BUTTON_COUNT | KN_BUTTON): + MPlayerUnitCount = countgauge.Get_Value() + MPlayerCountMin[MPlayerBases]; + display = REDRAW_MESSAGE; + transmit = 1; + break; + + /*------------------------------------------------------------------ + User adjusts build level + ------------------------------------------------------------------*/ + case (BUTTON_LEVEL | KN_BUTTON): + BuildLevel = levelgauge.Get_Value() + 1; + if (BuildLevel > MPLAYER_BUILD_LEVEL_MAX) // if it's pegged, max it out + BuildLevel = MPLAYER_BUILD_LEVEL_MAX; + display = REDRAW_MESSAGE; + transmit = 1; + break; + + /*------------------------------------------------------------------ + Toggle bases + ------------------------------------------------------------------*/ + case (BUTTON_BASES | KN_BUTTON): + if (MPlayerBases) { + MPlayerBases = 0; + basesbtn.Turn_Off(); + basesbtn.Set_Text(TXT_BASES_OFF); + MPlayerUnitCount = Fixed_To_Cardinal (MPlayerCountMax[0]-MPlayerCountMin[0], + Cardinal_To_Fixed(MPlayerCountMax[1]-MPlayerCountMin[1], + MPlayerUnitCount-MPlayerCountMin[1])) + MPlayerCountMin[0]; + } else { + MPlayerBases = 1; + basesbtn.Turn_On(); + basesbtn.Set_Text(TXT_BASES_ON); + MPlayerUnitCount = Fixed_To_Cardinal (MPlayerCountMax[1]-MPlayerCountMin[1], + Cardinal_To_Fixed(MPlayerCountMax[0]-MPlayerCountMin[0], + MPlayerUnitCount-MPlayerCountMin[0])) + MPlayerCountMin[1]; + } + MPlayerCredits = atoi(credbuf); + countgauge.Set_Maximum(MPlayerCountMax[MPlayerBases] - MPlayerCountMin[MPlayerBases]); + countgauge.Set_Value(MPlayerUnitCount - MPlayerCountMin[MPlayerBases]); + strcpy (MPlayerName, namebuf); + transmit = 1; + display = REDRAW_ALL; + break; + + /*------------------------------------------------------------------ + Toggle tiberium + ------------------------------------------------------------------*/ + case (BUTTON_TIBERIUM | KN_BUTTON): + if (MPlayerTiberium) { + MPlayerTiberium = 0; + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + tiberiumbtn.Turn_Off(); + tiberiumbtn.Set_Text(TXT_TIBERIUM_OFF); + } else { + MPlayerTiberium = 1; + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + tiberiumbtn.Turn_On(); + tiberiumbtn.Set_Text(TXT_TIBERIUM_ON); + } + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + break; + + /*------------------------------------------------------------------ + Toggle goodies + ------------------------------------------------------------------*/ + case (BUTTON_GOODIES | KN_BUTTON): + if (MPlayerGoodies) { + MPlayerGoodies = 0; + goodiesbtn.Turn_Off(); + goodiesbtn.Set_Text(TXT_CRATES_OFF); + } else { + MPlayerGoodies = 1; + goodiesbtn.Turn_On(); + goodiesbtn.Set_Text(TXT_CRATES_ON); + } + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + break; + + /*------------------------------------------------------------------ + Toggle ghosts + ------------------------------------------------------------------*/ + case (BUTTON_GHOSTS | KN_BUTTON): + if (!MPlayerGhosts && !Special.IsCaptureTheFlag) { // ghosts OFF => ghosts ON + MPlayerGhosts = 1; + Special.IsCaptureTheFlag = 0; + ghostsbtn.Turn_On(); + ghostsbtn.Set_Text(TXT_AI_PLAYERS_ON); + } + else if (MPlayerGhosts) { // ghosts ON => capture-flag + MPlayerGhosts = 0; + Special.IsCaptureTheFlag = 1; + ghostsbtn.Turn_On(); + ghostsbtn.Set_Text(TXT_CAPTURE_THE_FLAG); + } + else if (Special.IsCaptureTheFlag) { // capture-flag => AI OFF + MPlayerGhosts = 0; + Special.IsCaptureTheFlag = 0; + ghostsbtn.Turn_Off(); + ghostsbtn.Set_Text(TXT_AI_PLAYERS_OFF); + } + MPlayerCredits = atoi(credbuf); + strcpy (MPlayerName, namebuf); + transmit = 1; + break; + + /*------------------------------------------------------------------ + OK: exit loop with true status + ------------------------------------------------------------------*/ + case (BUTTON_OK | KN_BUTTON): + // + // make sure we got a game options packet from the other player + // + if (gameoptions) { + rc = true; + process = false; + + // force transmitting of game options packet one last time + + transmit = 1; + transmittime = 0; + } else { + CCMessageBox().Process (TXT_ONLY_ONE,TXT_OOPS,NULL); + display = REDRAW_ALL; + } + break; + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF, bail out with error code + ------------------------------------------------------------------*/ + case (KN_ESC): + if (Messages.Get_Edit_Buf() != NULL) { + Messages.Input(input); + display = REDRAW_MESSAGE; + break; + } + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + + /*------------------------------------------------------------------ + Default: manage the inter-player messages + ------------------------------------------------------------------*/ + default: + /*............................................................... + F4/SEND/'M' = send a message + ...............................................................*/ + if (Messages.Get_Edit_Buf()==NULL) { + if (input == KN_M || input==(BUTTON_SEND | KN_BUTTON) || + input == KN_F4) { + memset (txt, 0, 80); + + strcpy(txt,Text_String(TXT_MESSAGE)); // "Message:" + + Messages.Add_Edit (MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, d_message_w-70*factor); + display = REDRAW_MESSAGE; + + credit_edt.Clear_Focus(); + credit_edt.Flag_To_Redraw(); + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + + break; + } + } else { + if ( input == (BUTTON_SEND | KN_BUTTON) ) { + input = KN_RETURN; + } + } + + /*............................................................... + Manage the message system (get rid of old messages) + ...............................................................*/ + if (Messages.Manage()) { + display = REDRAW_MESSAGE; + } + + /*............................................................... + Service keyboard input for any message being edited. + ...............................................................*/ + i = Messages.Input(input); + + /*............................................................... + If 'Input' returned 1, it means refresh the message display. + ...............................................................*/ + if (i==1) { + Messages.Draw(); + } + + /*............................................................... + If 'Input' returned 2, it means redraw the message display. + ...............................................................*/ + else if (i==2) { + display = REDRAW_MESSAGE; + } + + /*............................................................... + If 'input' returned 3, it means send the current message. + ...............................................................*/ + else if (i==3) { + + sent_so_far = 0; + magic_number = MESSAGE_HEAD_MAGIC_NUMBER; + message_length = strlen(Messages.Get_Edit_Buf()); + crc = (unsigned short) + (Calculate_CRC(Messages.Get_Edit_Buf(), message_length) &0xffff); + + while (sent_so_far < message_length){ + + SendPacket.Command = SERIAL_MESSAGE; + strcpy (SendPacket.Name, MPlayerName); + SendPacket.ID = Build_MPlayerID(MPlayerColorIdx, MPlayerHouse); + memcpy (SendPacket.Message, Messages.Get_Edit_Buf()+sent_so_far, COMPAT_MESSAGE_LENGTH-5); + *(SendPacket.Message + COMPAT_MESSAGE_LENGTH-5) = 0; + *((unsigned short*)(SendPacket.Message + COMPAT_MESSAGE_LENGTH-4)) = magic_number; + *((unsigned short*)(SendPacket.Message + COMPAT_MESSAGE_LENGTH-2)) = crc; + + /*.................................................................. + Send the message + ..................................................................*/ + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + NullModem.Service(); + + /*.................................................................. + Add the message to our own screen + ..................................................................*/ + sprintf(txt, Text_String (TXT_FROM), MPlayerName, SendPacket.Message); + Messages.Add_Message (txt, MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, magic_number, crc); + + magic_number++; + sent_so_far += COMPAT_MESSAGE_LENGTH-5; + } + + display = REDRAW_MESSAGE; + } /* end of send message */ + + } /* end of input processing */ + + /*--------------------------------------------------------------------- + Detect editing of the credits buffer, transmit new values to players + ---------------------------------------------------------------------*/ + if (atoi(credbuf) != old_cred) { + old_cred = Bound(atoi(credbuf), 0, 9999); + MPlayerCredits = old_cred; + transmit = 1; + sprintf(credbuf, "%d", MPlayerCredits); + credit_edt.Set_Text(credbuf, CREDITSBUF_MAX); + } + + /*--------------------------------------------------------------------- + Detect editing of the name buffer, transmit new values to players + ---------------------------------------------------------------------*/ + if (strcmp (namebuf, MPlayerName)) { + strcpy (MPlayerName, namebuf); + transmit = 1; + changed = 1; + } + + /*--------------------------------------------------------------------- + If our Transmit flag is set, we need to send out a game option packet. + This message requires an ACK. The first time through the loop, transmit + should be set, so we send out our default options; we'll then send + any changes we make to the defaults. + ---------------------------------------------------------------------*/ + if (transmit && (TickCount.Time() - transmittime) > PACKET_RETRANS_TIME) { + SendPacket.Command = SERIAL_GAME_OPTIONS; + strcpy (SendPacket.Name, MPlayerName); +#ifdef PATCH + if (IsV107) { + SendPacket.Version = 1; + } else { + SendPacket.Version = 2; + } +#else + SendPacket.Version = Version_Number(); +#endif + SendPacket.House = MPlayerHouse; + SendPacket.Color = MPlayerColorIdx; + + SendPacket.Scenario = MPlayerFilenum[ScenarioIdx]; + + SendPacket.Credits = MPlayerCredits; + SendPacket.IsBases = MPlayerBases; + SendPacket.IsTiberium = MPlayerTiberium; + SendPacket.IsGoodies = MPlayerGoodies; + SendPacket.IsGhosties = MPlayerGhosts; + SendPacket.BuildLevel = BuildLevel; + SendPacket.UnitCount = MPlayerUnitCount; + SendPacket.Seed = Seed; + SendPacket.Special = Special; + SendPacket.GameSpeed = Options.GameSpeed; + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + transmittime = TickCount.Time(); + transmit = 0; + } + + // + // send a timing packet if enough time has gone by. + // + if ( (TickCount.Time() - timingtime) > PACKET_TIMING_TIMEOUT) { + SendPacket.Command = SERIAL_TIMING; + SendPacket.ResponseTime = NullModem.Response_Time(); + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 0); + timingtime = TickCount.Time(); + } + + /*--------------------------------------------------------------------- + Check for an incoming message + ---------------------------------------------------------------------*/ + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { +// Smart_Printf( "received packet of length %d\n", packetlen ); + + lastmsgtime = TickCount.Time(); + msg_timeout = 600; // reset timeout value to 10 seconds + // (only the 1st time through is 20 seconds) + + // are we getting our own packets back?? + + if (ReceivePacket.Command >= SERIAL_CONNECT && + ReceivePacket.Command < SERIAL_LAST_COMMAND && + ReceivePacket.Command != SERIAL_MESSAGE && + ReceivePacket.ID == ModemGameToPlay) { + + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + break; + } + + event = (EventClass *)&ReceivePacket; + if (event->Type <= EventClass::FRAMEINFO) { + if ( (TickCount.Time() - lastredrawtime) > PACKET_REDRAW_TIME) { + lastredrawtime = TickCount.Time(); + oppscorescreen = true; + +if (display != REDRAW_ALL) { + display = REDRAW_MESSAGE; +} + +// display = REDRAW_MESSAGE; + parms_received = 1; + } + } else { + switch ( ReceivePacket.Command ) { + /*.................................................................. + Sign-off: Give the other machine time to receive my ACK, display a + message, and exit. + ..................................................................*/ + case (SERIAL_SIGN_OFF): +// Smart_Printf( "received sign off\n" ); + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < 60) + NullModem.Service(); + CCMessageBox().Process(TXT_USER_SIGNED_OFF); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + recsignedoff = true; + break; + + /*.................................................................. + Game Options: Store the other machine's name, color & house; + If they've picked the same color as myself, re-transmit my settings + to force him to choose a different color. (Com_Show_Scenario_Dialog + is responsible for ensuring the colors are different.) + ..................................................................*/ + case (SERIAL_GAME_OPTIONS): +// Smart_Printf( "received game options\n" ); + oppscorescreen = false; + gameoptions = true; + strcpy (TheirName, ReceivePacket.Name); + TheirColor = ReceivePacket.Color; + TheirHouse = ReceivePacket.House; + transmit = 1; + + parms_received = 1; +if (display != REDRAW_ALL) { + display = REDRAW_MESSAGE; +} +// display = REDRAW_MESSAGE; + + /*............................................................... + Check the version number of the other system. + ...............................................................*/ +#ifdef PATCH + if (IsV107) { + version = 1; + } else { + version = 2; + } +#else + version = Version_Number(); +#endif + if (ReceivePacket.Version > version) { + CCMessageBox().Process (TXT_YOURGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + } else { + if (ReceivePacket.Version < version) { + CCMessageBox().Process (TXT_DESTGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + } + } + break; + + /*.................................................................. + Incoming message: add to our list + ..................................................................*/ + case (SERIAL_MESSAGE): +// Smart_Printf( "received serial message\n" ); + oppscorescreen = false; + sprintf(txt, Text_String (TXT_FROM), ReceivePacket.Name, + ReceivePacket.Message); + magic_number = *((unsigned short*)(ReceivePacket.Message + COMPAT_MESSAGE_LENGTH-4)); + crc = *((unsigned short*)(ReceivePacket.Message + COMPAT_MESSAGE_LENGTH-2)); + Messages.Add_Message (txt, + MPlayerTColors[MPlayerID_To_ColorIndex(ReceivePacket.ID)], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, magic_number, crc); +if (display != REDRAW_ALL) { + display = REDRAW_MESSAGE; +} +// display = REDRAW_MESSAGE; + break; + + // + // get their response time + // + case (SERIAL_TIMING): +// Smart_Printf( "received timing\n" ); + oppscorescreen = false; + theirresponsetime = ReceivePacket.ResponseTime; + + if ( !gameoptions ) { + + // retransmit of game options packet again + transmit = 1; + } + break; + + // + // print msg waiting for opponent + // + case (SERIAL_SCORE_SCREEN): +// Smart_Printf( "received score screen\n" ); + oppscorescreen = true; +if (display != REDRAW_ALL) { + display = REDRAW_MESSAGE; +} +// display = REDRAW_MESSAGE; + parms_received = 1; + break; + + default: +// Smart_Printf( "received unknown command %X\n", ReceivePacket.Command ); + break; + } + } + } + + // if we haven't received a msg for 10 seconds exit + + if ( (TickCount.Time() - lastmsgtime) > msg_timeout) { + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + } + + /*--------------------------------------------------------------------- + Service the connection + ---------------------------------------------------------------------*/ + NullModem.Service(); + + } /* end of while */ + + /*------------------------------------------------------------------------ + Sort player ID's, so we can execute commands in the same order on both + machines. + ------------------------------------------------------------------------*/ + if (rc) { + /*..................................................................... + Set the number of players in this game, and my ID + .....................................................................*/ + MPlayerCount = 2; + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + TheirID = Build_MPlayerID (TheirColor,TheirHouse); + + /*..................................................................... + Store every player's ID in the MPlayerID[] array. This array will + determine the order of event execution, so the ID's must be stored + in the same order on all systems. + .....................................................................*/ + if (TheirID < MPlayerLocalID) { + MPlayerID[0] = TheirID; + MPlayerID[1] = MPlayerLocalID; + strcpy (MPlayerNames[0], TheirName); + strcpy (MPlayerNames[1], MPlayerName); + } else { + MPlayerID[0] = MPlayerLocalID; + MPlayerID[1] = TheirID; + strcpy (MPlayerNames[0], MPlayerName); + strcpy (MPlayerNames[1], TheirName); + } + + /*..................................................................... + Get the scenario filename + .....................................................................*/ + Scenario = MPlayerFilenum[ScenarioIdx]; + + /*..................................................................... + Send all players the GO packet. + .....................................................................*/ + SendPacket.Command = SERIAL_GO; + SendPacket.ResponseTime = NullModem.Response_Time(); + if ( theirresponsetime == 10000 ) { +// Mono_Clear_Screen(); +// Smart_Printf( "Did not receive their response time!!!!!!!\n" ); +// Get_Key(); + } else { + if (SendPacket.ResponseTime < theirresponsetime) { + SendPacket.ResponseTime = theirresponsetime; + } + } + + // + // calculated one way delay for a packet and overall delay to execute + // a packet + // + MPlayerMaxAhead = MAX( (SendPacket.ResponseTime / 8), 2); + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount.Time(); + while ( ( NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_SENDING_TIMEOUT) ) + || ((TickCount.Time() - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + NullModem.Service(); + Keyboard::Check(); //Make sure the message loop gets called + } + + // clear queue to keep from doing any resends + NullModem.Init_Send_Queue(); + + } else { + if ( !recsignedoff ) { + /*..................................................................... + Broadcast my sign-off over my network + .....................................................................*/ + SendPacket.Command = SERIAL_SIGN_OFF; + SendPacket.Color = MPlayerLocalID; // use Color for ID + SendPacket.ID = ModemGameToPlay; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount.Time(); + while ( (NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_CANCEL_TIMEOUT) ) + || ((TickCount.Time() - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + if ( NullModem.Get_Message( &ReceivePacket, &packetlen ) > 0) { + + // are we getting our own packets back?? + + if (ReceivePacket.Command == SERIAL_SIGN_OFF + && ReceivePacket.ID == ModemGameToPlay) { + + // exit while + break; + } + } + + NullModem.Service(); + } + } + + Shutdown_Modem(); + } + + /*------------------------------------------------------------------------ + Clear all lists + ------------------------------------------------------------------------*/ + while (scenariolist.Count()) { + scenariolist.Remove_Item(scenariolist.Get_Item(0)); + } + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Show_Mouse(); + + /*------------------------------------------------------------------------ + Save any changes made to our options + ------------------------------------------------------------------------*/ + if (changed) { + Write_MultiPlayer_Settings (); + } + + return(rc); + +} /* end of Com_Scenario_Dialog */ + + +/*********************************************************************************************** + * Com_Show_Scenario_Dialog -- Serial game scenario selection dialog * + * * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Serial Game ³ * + * ³ ³ * + * ³ Your Name: __________ ³ * + * ³ House: [GDI] [NOD] ³ * + * ³ Desired Color: [ ][ ][ ][ ] ³ * + * ³ ³ * + * ³ Opponent: Name ³ * + * ³ Scenario: Description ³ * + * ³ Credits: xxxx ³ * + * ³ Bases: ON ³ * + * ³ Crates: ON ³ * + * ³ Tiberium: ON ³ * + * ³ Ghosts: ON ³ * + * ³ ³ * + * ³ [Cancel] ³ * + * ³ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ [Send Message] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = success, false = cancel * + * * + * WARNINGS: * + * MPlayerName & MPlayerGameName must contain this player's name. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +int Com_Show_Scenario_Dialog(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 306*factor; // dialog width + int d_dialog_h = 187*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6*factor+1; // ht of 6-pt text + int d_margin1 = 5*factor; // margin width/height + int d_margin2 = 2*factor; // margin width/height + + int d_name_w = 70*factor; + int d_name_h = 9*factor; + int d_name_x = d_dialog_cx; + int d_name_y = d_dialog_y + d_margin1 + d_txt6_h + d_txt6_h; + + int d_gdi_w = 30*factor; + int d_gdi_h = 9*factor; + int d_gdi_x = d_dialog_cx; + int d_gdi_y = d_name_y + d_name_h + d_margin2; + + int d_nod_w = 30*factor; + int d_nod_h = 9*factor; + int d_nod_x = d_gdi_x + d_gdi_w + d_margin2; + int d_nod_y = d_gdi_y; + + int d_color_w = 10*factor; + int d_color_h = 9*factor; + int d_color_y = d_gdi_y + d_gdi_h + d_margin2; + + int d_opponent_y = d_color_y + d_color_h + d_margin1; + int d_scenario_y = d_opponent_y + d_txt6_h; + int d_credits_y = d_scenario_y + d_txt6_h; + int d_count_y = d_credits_y + d_txt6_h; + int d_level_y = d_count_y + d_txt6_h; + int d_bases_y = d_level_y + d_txt6_h; + int d_goodies_y = d_bases_y + d_txt6_h; + int d_tiberium_y = d_goodies_y + d_txt6_h; + int d_ghosts_y = d_tiberium_y + d_txt6_h; + + int d_cancel_w = 45*factor; + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx - (d_cancel_w / 2); + int d_cancel_y = d_ghosts_y + d_txt6_h + d_margin1; + + int d_message_w = d_dialog_w - (d_margin1 * 2); + int d_message_h = 34*factor; + int d_message_x = d_dialog_x + d_margin1; + int d_message_y = d_cancel_y + d_cancel_h + d_margin1; + + int d_send_w = 80*factor; + int d_send_h = 9*factor; + int d_send_x = d_dialog_cx - (d_send_w / 2); + int d_send_y = d_message_y + d_message_h + d_margin2; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_NAME = 100, + BUTTON_GDI, + BUTTON_NOD, + BUTTON_CANCEL, + BUTTON_SEND, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + BOOL process = true; // process while true + KeyNumType input; + + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int transmit; // 1 = re-transmit new game options + int first; // 1 = no packets received yet + int cbox_x[] = { d_dialog_cx, + d_dialog_cx + d_color_w, + d_dialog_cx + (d_color_w * 2), + d_dialog_cx + (d_color_w * 3), + d_dialog_cx + (d_color_w * 4), + d_dialog_cx + (d_color_w * 5)}; + int parms_received = 0; // 1 = game options received + int changed = 0; // 1 = user has changed an option + + int rc; + int recsignedoff = 0; + int i; + int version; + char txt[80]; + unsigned long starttime; + unsigned long timingtime; + unsigned long lastmsgtime; + unsigned long lastredrawtime; + unsigned long transmittime = 0; + int packetlen; + bool oppscorescreen = false; + bool gameoptions = false; + EventClass *event; // event ptr + unsigned long msg_timeout = 1200; // init to 20 seconds + + int message_length; + int sent_so_far; + unsigned short magic_number; + unsigned short crc; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + EditClass name_edt (BUTTON_NAME, + namebuf, MPLAYER_NAME_MAX, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_name_x, d_name_y, d_name_w, d_name_h, EditClass::ALPHANUMERIC); + + TextButtonClass gdibtn(BUTTON_GDI, TXT_G_D_I, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_gdi_x, d_gdi_y, d_gdi_w, d_gdi_h); + + TextButtonClass nodbtn(BUTTON_NOD, TXT_N_O_D, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_nod_x, d_nod_y, d_nod_w, d_nod_h); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + d_cancel_x, d_cancel_y); +#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +#endif + + TextButtonClass sendbtn(BUTTON_SEND, TXT_SEND_MESSAGE, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + d_send_x, d_send_y); +#else + d_send_x, d_send_y, d_send_w, d_send_h); +#endif + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &name_edt; + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + sendbtn.Add_Tail(*commands); + + /* + ----------------------------- Various Inits ------------------------------ + */ + /*........................................................................ + Init player name & house + ........................................................................*/ + MPlayerColorIdx = MPlayerPrefColor; // init my preferred color + strcpy (namebuf, MPlayerName); // set my name + name_edt.Set_Text(namebuf,MPLAYER_NAME_MAX); + name_edt.Set_Color(MPlayerTColors[MPlayerColorIdx]); + + if (MPlayerHouse==HOUSE_GOOD) { + gdibtn.Turn_On(); + } else { + nodbtn.Turn_On(); + } + + Fancy_Text_Print("", 0, 0, CC_GREEN, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + transmit = 1; + first = 1; + + /*........................................................................ + Init the message display system + ........................................................................*/ + Messages.Init (d_message_x + 2*factor, d_message_y + 2*factor, 4, MAX_MESSAGE_LENGTH, d_txt6_h); + + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + + extern char ModemRXString[]; + + if (strlen(ModemRXString) > 36) + ModemRXString[36] = 0; + + if (strlen(ModemRXString) > 0) + Messages.Add_Message (ModemRXString, CC_TAN, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, 0, 0); + + ModemRXString[0] = '\0'; + + /* + ---------------------------- Init Mono Output ---------------------------- + */ + #if(SHOW_MONO) + NullModem.Configure_Debug(sizeof (CommHeaderType),sizeof (SerialCommandType), + SerialPacketNames, 106); + NullModem.Mono_Debug_Print(1); + #endif + + /* + ---------------------------- Processing loop ----------------------------- + */ + NullModem.Reset_Response_Time(); // clear response time + timingtime = lastmsgtime = lastredrawtime = TickCount.Time(); + while (Get_Mouse_State() > 0) Show_Mouse(); + + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ +#ifdef FORCE_WINSOCK + if (Winsock.Get_Connected()){ + Draw_Caption (TXT_JOIN_INTERNET_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + }else{ + Draw_Caption (TXT_JOIN_SERIAL_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + } +#else + Draw_Caption (TXT_JOIN_SERIAL_GAME, d_dialog_x, d_dialog_y, d_dialog_w); +#endif //FORCE_WINSOCK + + Fancy_Text_Print(TXT_YOUR_NAME, + d_name_x - 5*factor, d_name_y + 1*factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_SIDE_COLON, + d_gdi_x - 5*factor, d_gdi_y + 1*factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print(TXT_COLOR_COLON, + cbox_x[0] - 5*factor, d_color_y + 1*factor, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + } + + /*.................................................................. + Draw the color boxes + ..................................................................*/ + if (display >= REDRAW_COLORS) { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + LogicPage->Fill_Rect (cbox_x[i] + 1*factor, d_color_y + 1*factor, + cbox_x[i] + 1*factor + d_color_w - 2*factor, d_color_y + 1*factor + d_color_h - 2*factor, + MPlayerGColors[i]); + + if (i == MPlayerColorIdx) { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, + BOXSTYLE_GREEN_DOWN, false); + } else { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, + BOXSTYLE_GREEN_RAISED, false); + } + } + } + + /*.................................................................. + Draw the message: + - Erase an old message first + ..................................................................*/ + if (display >= REDRAW_MESSAGE) { + Draw_Box(d_message_x, d_message_y, d_message_w, d_message_h, + BOXSTYLE_GREEN_BORDER, true); + Messages.Draw(); + + LogicPage->Fill_Rect( d_dialog_x + 2*factor, + d_opponent_y, + d_dialog_x + d_dialog_w - 4*factor, + d_ghosts_y + d_txt6_h, + BLACK); + + if (parms_received) { + if (oppscorescreen) { + sprintf(txt,"%s",Text_String(TXT_WAITING_FOR_OPPONENT)); + + int txtwidth = String_Pixel_Width( txt ); + + Fancy_Text_Print (txt, d_dialog_cx - (txtwidth / 2), + d_opponent_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } else { + /*............................................................ + Opponent's name + ............................................................*/ + Fancy_Text_Print (TXT_OPPONENT_COLON, d_dialog_cx - 3*factor, + d_opponent_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (TheirHouse == HOUSE_GOOD) { + sprintf(txt,"%s %s",TheirName,Text_String(TXT_G_D_I)); + } else { + sprintf(txt,"%s %s",TheirName,Text_String(TXT_N_O_D)); + } + + Fancy_Text_Print (txt, d_dialog_cx, + d_opponent_y, MPlayerTColors[TheirColor], TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Scenario description + ............................................................*/ + Fancy_Text_Print (TXT_SCENARIO_COLON, d_dialog_cx - 3*factor, + d_scenario_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (ScenarioIdx != -1) { + sprintf(txt,"%s", MPlayerScenarios[ScenarioIdx]); + + Fancy_Text_Print (txt, d_dialog_cx, + d_scenario_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } else { + strcpy(txt,Text_String(TXT_NOT_FOUND)); + + Fancy_Text_Print (txt, d_dialog_cx, + d_scenario_y, RED, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + + /*............................................................ + Credits + ............................................................*/ + Fancy_Text_Print (TXT_START_CREDITS_COLON, d_dialog_cx - 3*factor, + d_credits_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + sprintf(txt,"%d",MPlayerCredits); + Fancy_Text_Print (txt, d_dialog_cx, + d_credits_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Count + ............................................................*/ + + Fancy_Text_Print (TXT_COUNT, d_dialog_cx - 3*factor, + d_count_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + sprintf( txt, "%d ", MPlayerUnitCount ); + Fancy_Text_Print (txt, d_dialog_cx, + d_count_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Level + ............................................................*/ + + Fancy_Text_Print (TXT_LEVEL, d_dialog_cx - 3*factor, + d_level_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(txt, "%d ", BuildLevel); + } else { + sprintf(txt, "**"); + } + Fancy_Text_Print (txt, d_dialog_cx, + d_level_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Bases status + ............................................................*/ + Fancy_Text_Print (TXT_BASES_COLON, d_dialog_cx - 3*factor, + d_bases_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (MPlayerBases) { + strcpy(txt,Text_String(TXT_ON)); + } else { + strcpy(txt,Text_String(TXT_OFF)); + } + Fancy_Text_Print (txt, d_dialog_cx, + d_bases_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Tiberium status + ............................................................*/ + Fancy_Text_Print (TXT_TIBERIUM_COLON, d_dialog_cx - 3*factor, + d_tiberium_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (MPlayerTiberium) { + strcpy(txt,Text_String(TXT_ON)); + } else { + strcpy(txt,Text_String(TXT_OFF)); + } + Fancy_Text_Print (txt, d_dialog_cx, + d_tiberium_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Goodies status + ............................................................*/ + Fancy_Text_Print (TXT_CRATES_COLON, d_dialog_cx - 3*factor, + d_goodies_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (MPlayerGoodies) { + strcpy(txt,Text_String(TXT_ON)); + } else { + strcpy(txt,Text_String(TXT_OFF)); + } + Fancy_Text_Print (txt, d_dialog_cx, + d_goodies_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................ + Capture the flag or AI player ON/OFF + ............................................................*/ + if ( Special.IsCaptureTheFlag ) { + strcpy( txt, Text_String( TXT_CAPTURE_THE_FLAG ) ); + strcat( txt, ":" ); + Fancy_Text_Print (txt, d_dialog_cx - 3*factor, + d_ghosts_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + strcpy(txt,Text_String(TXT_ON)); + Fancy_Text_Print (txt, d_dialog_cx, + d_ghosts_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } else { + /*............................................................ + Ghost player status + ............................................................*/ + Fancy_Text_Print (TXT_AI_PLAYERS_COLON, d_dialog_cx - 3*factor, + d_ghosts_y, CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + if (MPlayerGhosts) { + strcpy(txt,Text_String(TXT_ON)); + } else { + strcpy(txt,Text_String(TXT_OFF)); + } + Fancy_Text_Print (txt, d_dialog_cx, + d_ghosts_y, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + } + } + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + /*------------------------------------------------------------------ + User clicks on a color button + ------------------------------------------------------------------*/ + case KN_LMOUSE: + if (_Kbd->MouseQX > cbox_x[0] && + _Kbd->MouseQX < (cbox_x[MAX_MPLAYER_COLORS - 1] + d_color_w) && + _Kbd->MouseQY > d_color_y && + _Kbd->MouseQY < (d_color_y + d_color_h)) { + /*......................................................... + Compute my preferred color as the one I clicked on. + .........................................................*/ + MPlayerPrefColor = (_Kbd->MouseQX - cbox_x[0]) / d_color_w; + changed = 1; + /*......................................................... + If 'TheirColor' is set to the other player's color, make + sure we can't pick that color. + .........................................................*/ + if (parms_received) { + if (MPlayerPrefColor == TheirColor) + break; + } + MPlayerColorIdx = MPlayerPrefColor; + + name_edt.Set_Color(MPlayerTColors[MPlayerColorIdx]); + name_edt.Flag_To_Redraw(); + display = REDRAW_COLORS; + strcpy (MPlayerName, namebuf); + transmit = 1; + } + break; + + /*------------------------------------------------------------------ + House Buttons: set the player's desired House + ------------------------------------------------------------------*/ + case (BUTTON_GDI | KN_BUTTON): + MPlayerHouse = HOUSE_GOOD; + gdibtn.Turn_On(); + nodbtn.Turn_Off(); + strcpy (MPlayerName, namebuf); + transmit = 1; + break; + + case (BUTTON_NOD | KN_BUTTON): + MPlayerHouse = HOUSE_BAD; + gdibtn.Turn_Off(); + nodbtn.Turn_On(); + strcpy (MPlayerName, namebuf); + transmit = 1; + break; + + /*------------------------------------------------------------------ + User edits the name value; retransmit + ------------------------------------------------------------------*/ + case (BUTTON_NAME | KN_BUTTON): + strcpy (MPlayerName, namebuf); + transmit = 1; + changed = 1; + break; + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF, bail out with error code + ------------------------------------------------------------------*/ + case (KN_ESC): + if (Messages.Get_Edit_Buf() != NULL) { + Messages.Input(input); + display = REDRAW_MESSAGE; + break; + } + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + + /*------------------------------------------------------------------ + Default: manage the inter-player messages + ------------------------------------------------------------------*/ + default: + /*............................................................... + F4/SEND/'M' = send a message + ...............................................................*/ + if (Messages.Get_Edit_Buf()==NULL) { + if (input == KN_M || input==(BUTTON_SEND | KN_BUTTON) || + input == KN_F4) { + memset (txt, 0, 80); + + strcpy(txt,Text_String(TXT_MESSAGE)); // "Message:" + + Messages.Add_Edit (MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, d_message_w-70*factor); + display = REDRAW_MESSAGE; + + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + + break; + } + } else { + if ( input == (BUTTON_SEND | KN_BUTTON) ) { + input = KN_RETURN; + } + } + + /*............................................................... + Manage the message system (get rid of old messages) + ...............................................................*/ + if (Messages.Manage()) { + display = REDRAW_MESSAGE; + } + + /*............................................................... + Re-draw the messages & service keyboard input for any message + being edited. + ...............................................................*/ + i = Messages.Input(input); + + /*............................................................... + If 'Input' returned 1, it means refresh the message display. + ...............................................................*/ + if (i==1) { + Messages.Draw(); + } else { + + /*............................................................... + If 'Input' returned 2, it means redraw the message display. + ...............................................................*/ + if (i==2) { + display = REDRAW_MESSAGE; + } else { + + /*............................................................... + If 'input' returned 3, it means send the current message. + ...............................................................*/ + if (i==3) { + + sent_so_far = 0; + magic_number = MESSAGE_HEAD_MAGIC_NUMBER; + message_length = strlen(Messages.Get_Edit_Buf()); + crc = (unsigned short) + (Calculate_CRC(Messages.Get_Edit_Buf(), message_length) &0xffff); + + while (sent_so_far < message_length){ + SendPacket.Command = SERIAL_MESSAGE; + strcpy (SendPacket.Name, MPlayerName); + SendPacket.ID = Build_MPlayerID(MPlayerColorIdx, MPlayerHouse); + memcpy (SendPacket.Message, Messages.Get_Edit_Buf()+sent_so_far, COMPAT_MESSAGE_LENGTH-5); + *(SendPacket.Message + COMPAT_MESSAGE_LENGTH-5) = 0; + *((unsigned short*)(SendPacket.Message + COMPAT_MESSAGE_LENGTH-4)) = magic_number; + *((unsigned short*)(SendPacket.Message + COMPAT_MESSAGE_LENGTH-2)) = crc; + + /*.................................................................. + Send the message + ..................................................................*/ + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + NullModem.Service(); + + /*.................................................................. + Add the message to our own screen + ..................................................................*/ + sprintf(txt, Text_String (TXT_FROM), MPlayerName, SendPacket.Message); + Messages.Add_Message (txt, MPlayerTColors[MPlayerColorIdx], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, magic_number, crc); + + magic_number++; + sent_so_far += COMPAT_MESSAGE_LENGTH-5; + } + display = REDRAW_MESSAGE; + } + } + } + break; + } + + /*--------------------------------------------------------------------- + Detect editing of the name buffer, transmit new values to players + ---------------------------------------------------------------------*/ + if (strcmp (namebuf, MPlayerName)) { + strcpy (MPlayerName, namebuf); + transmit = 1; + changed = 1; + } + + /*--------------------------------------------------------------------- + If our Transmit flag is set, we need to send out a game option packet + ---------------------------------------------------------------------*/ + if (transmit && (TickCount.Time() - transmittime) > PACKET_RETRANS_TIME) { + SendPacket.Command = SERIAL_GAME_OPTIONS; + strcpy (SendPacket.Name, MPlayerName); +#ifdef PATCH + if (IsV107) { + SendPacket.Version = 1; + } else { + SendPacket.Version = 2; + } +#else + SendPacket.Version = Version_Number(); +#endif + SendPacket.House = MPlayerHouse; + SendPacket.Color = MPlayerColorIdx; + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + transmittime = TickCount.Time(); + transmit = 0; + } + + // + // send a timing packet if enough time has gone by. + // + if ( (TickCount.Time() - timingtime) > PACKET_TIMING_TIMEOUT) { + SendPacket.Command = SERIAL_TIMING; + SendPacket.ResponseTime = NullModem.Response_Time(); + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 0); + timingtime = TickCount.Time(); + } + + /*--------------------------------------------------------------------- + Check for an incoming message + ---------------------------------------------------------------------*/ + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { +// Smart_Printf( "received packet of length %d\n", packetlen ); + + lastmsgtime = TickCount.Time(); + + msg_timeout = 600; + + // are we getting our own packets back?? + + if (ReceivePacket.Command >= SERIAL_CONNECT && + ReceivePacket.Command < SERIAL_LAST_COMMAND && + ReceivePacket.Command != SERIAL_MESSAGE && + ReceivePacket.ID == ModemGameToPlay) { + + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + break; + } + + event = (EventClass *)&ReceivePacket; + if (event->Type <= EventClass::FRAMEINFO) { + if ( (TickCount.Time() - lastredrawtime) > PACKET_REDRAW_TIME) { + lastredrawtime = TickCount.Time(); + oppscorescreen = true; + display = REDRAW_MESSAGE; + parms_received = 1; + } + } else { + switch ( ReceivePacket.Command ) { + /*.................................................................. + Other system signs off: Give it time to receive my ACK, then show + a message. + ..................................................................*/ + case (SERIAL_SIGN_OFF): + starttime = TickCount.Time(); + while ( (TickCount.Time() - starttime) < 60) + NullModem.Service(); + CCMessageBox().Process(TXT_USER_SIGNED_OFF); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + recsignedoff = true; + break; + + /*.................................................................. + Game Options: Store all options; check my color & game version. + ..................................................................*/ + case (SERIAL_GAME_OPTIONS): + oppscorescreen = false; + gameoptions = true; + display = REDRAW_MESSAGE; + parms_received = 1; + + strcpy (TheirName, ReceivePacket.Name); + TheirColor = ReceivePacket.Color; + TheirHouse = ReceivePacket.House; + + /*............................................................... + Make sure I don't have the same color as the other guy. + ...............................................................*/ + if (MPlayerColorIdx == TheirColor) { + + // force transmitting of game options packet + + transmit = 1; + transmittime = 0; + + MPlayerColorIdx = TheirColor + 1; + if (MPlayerColorIdx >= 6) + MPlayerColorIdx = 0; + name_edt.Set_Color(MPlayerTColors[MPlayerColorIdx]); + name_edt.Flag_To_Redraw(); + display = REDRAW_COLORS; + } + + /*............................................................... + Save scenario settings. + ...............................................................*/ + MPlayerCredits = ReceivePacket.Credits; + MPlayerBases = ReceivePacket.IsBases; + MPlayerTiberium = ReceivePacket.IsTiberium; + MPlayerGoodies = ReceivePacket.IsGoodies; + MPlayerGhosts = ReceivePacket.IsGhosties; + BuildLevel = ReceivePacket.BuildLevel; + MPlayerUnitCount = ReceivePacket.UnitCount; + Seed = ReceivePacket.Seed; + Special = ReceivePacket.Special; + Options.GameSpeed = ReceivePacket.GameSpeed; + + if (MPlayerTiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + + /*............................................................... + Find the index of the scenario number; if it's not found, leave + it at -1. + ...............................................................*/ + ScenarioIdx = -1; + for (i = 0; i < MPlayerFilenum.Count(); i++) { + if (ReceivePacket.Scenario == MPlayerFilenum[i]) + ScenarioIdx = i; + } + + /*............................................................... + Check our version numbers; if they're incompatible, sign off. + ...............................................................*/ +#ifdef PATCH + if (IsV107) { + version = 1; + } else { + version = 2; + } +#else + version = Version_Number(); +#endif + if (ReceivePacket.Version > version) { + CCMessageBox().Process (TXT_YOURGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + } else { + if (ReceivePacket.Version < version) { + CCMessageBox().Process (TXT_DESTGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + } + } + + /*............................................................... + If this is the first game-options packet we've received, transmit + our options to him. + ...............................................................*/ + if (first) { + first = 0; + + // force transmitting of game options packet + + transmit = 1; + transmittime = 0; + } + break; + + /*.................................................................. + GO: Exit this routine with a success code. + ..................................................................*/ + case (SERIAL_GO): + + // + // calculated one way delay for a packet and overall delay + // to execute a packet + // + MPlayerMaxAhead = MAX( (ReceivePacket.ResponseTime / 8), 2); + + process = false; + rc = true; + break; + + /*.................................................................. + Incoming message: add to our list + ..................................................................*/ + case (SERIAL_MESSAGE): + oppscorescreen = false; + sprintf(txt, Text_String (TXT_FROM), ReceivePacket.Name, + ReceivePacket.Message); + magic_number = *((unsigned short*)(ReceivePacket.Message + COMPAT_MESSAGE_LENGTH-4)); + crc = *((unsigned short*)(ReceivePacket.Message + COMPAT_MESSAGE_LENGTH-2)); + Messages.Add_Message (txt, MPlayerTColors[MPlayerID_To_ColorIndex(ReceivePacket.ID)], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 1200, magic_number, crc); + display = REDRAW_MESSAGE; + break; + + // + // throw away timing packet + // + case (SERIAL_TIMING): + oppscorescreen = false; + break; + + // + // print msg waiting for opponent + // + case (SERIAL_SCORE_SCREEN): +// Smart_Printf( "received score screen\n" ); + oppscorescreen = true; + display = REDRAW_MESSAGE; + parms_received = 1; + break; + + default: + break; + } + } + } + + // if we haven't received a msg for 10 seconds exit + + //if ( ((TickCount.Time() - lastmsgtime) > msg_timeout) || + //(Winsock.Get_Connected() && Winsock.Get_Connection_Status == TcpipManagerClass::CONNECTION_LOST)) { + + if ( (TickCount.Time() - lastmsgtime) > msg_timeout) { + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + } + + + + + /*--------------------------------------------------------------------- + Service the connection + ---------------------------------------------------------------------*/ + NullModem.Service(); + + } /* end of while */ + + /*------------------------------------------------------------------------ + Sort player ID's, so we can execute commands in the same order on both + machines. + ------------------------------------------------------------------------*/ + if (rc) { + /*..................................................................... + Set the number of players in this game, and my ID + .....................................................................*/ + MPlayerCount = 2; + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + TheirID = Build_MPlayerID (TheirColor,TheirHouse); + + /*..................................................................... + Store every player's ID in the MPlayerID[] array. This array will + determine the order of event execution, so the ID's must be stored + in the same order on all systems. + .....................................................................*/ + if (TheirID < MPlayerLocalID) { + MPlayerID[0] = TheirID; + MPlayerID[1] = MPlayerLocalID; + strcpy (MPlayerNames[0], TheirName); + strcpy (MPlayerNames[1], MPlayerName); + } else { + MPlayerID[0] = MPlayerLocalID; + MPlayerID[1] = TheirID; + strcpy (MPlayerNames[0], MPlayerName); + strcpy (MPlayerNames[1], TheirName); + } + + /*..................................................................... + Get the scenario filename + .....................................................................*/ + Scenario = MPlayerFilenum[ScenarioIdx]; + + starttime = TickCount.Time(); + while ( ( NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_SENDING_TIMEOUT) ) + || ((TickCount.Time() - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + NullModem.Service(); + Keyboard::Check(); //Make sure the message loop gets called + } + + // clear queue to keep from doing any resends + NullModem.Init_Send_Queue(); + + } else { + if ( !recsignedoff ) { + /*..................................................................... + Broadcast my sign-off over my network + .....................................................................*/ + SendPacket.Command = SERIAL_SIGN_OFF; + SendPacket.Color = MPlayerLocalID; // use Color for ID + SendPacket.ID = ModemGameToPlay; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount.Time(); + while ( (NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_CANCEL_TIMEOUT) ) + || ((TickCount.Time() - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + if ( NullModem.Get_Message( &ReceivePacket, &packetlen ) > 0) { + + // are we getting our own packets back?? + + if (ReceivePacket.Command == SERIAL_SIGN_OFF + && ReceivePacket.ID == ModemGameToPlay) { + + // exit while + break; + } + } + + NullModem.Service(); + } + } + + Shutdown_Modem(); + } + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Show_Mouse(); + + /*------------------------------------------------------------------------ + Save any changes made to our options + ------------------------------------------------------------------------*/ + if (changed) { + Write_MultiPlayer_Settings (); + } + + return(rc); + +} /* end of Com_Show_Scenario_Dialog */ + + + +/*************************************************************************** + * Phone_Dialog -- Lets user edit phone directory & dial * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = dial the current phone book entry, false = cancel. * + * * + * WARNINGS: * + * Serial options must have been read from CC.INI. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static int Phone_Dialog (void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 280 *factor; // dialog width + int d_dialog_h = 150 *factor; // dialog height + int d_dialog_x = ((320 *factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200 *factor- d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 11 *factor; // ht of 6-pt text + int d_margin = 7 *factor; // margin width/height + + int d_phonelist_w = 268 *factor; + int d_phonelist_h = 87 *factor; + int d_phonelist_x = d_dialog_cx - (d_phonelist_w / 2); + int d_phonelist_y = d_dialog_y + d_margin + d_txt6_h + 11*factor; + + int d_add_w = 45*factor; + int d_add_h = 9*factor; + int d_add_x = d_dialog_cx - (d_add_w / 2) - d_margin - d_add_w; + int d_add_y = d_phonelist_y + d_phonelist_h + d_margin; + + int d_edit_w = 45*factor; + int d_edit_h = 9*factor; + int d_edit_x = d_dialog_cx - (d_edit_w / 2); + int d_edit_y = d_phonelist_y + d_phonelist_h + d_margin; + + int d_delete_w = 45*factor; + int d_delete_h = 9*factor; + int d_delete_x = d_dialog_cx + (d_delete_w / 2) + d_margin; + int d_delete_y = d_phonelist_y + d_phonelist_h + d_margin; + + int d_numedit_w = ( (PhoneEntryClass::PHONE_MAX_NUM - 1) * 6*factor) + 3*factor; + int d_numedit_h = 9*factor; + int d_numedit_x = d_dialog_cx - (d_numedit_w / 2); + int d_numedit_y = d_add_y + d_add_h + d_margin; + + int d_dial_w = 45*factor; + int d_dial_h = 9*factor; + int d_dial_x = d_dialog_cx - (d_numedit_w / 2) - d_margin - d_dial_w; + int d_dial_y = d_add_y + d_add_h + d_margin; + + int d_cancel_w = 45*factor; + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx + (d_numedit_w / 2) + d_margin; + int d_cancel_y = d_add_y + d_add_h + d_margin; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_PHONELIST = 100, + BUTTON_ADD, + BUTTON_EDIT, + BUTTON_DELETE, + BUTTON_DIAL, + BUTTON_CANCEL, + BUTTON_NUMEDIT, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + BOOL process = true; // process while true + KeyNumType input; + + char phone_num[ PhoneEntryClass::PHONE_MAX_NUM ] = { 0 }; // buffer for editing phone # + int rc; + int i; + int tabs[] = {123*factor, 207*factor}; // tabs for list box + char *item; // for removing items from list box + PhoneEntryClass *p_entry; // for creating / editing phonebook entries + int changed = 0; // 1 = save changes to INI file + int firsttime = 0; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + void const *up_button; + void const *down_button; + + if (InMainLoop){ + up_button = Hires_Retrieve("BTN-UP.SHP"); + down_button = Hires_Retrieve("BTN-DN.SHP"); + }else{ + up_button = Hires_Retrieve("BTN-UP2.SHP"); + down_button = Hires_Retrieve("BTN-DN2.SHP"); + } + + + ListClass phonelist(BUTTON_PHONELIST, + d_phonelist_x, d_phonelist_y, d_phonelist_w, d_phonelist_h, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + up_button, + down_button); + + TextButtonClass addbtn(BUTTON_ADD, TXT_ADD, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#ifdef FRENCH + d_add_x-4, d_add_y); +#else + d_add_x, d_add_y, d_add_w, d_add_h); +#endif + + TextButtonClass editbtn(BUTTON_EDIT, TXT_EDIT, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_edit_x, d_edit_y, d_edit_w, d_edit_h); + + TextButtonClass deletebtn(BUTTON_DELETE, TXT_DELETE_BUTTON, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#ifdef FRENCH + d_delete_x, d_delete_y); +#else + d_delete_x, d_delete_y, d_delete_w, d_delete_h); +#endif + + TextButtonClass dialbtn(BUTTON_DIAL, TXT_DIAL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +/* ###Change collision detected! C:\PROJECTS\CODE\NULLDLG.CPP... */ + d_dial_x, d_dial_y, d_dial_w, d_dial_h); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + d_cancel_x, d_cancel_y); +#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +#endif + + EditClass numedit (BUTTON_NUMEDIT, + phone_num, PhoneEntryClass::PHONE_MAX_NUM, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_numedit_x, d_numedit_y, d_numedit_w, d_numedit_h, EditClass::ALPHANUMERIC); + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &phonelist; + addbtn.Add_Tail(*commands); + editbtn.Add_Tail(*commands); + deletebtn.Add_Tail(*commands); + dialbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + numedit.Add_Tail(*commands); + dialbtn.Turn_On(); + + /* + ----------------------------- Various Inits ------------------------------ + */ + /*........................................................................ + Fill in the phone directory list box + ........................................................................*/ + phonelist.Set_Tabs(tabs); + Build_Phone_Listbox(&phonelist, &numedit, phone_num); + + if (CurPhoneIdx == -1) { + firsttime = 1; + } + + /* + ---------------------------- Processing loop ----------------------------- + */ + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_PHONE_LIST, d_dialog_x, d_dialog_y, d_dialog_w); + } + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + phonelist.Flag_To_Redraw(); + addbtn.Flag_To_Redraw(); + editbtn.Flag_To_Redraw(); + deletebtn.Flag_To_Redraw(); + dialbtn.Flag_To_Redraw(); + cancelbtn.Flag_To_Redraw(); + numedit.Flag_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + if ( firsttime ) { + numedit.Set_Focus(); + numedit.Flag_To_Redraw(); + input = commands->Input(); + firsttime = 0; + } + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + /*------------------------------------------------------------------ + New phone listing selected. + ------------------------------------------------------------------*/ + case (BUTTON_PHONELIST | KN_BUTTON): + /*............................................................... + Detect a change in the selected item; update CurPhoneIdx, and + the edit box buffer. + ...............................................................*/ + if (phonelist.Current_Index() != CurPhoneIdx) { + CurPhoneIdx = phonelist.Current_Index(); + strcpy (phone_num, PhoneBook[CurPhoneIdx]->Number); + numedit.Set_Text (phone_num, PhoneEntryClass::PHONE_MAX_NUM ); + changed = 1; + } + break; + + /*------------------------------------------------------------------ + Add a new entry + ------------------------------------------------------------------*/ + case (BUTTON_ADD | KN_BUTTON): + + /*............................................................... + Allocate a new phone book entry + ...............................................................*/ + p_entry = new PhoneEntryClass(); + p_entry->Name[0] = 0; + p_entry->Number[0] = 0; + p_entry->Settings.Port = 0; + p_entry->Settings.IRQ = -1; + p_entry->Settings.Baud = -1; + p_entry->Settings.DialMethod = DIAL_TOUCH_TONE; + p_entry->Settings.InitStringIndex = 0; + p_entry->Settings.CallWaitStringIndex = CALL_WAIT_CUSTOM; + p_entry->Settings.CallWaitString[0] = 0; + + /*............................................................... + Invoke the entry editor; if user clicks Save, add the new entry + to the list, and rebuild the list box. + ...............................................................*/ + if ( Edit_Phone_Dialog( p_entry ) ) { + PhoneBook.Add (p_entry); + Build_Phone_Listbox( &phonelist, &numedit, phone_num ); + /*............................................................ + Set the current listbox index to the newly-added item. + ............................................................*/ + for (i = 0; i < PhoneBook.Count(); i++) { + if (p_entry == PhoneBook[i]) { + CurPhoneIdx = i; + strcpy (phone_num, PhoneBook[CurPhoneIdx]->Number); + numedit.Set_Text (phone_num, PhoneEntryClass::PHONE_MAX_NUM ); + phonelist.Set_Selected_Index( CurPhoneIdx ); + } + } + changed = 1; + } else { + + /*............................................................... + If the user clicked Cancel, delete the entry & keep looping. + ...............................................................*/ + delete p_entry; + } + display = REDRAW_ALL; + break; + + /*------------------------------------------------------------------ + Edit the current entry + ------------------------------------------------------------------*/ + case (BUTTON_EDIT | KN_BUTTON): + + /*............................................................... + Do nothing if no entry is selected. + ...............................................................*/ + if (CurPhoneIdx==-1) + break; + + /*............................................................... + Allocate a new entry & copy the currently-selected entry into it + ...............................................................*/ + p_entry = new PhoneEntryClass(); + (*p_entry) = (*PhoneBook[CurPhoneIdx]); + + /*............................................................... + Pass the new entry to the entry editor; if the user selects OK, + copy the data back into our phone book. Rebuild the list so + the changes show up in the list box. + ...............................................................*/ + if ( Edit_Phone_Dialog( p_entry ) ) { + (*PhoneBook[CurPhoneIdx]) = (*p_entry); + Build_Phone_Listbox(&phonelist, &numedit, phone_num); + /*............................................................ + Set the current listbox index to the newly-added item. + ............................................................*/ + for (i = 0; i < PhoneBook.Count(); i++) { + if (PhoneBook[CurPhoneIdx] == PhoneBook[i]) { + CurPhoneIdx = i; + strcpy (phone_num, PhoneBook[CurPhoneIdx]->Number); + numedit.Set_Text (phone_num, PhoneEntryClass::PHONE_MAX_NUM ); + phonelist.Set_Selected_Index( CurPhoneIdx ); + } + } + changed = 1; + } + delete p_entry; + display = REDRAW_ALL; + break; + + /*------------------------------------------------------------------ + Delete the current entry + ------------------------------------------------------------------*/ + case (BUTTON_DELETE | KN_BUTTON): + + /*............................................................... + Do nothing if no entry is selected. + ...............................................................*/ + if (CurPhoneIdx == -1) + break; + + /*............................................................... + Delete the current item & rebuild the phone listbox + ...............................................................*/ + PhoneBook.Delete (CurPhoneIdx); + Build_Phone_Listbox(&phonelist, &numedit, phone_num); + + if (CurPhoneIdx == -1) { + *phone_num = 0; + numedit.Set_Text (phone_num, PhoneEntryClass::PHONE_MAX_NUM ); + } + changed = 1; + break; + + /*------------------------------------------------------------------ + Dial the current number + ------------------------------------------------------------------*/ + case (KN_RETURN): + dialbtn.IsPressed = true; + dialbtn.Draw_Me(true); + // fall thru + + case (BUTTON_DIAL | KN_BUTTON): + + /*............................................................... + If no item is selected, just dial the number in the phone # + edit box: + - Create a new phone entry + - Copy the phone number into it + - Set settings to defaults + ...............................................................*/ + if (CurPhoneIdx == -1 || + strcmp( PhoneBook[CurPhoneIdx]->Number, phone_num) ) { + + if ( strlen(phone_num) == 0) { // do not dial + dialbtn.IsPressed = false; + dialbtn.Flag_To_Redraw(); + break; + } + + p_entry = new PhoneEntryClass(); + strcpy( p_entry->Name, "NONAME" ); + strcpy( p_entry->Number, phone_num); + p_entry->Settings.Port = 0; + p_entry->Settings.IRQ = -1; + p_entry->Settings.Baud = -1; + p_entry->Settings.DialMethod = DIAL_TOUCH_TONE; + p_entry->Settings.InitStringIndex = 0; + p_entry->Settings.CallWaitStringIndex = CALL_WAIT_CUSTOM; + p_entry->Settings.CallWaitString[0] = 0; + + PhoneBook.Add (p_entry); + Build_Phone_Listbox(&phonelist, &numedit, phone_num); + /*............................................................ + Set the current listbox index to the newly-added item. + ............................................................*/ + for (i = 0; i < PhoneBook.Count(); i++) { + if (p_entry == PhoneBook[i]) { + CurPhoneIdx = i; + } + } + changed = 1; + } + + process = false; + rc = true; + break; + + /*------------------------------------------------------------------ + CANCEL: bail out + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + } + + } /* end of while */ + + /*------------------------------------------------------------------------ + Save any changes we've made to the phone list or settings + ------------------------------------------------------------------------*/ + if (changed) { + Write_MultiPlayer_Settings (); + } + + /*------------------------------------------------------------------------ + Clear the list box + ------------------------------------------------------------------------*/ + while (phonelist.Count()) { + item = (char *)phonelist.Get_Item(0); + phonelist.Remove_Item(item); + delete [] item; + } + + return(rc); + +} /* end of Phone_Dialog */ + + +/*************************************************************************** + * Build_Phone_Listbox -- [re]builds the phone entry listbox * + * * + * This routine rebuilds the phone list box from scratch; it also updates * + * the contents of the phone # edit field. * + * * + * INPUT: * + * list ptr to list box * + * edit ptr to edit box * + * buf ptr to buffer for phone # * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static void Build_Phone_Listbox (ListClass *list, EditClass *edit, char *buf) +{ + int i; + char *item; + char phonename[21]; + char phonenum[15]; + + /*........................................................................ + Clear the list + ........................................................................*/ + while (list->Count()) { + item = (char *)(list->Get_Item(0)); + list->Remove_Item(item); + delete [] item; + } + + /* + ** Now sort the phone list by name then number + */ + qsort ((void *)(&PhoneBook[0]), PhoneBook.Count(), sizeof(class PhoneEntryClass *), Phone_Compare); + + /*........................................................................ + Build the list + ........................................................................*/ + for (i = 0; i < PhoneBook.Count(); i++) { + item = new char[80]; + if ( !(strlen( PhoneBook[i]->Name )) ) { + strcpy( phonename, " " ); + } else { + strncpy( phonename, PhoneBook[i]->Name, 20 ); + phonename[21] = 0; + } + + if ( !(strlen( PhoneBook[i]->Number )) ) { + strcpy( phonenum, " " ); + } else { + if ( strlen( PhoneBook[i]->Number ) < 14) { + strcpy( phonenum, PhoneBook[i]->Number ); + } else { + strncpy( phonenum, PhoneBook[i]->Number, 12 ); + phonenum[12] = 0; + strcat( phonenum, "..." ); + } + } + + if (PhoneBook[i]->Settings.Baud != -1) { + sprintf(item,"%s\t%s\t%d", phonename, phonenum, + PhoneBook[i]->Settings.Baud); + } else { + sprintf(item,"%s\t%s\t[%s]", phonename, phonenum, + Text_String(TXT_DEFAULT)); + } + list->Add_Item(item); + } + list->Flag_To_Redraw(); + + /*........................................................................ + Init the current phone book index + ........................................................................*/ + if (list->Count() == 0 || CurPhoneIdx < -1) { + CurPhoneIdx = -1; + } else { + if (CurPhoneIdx >= list->Count() ) { + CurPhoneIdx = 0; + } + } + + /*........................................................................ + Fill in phone number edit buffer + ........................................................................*/ + if (CurPhoneIdx > -1) { + strcpy (buf, PhoneBook[CurPhoneIdx]->Number); + edit->Set_Text (buf, PhoneEntryClass::PHONE_MAX_NUM ); + list->Set_Selected_Index( CurPhoneIdx ); + } +} + + +/*************************************************************************** + * Phone_Compare -- for qsort * + * * + * INPUT: * + * p1,p2 ptrs to elements to compare * + * * + * OUTPUT: * + * 0 = same, -1 = (*p1) goes BEFORE (*p2), 1 = (*p1) goes AFTER (*p2) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=========================================================================*/ +static int Phone_Compare (const void *p1, const void *p2) +{ + class PhoneEntryClass *pe1,*pe2; + int result; + + pe1 = *((class PhoneEntryClass **)p1); + pe2 = *((class PhoneEntryClass **)p2); + + result = strcmp( pe1->Name, pe2->Name ); + + // if strings are equal then check the phone number + + if ( !result ) { + result = strcmp( pe1->Number, pe2->Number ); + } + + return(result); +} + + +/*************************************************************************** + * Edit_Phone_Dialog -- lets user edit a phone book entry * + * * + * INPUT: * + * phone entry to edit * + * * + * OUTPUT: * + * true = OK, false = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static int Edit_Phone_Dialog (PhoneEntryClass *phone) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 230 *factor; // dialog width + int d_dialog_h = 105*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord +// D_DIALOG_Y = ((200 - D_DIALOG_H) / 2); // dialog y-coord + int d_dialog_y = ((136*factor - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 11 *factor; // ht of 6-pt text + int d_margin = 7 *factor; // margin width/height + + int d_name_w = ( (PhoneEntryClass::PHONE_MAX_NAME - 1) * 6) + 3 *factor; + int d_name_h = 9 *factor; + int d_name_x = d_dialog_x + (((d_dialog_w - d_name_w) * 3) / 4) - 5 *factor; + int d_name_y = d_dialog_y + 25 *factor; + + int d_number_w = ( (PhoneEntryClass::PHONE_MAX_NUM - 1) * 6) + 3 *factor; + int d_number_h = 9 *factor; + int d_number_x = d_dialog_x + (((d_dialog_w - d_number_w) * 3) / 4) - 5 *factor; + int d_number_y = d_name_y + d_name_h + d_margin; + +#if (GERMAN | FRENCH) + int d_default_w = 130 *factor; +#else + int d_default_w = 104 *factor; +#endif + int d_default_h = 9 *factor; + int d_default_x = d_dialog_cx - (d_default_w / 2); + int d_default_y = d_number_y + d_number_h + d_margin; + +#if (GERMAN | FRENCH) + int d_custom_w = 130 *factor; +#else + int d_custom_w = 100 *factor; +#endif + int d_custom_h = 9 *factor; + int d_custom_x = d_dialog_cx - (d_default_w / 2); + int d_custom_y = d_default_y + d_default_h + d_margin; + +#if (GERMAN | FRENCH) + int d_save_w = 55 *factor; +#else + int d_save_w = 45 *factor; +#endif + int d_save_h = 9 *factor; + int d_save_x = d_dialog_cx - d_margin - d_save_w; + int d_save_y = d_dialog_y + d_dialog_h - d_margin - d_save_h; + +#if (GERMAN | FRENCH) + int d_cancel_w = 55 *factor; +#else + int d_cancel_w = 45 *factor; +#endif + int d_cancel_h = 9 *factor; + int d_cancel_x = d_dialog_cx + d_margin; + int d_cancel_y = d_dialog_y + d_dialog_h - d_margin - d_cancel_h; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_NAME = 100, + BUTTON_NUMBER, + BUTTON_DEFAULT, + BUTTON_CUSTOM, + BUTTON_SAVE, + BUTTON_CANCEL, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + BOOL process = true; // process while true + KeyNumType input; + + char namebuf[PhoneEntryClass::PHONE_MAX_NAME] = { 0 }; // buffer for editing name + char numbuf[PhoneEntryClass::PHONE_MAX_NUM] = { 0 }; // buffer for editing phone # + int rc; + SerialSettingsType settings; + int custom = 0; + int firsttime = 1; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + EditClass nameedit (BUTTON_NAME, + namebuf, PhoneEntryClass::PHONE_MAX_NAME, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_name_x, d_name_y, d_name_w, d_name_h, EditClass::ALPHANUMERIC); + + EditClass numedit (BUTTON_NUMBER, + numbuf, PhoneEntryClass::PHONE_MAX_NUM, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_number_x, d_number_y, d_number_w, d_number_h, EditClass::ALPHANUMERIC); + + TextButtonClass defaultbtn(BUTTON_DEFAULT, TXT_DEFAULT_SETTINGS, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_default_x, d_default_y, d_default_w, d_default_h); + + TextButtonClass custombtn(BUTTON_CUSTOM, TXT_CUSTOM_SETTINGS, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_custom_x, d_custom_y, d_custom_w, d_custom_h); + + TextButtonClass savebtn(BUTTON_SAVE, TXT_SAVE_BUTTON, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_save_x, d_save_y, d_save_w, d_save_h); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &nameedit; + numedit.Add_Tail(*commands); + defaultbtn.Add_Tail(*commands); + custombtn.Add_Tail(*commands); + savebtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + + /* + ----------------------------- Various Inits ------------------------------ + */ + /*........................................................................ + Init the settings; if the phone entry is set to use defaults, init our + settings to sensible values (in case we invoke the setting editor); + otherwise, copy the entry's settings. + ........................................................................*/ + if (phone->Settings.Port == 0 || phone->Settings.IRQ == -1 || + phone->Settings.Baud == -1) { + settings = SerialDefaults; + defaultbtn.Turn_On(); + custom = false; + } else { + settings = phone->Settings; + custombtn.Turn_On(); + custom = true; + } + + strcpy (namebuf, phone->Name); + nameedit.Set_Text (namebuf, PhoneEntryClass::PHONE_MAX_NAME); + + strcpy (numbuf, phone->Number); + numedit.Set_Text (numbuf, PhoneEntryClass::PHONE_MAX_NUM); + + /* + ---------------------------- Processing loop ----------------------------- + */ + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_PHONE_LISTING, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print (TXT_NAME_COLON, + d_name_x - 5, d_name_y + 1, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Fancy_Text_Print (TXT_NUMBER_COLON, + d_number_x - 5, d_number_y + 1, + CC_GREEN, TBLACK, + TPF_RIGHT | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + } + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + if ( firsttime ) { + nameedit.Set_Focus(); + nameedit.Flag_To_Redraw(); + input = commands->Input(); + firsttime = 0; + } + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + case (BUTTON_NAME | KN_BUTTON): + numedit.Set_Focus(); + numedit.Flag_To_Redraw(); + break; + +// case (BUTTON_NUMBER | KN_BUTTON): +// nameedit.Clear_Focus(); +// nameedit.Flag_To_Redraw(); +// break; + + /*------------------------------------------------------------------ + Use Default Serial Settings + ------------------------------------------------------------------*/ + case (BUTTON_DEFAULT | KN_BUTTON): + custombtn.Turn_Off(); + defaultbtn.Turn_On(); + custom = 0; + break; + + /*------------------------------------------------------------------ + Use Custom Serial Settings + ------------------------------------------------------------------*/ + case (BUTTON_CUSTOM | KN_BUTTON): + if (Com_Settings_Dialog (&settings)) { + custombtn.Turn_On(); + defaultbtn.Turn_Off(); + } + custom = 1; + display = REDRAW_ALL; + break; + + /*------------------------------------------------------------------ + CANCEL: bail out + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + + /*------------------------------------------------------------------ + Save: save changes + ------------------------------------------------------------------*/ + case (KN_RETURN): + case (BUTTON_SAVE | KN_BUTTON): + process = false; + rc = true; + break; + } + + } /* end of while */ + + /*------------------------------------------------------------------------ + If 'Save', save all current settings + ------------------------------------------------------------------------*/ + if (rc) { + strcpy( phone->Name, strupr( namebuf ) ); + + // if nothing was entered then make if NONAME + + if ( !(phone->Name[0]) ) { + strcpy( phone->Name, "NONAME" ); + } + + strcpy( phone->Number, strupr( numbuf ) ); + + if (custom) { + phone->Settings = settings; + } else { + phone->Settings.Port = 0; + phone->Settings.IRQ = -1; + phone->Settings.Baud = -1; + phone->Settings.DialMethod = DIAL_TOUCH_TONE; + phone->Settings.InitStringIndex = 0; + phone->Settings.CallWaitStringIndex = CALL_WAIT_CUSTOM; + phone->Settings.CallWaitString[0] = 0; + } + } + + return(rc); + + +} /* end of Edit_Phone_Dialog */ + + +static bool Dial_Modem( SerialSettingsType *settings, bool reconnect ) +{ + bool connected = false; + DialStatusType dialstatus; + int modemstatus; + + /* + ** Turn modem servicing off in the callback routine. + */ + ModemService = false; + + // save for later to reconnect + + DialSettings = settings; + + modemstatus = NullModem.Get_Modem_Status(); + if (reconnect) { + if ( (modemstatus & CD_SET) ) { + connected = true; + ModemService = true; + return( connected ); + } + } else { + if ( (modemstatus & CD_SET) ) { + NullModem.Hangup_Modem(); + ModemService = false; + } + } + + NullModem.Setup_Modem_Echo( Modem_Echo ); + + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); +#if (0) + /* + ** If our first attempt to detect the modem failed, and we're at + ** 14400 or 28800, bump up to the next baud rate & try again. + */ + switch (settings->Baud) { + case 9600: + case 19200: + case 38400: + //CCMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + ModemService = true; + return( connected ); + + case 14400: + settings->Baud = 19200; + Shutdown_Modem(); + Init_Null_Modem(settings); + NullModem.Setup_Modem_Echo( Modem_Echo ); + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + //CCMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + ModemService = true; + return( connected ); + } + break; + + case 28800: + settings->Baud = 38400; + Shutdown_Modem(); + Init_Null_Modem(settings); + NullModem.Setup_Modem_Echo( Modem_Echo ); + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + //CCMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + ModemService = true; + return( connected ); + } + break; + } +#else //(0) + ModemService = true; + return( connected ); + +#endif //(0) + } + else if ( modemstatus == -1 ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + CCMessageBox().Process( TXT_ERROR_IN_INITSTRING ); +// CCMessageBox().Process( "Error in the InitString." ); + ModemService = true; + return( connected ); + } + + dialstatus = NullModem.Dial_Modem( DialString, settings->DialMethod, reconnect ); + + if (reconnect) { + /* + --------------------------- Redraw the display --------------------------- + */ + HiddenPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + } + + switch ( dialstatus ) { + case DIAL_CONNECTED: + connected = true; + break; + + case DIAL_NO_CARRIER: + CCMessageBox().Process(TXT_NO_CARRIER); + connected = false; + break; + + case DIAL_BUSY: + CCMessageBox().Process(TXT_LINE_BUSY); + connected = false; + break; + + case DIAL_ERROR: + CCMessageBox().Process(TXT_NUMBER_INVALID); + connected = false; + break; + + case DIAL_CANCELED: + NullModem.Hangup_Modem(); + ModemService = false; + CCMessageBox().Process(TXT_DIALING_CANCELED); + connected = false; + break; + } + + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + + ModemService = true; + return( connected ); + +} /* end of Dial_Modem */ + + +static bool Answer_Modem( SerialSettingsType *settings, bool reconnect ) +{ + bool connected = false; + DialStatusType dialstatus; + int modemstatus; + +/* ###Change collision detected! C:\PROJECTS\CODE\NULLDLG.CPP... */ + /* + ** Turn modem servicing off in the callback routine. + */ + ModemService = false; + + // save for later to reconnect + + DialSettings = settings; + + modemstatus = NullModem.Get_Modem_Status(); + if (reconnect) { + if ( (modemstatus & CD_SET) ) { + connected = true; + ModemService = true; + return( connected ); + } + } else { + if ( (modemstatus & CD_SET) ) { + NullModem.Hangup_Modem(); + ModemService = false; + } + } + + NullModem.Setup_Modem_Echo( Modem_Echo ); + + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + +#if (0) + /* + ** If our first attempt to detect the modem failed, and we're at + ** 14400 or 28800, bump up to the next baud rate & try again. + */ + switch (settings->Baud) { + case 9600: + case 19200: + case 38400: + //CCMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + ModemService = true; + return( connected ); + + case 14400: + settings->Baud = 19200; + Shutdown_Modem(); + Init_Null_Modem(settings); + NullModem.Setup_Modem_Echo( Modem_Echo ); + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + //CCMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + ModemService = true; + return( connected ); + } + break; + + case 28800: + settings->Baud = 38400; + Shutdown_Modem(); + Init_Null_Modem(settings); + NullModem.Setup_Modem_Echo( Modem_Echo ); + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + //CCMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + ModemService = true; + return( connected ); + } + break; + } +#else //(0) + + ModemService = true; + return( connected ); +#endif //(0) + + } + else if ( modemstatus == -1 ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + CCMessageBox().Process( TXT_ERROR_IN_INITSTRING ); + ModemService = true; + return( connected ); + } + + dialstatus = NullModem.Answer_Modem( reconnect ); + + switch ( dialstatus ) { + case DIAL_CONNECTED: + connected = true; + break; + + case DIAL_NO_CARRIER: + CCMessageBox().Process(TXT_NO_CARRIER); + connected = false; + break; + +// case DIAL_BUSY: +// CCMessageBox().Process(TXT_LINE_BUSY); +// connected = false; +// break; + + case DIAL_ERROR: + // Error occurred but not neccessarily an invalid number + //CCMessageBox().Process(TXT_NUMBER_INVALID); + connected = false; + break; + + case DIAL_CANCELED: + CCMessageBox().Process(TXT_ANSWERING_CANCELED); + connected = false; + break; + } + + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + + ModemService = true; + return( connected ); + +} /* end of Answer_Modem */ + + +static void Modem_Echo( char c ) +{ + if (NullModem.EchoCount < (NullModem.EchoSize - 1) ) { + *(NullModem.EchoBuf + NullModem.EchoCount) = c; + *(NullModem.EchoBuf + NullModem.EchoCount + 1) = 0; + NullModem.EchoCount++; + } else { + //Smart_Printf( "Echo buffer full!!!\n" ); + } + +} /* end of Modem_Echo */ + + +void Smart_Printf( char *format, ... ) +{ + va_list arglist; + char buf[501]; + + + va_start(arglist,format); + vsprintf(buf,format,arglist); + va_end(arglist); + + if (Debug_Smart_Print) { + if (Special.IsMonoEnabled) { +// Mono_Set_Cursor(0,0); + Mono_Printf("%s",buf); + } else { +// Mono_Printf("%s",buf); + printf("%s",buf); + } + } else { + if (Debug_Heap_Dump) { + printf("%s",buf); + } + } +} + + +void Hex_Dump_Data( char *buffer, int length ) +{ + int i; + int offset = 0; + char buff[10]; + char ptr[16]; + char c; + + + while (length >= 16) { + memcpy( ptr, (buffer + offset), 16); + + Smart_Printf("%05lX ", offset); + + for (i = 0; i < 16; i++) { + + c = ptr[i]; + itoh(c, buff); + + if (!(i & 0x3) && i) { + Smart_Printf("?"); + } + + Smart_Printf("%s ", buff); + } + + Smart_Printf(" "); + + for (i = 0; i < 16; i++) { + c = ptr[i]; + + if (c && ((c < 7) || (c > 11)) && (c != 13)) { + Smart_Printf("%c", c); + } else { + Smart_Printf("."); + } + } + + Smart_Printf("\n"); + + offset += 16; + length -= 16; + } + + if (length) { + memcpy( ptr, (buffer + offset), 16); + + Smart_Printf("%05lX ", offset); + + for (i = 0; i < 16; i++) { + if (i < length) { + c = ptr[i]; + itoh(c, buff); + if (!(i & 0x3) && i) { + Smart_Printf("?"); + } + Smart_Printf("%s ", buff); + } else { + if (!(i & 0x3) && i) { + Smart_Printf(" "); + } + Smart_Printf(" "); + } + } + + Smart_Printf(" "); + + for (i = 0; i < length; i++) { + + c = ptr[i]; + + if (c && ((c < 7) || (c > 11)) && (c != 13)) { + Smart_Printf("%c", c); + } else { + Smart_Printf("."); + } + } + + Smart_Printf("\n"); + } + +} /* end of Hex_Dump_Data */ + + +void itoh( int i, char *s) +{ + + int nibble, loop; + +// *s++ = '0'; +// *s++ = 'x'; + + if (i == 0) { + *s++ = '0'; + *s++ = '0'; + } else { + for (loop = 1; loop >= 0; loop--) { + nibble = (i >> (loop << 2)) & 0x000F; + + /* decimal range */ + if (nibble < 10) { + *s++ = '0' + nibble; + } else { + *s++ = 'A' + (nibble - 10); + } + } + } + *s = 0; /* null terminate it */ +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#if (0) + +int Com_Fake_Scenario_Dialog(void) +{ + bool display = true; // redraw level + bool process = true; // process while true + + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int transmit; // 1 = re-transmit new game options + int parms_received = 1; // 1 = game options received + int changed = 0; // 1 = user has changed an option + + int rc; + int recsignedoff = false; + int version; + unsigned long starttime; + unsigned long timingtime; + unsigned long lastmsgtime; + unsigned long lastredrawtime; + unsigned long transmittime = 0; + unsigned long theirresponsetime; + int packetlen; + bool oppscorescreen = false; + bool gameoptions = false; + EventClass *event; // event ptr + unsigned long msg_timeout = 60*60; // init to 60 seconds + + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_CANCEL = 100, + }; + + /*........................................................................ + Dialog variables + ........................................................................*/ + TextButtonClass * buttons = 0; + + KeyNumType input; + + int x,y; + int width=0; + int height=0; + char text_buffer[80*3]; + + const char *current_status_string = Text_String(TXT_WINSOCK_CONNECTING); + strcpy(text_buffer, current_status_string); + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + + Format_Window_String(text_buffer, SeenBuff.Get_Height(), width, height); + + width = MAX(width, 50*factor); + width += 40*factor; + height += 60*factor; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8*factor)) >> 1), + y + height - (FontHeight + FontYSpacing + 2*factor) - 5*factor); + + Set_Logic_Page(SeenBuff); + buttons = &cancelbtn; + + buttons->Flag_List_To_Redraw(); + process = true; + + /*........................................................................ + Init other scenario parameters + ........................................................................*/ + Special.IsTGrowth = MPlayerTiberium; + Special.IsTSpread = MPlayerTiberium; + transmit = 1; + + /*........................................................................ + Init random-number generator, & create a seed to be used for all random + numbers from here on out + ........................................................................*/ + randomize(); + //Seed = rand(); + + /* + ---------------------------- Processing loop ----------------------------- + */ + NullModem.Reset_Response_Time(); // clear response time + theirresponsetime = 10000; // initialize to an invalid value + timingtime = lastmsgtime = lastredrawtime = TickCount.Time(); + + + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=true; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + /* + ..................... Draw the background ....................... + */ + Dialog_Box(x, y, width, height); + /* + ....................... Draw the labels ......................... + */ + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(text_buffer, x + 20*factor, y + 25*factor, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + buttons->Draw_All(); + Show_Mouse(); + display = false; + } + + /* + ............................ Process input ............................ + */ + input = buttons->Input(); + switch (input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + + default: + break; + } + + + /*--------------------------------------------------------------------- + If our Transmit flag is set, we need to send out a game option packet. + This message requires an ACK. The first time through the loop, transmit + should be set, so we send out our default options; we'll then send + any changes we make to the defaults. + ---------------------------------------------------------------------*/ + if (transmit && (TickCount.Time() - transmittime) > PACKET_RETRANS_TIME) { + SendPacket.Command = SERIAL_GAME_OPTIONS; + strcpy (SendPacket.Name, MPlayerName); +#ifdef PATCH + if (IsV107) { + SendPacket.Version = 1; + } else { + SendPacket.Version = 2; + } +#else + SendPacket.Version = Version_Number(); +#endif + SendPacket.House = MPlayerHouse; + SendPacket.Color = MPlayerColorIdx; + + SendPacket.Scenario = MPlayerFilenum[ScenarioIdx]; + + SendPacket.Credits = MPlayerCredits; + SendPacket.IsBases = MPlayerBases; + SendPacket.IsTiberium = MPlayerTiberium; + SendPacket.IsGoodies = MPlayerGoodies; + SendPacket.IsGhosties = MPlayerGhosts; + SendPacket.BuildLevel = BuildLevel; + SendPacket.UnitCount = MPlayerUnitCount; + SendPacket.Seed = Seed; + SendPacket.Special = Special; + SendPacket.GameSpeed = Options.GameSpeed; + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + transmittime = TickCount.Time(); + transmit = 0; + } + + // + // send a timing packet if enough time has gone by. + // + if ( (TickCount.Time() - timingtime) > PACKET_TIMING_TIMEOUT) { + SendPacket.Command = SERIAL_TIMING; + SendPacket.ResponseTime = NullModem.Response_Time(); + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 0); + timingtime = TickCount.Time(); + } + + /*--------------------------------------------------------------------- + Check for an incoming message + ---------------------------------------------------------------------*/ + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + + lastmsgtime = TickCount.Time(); + msg_timeout = 600; // reset timeout value to 10 seconds + // (only the 1st time through is 20 seconds) + + + if (ReceivePacket.Command >= SERIAL_CONNECT && + ReceivePacket.Command < SERIAL_LAST_COMMAND && + ReceivePacket.Command != SERIAL_MESSAGE && + ReceivePacket.ID == ModemGameToPlay) { + + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + break; + } + + event = (EventClass *)&ReceivePacket; + if (event->Type <= EventClass::FRAMEINFO) { + if ( (TickCount.Time() - lastredrawtime) > PACKET_REDRAW_TIME) { + lastredrawtime = TickCount.Time(); + oppscorescreen = true; + parms_received = 1; + } + } else { + switch ( ReceivePacket.Command ) { + /*.................................................................. + Sign-off: Give the other machine time to receive my ACK, display a + message, and exit. + ..................................................................*/ + case (SERIAL_SIGN_OFF): + starttime = TickCount.Time(); + while (TickCount.Time() - starttime < 60) + NullModem.Service(); + CCMessageBox().Process(TXT_USER_SIGNED_OFF); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + recsignedoff = true; + break; + + /*.................................................................. + Game Options: Store the other machine's name, color & house; + If they've picked the same color as myself, re-transmit my settings + to force him to choose a different color. (Com_Show_Scenario_Dialog + is responsible for ensuring the colors are different.) + ..................................................................*/ + case (SERIAL_GAME_OPTIONS): + oppscorescreen = false; + gameoptions = true; + strcpy (TheirName, ReceivePacket.Name); + TheirColor = ReceivePacket.Color; + TheirHouse = ReceivePacket.House; + transmit = 1; + + parms_received = 1; + + /*............................................................... + Check the version number of the other system. + ...............................................................*/ +#ifdef PATCH + if (IsV107) { + version = 1; + } else { + version = 2; + } +#else + version = Version_Number(); +#endif + if (ReceivePacket.Version > version) { + CCMessageBox().Process (TXT_YOURGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + } else { + if (ReceivePacket.Version < version) { + CCMessageBox().Process (TXT_DESTGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + } + } + break; + + /*.................................................................. + Incoming message: add to our list + ..................................................................*/ + + // + // get their response time + // + case (SERIAL_TIMING): + oppscorescreen = false; + theirresponsetime = ReceivePacket.ResponseTime; + + if ( !gameoptions ) { + transmit = 1; + }else{ + process = false; + rc = true; + } + break; + + // + // print msg waiting for opponent + // + case (SERIAL_SCORE_SCREEN): + oppscorescreen = true; + parms_received = 1; + break; + + default: + break; + } + } + } + + // if we haven't received a msg for 10 seconds exit + + if ( (TickCount.Time() - lastmsgtime) > msg_timeout) { + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + } + + /*--------------------------------------------------------------------- + Service the connection + ---------------------------------------------------------------------*/ + NullModem.Service(); + + } /* end of while */ + + /*------------------------------------------------------------------------ + Sort player ID's, so we can execute commands in the same order on both + machines. + ------------------------------------------------------------------------*/ + if (rc) { + /*..................................................................... + Set the number of players in this game, and my ID + .....................................................................*/ + MPlayerCount = 2; + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + TheirID = Build_MPlayerID (TheirColor,TheirHouse); + + /*..................................................................... + Store every player's ID in the MPlayerID[] array. This array will + determine the order of event execution, so the ID's must be stored + in the same order on all systems. + .....................................................................*/ + if (TheirID < MPlayerLocalID) { + MPlayerID[0] = TheirID; + MPlayerID[1] = MPlayerLocalID; + strcpy (MPlayerNames[0], TheirName); + strcpy (MPlayerNames[1], MPlayerName); + } else { + MPlayerID[0] = MPlayerLocalID; + MPlayerID[1] = TheirID; + strcpy (MPlayerNames[0], MPlayerName); + strcpy (MPlayerNames[1], TheirName); + } + + /*..................................................................... + Get the scenario filename + .....................................................................*/ + Scenario = MPlayerFilenum[ScenarioIdx]; + + /*..................................................................... + Send all players the GO packet. + .....................................................................*/ + SendPacket.Command = SERIAL_GO; + SendPacket.ResponseTime = NullModem.Response_Time(); + if ( theirresponsetime == 10000 ) { +// Mono_Clear_Screen(); +// Smart_Printf( "Did not receive their response time!!!!!!!\n" ); +// Get_Key(); + } else { + if (SendPacket.ResponseTime < theirresponsetime) { + SendPacket.ResponseTime = theirresponsetime; + } + } + + // + // calculated one way delay for a packet and overall delay to execute + // a packet + // + ////////MPlayerMaxAhead = MAX( (SendPacket.ResponseTime / 8), 2); + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount.Time(); + while ( ( NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_SENDING_TIMEOUT) ) + || ((TickCount.Time() - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + NullModem.Service(); + Keyboard::Check(); //Make sure the message loop gets called + } + + // clear queue to keep from doing any resends + NullModem.Init_Send_Queue(); + + } else { + if ( !recsignedoff ) { + /*..................................................................... + Broadcast my sign-off over my network + .....................................................................*/ + SendPacket.Command = SERIAL_SIGN_OFF; + SendPacket.Color = MPlayerLocalID; // use Color for ID + SendPacket.ID = ModemGameToPlay; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount.Time(); + while ( (NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_CANCEL_TIMEOUT) ) + || ((TickCount.Time() - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + if ( NullModem.Get_Message( &ReceivePacket, &packetlen ) > 0) { + + // are we getting our own packets back?? + + if (ReceivePacket.Command == SERIAL_SIGN_OFF + && ReceivePacket.ID == ModemGameToPlay) { + + // exit while + break; + } + } + + NullModem.Service(); + } + } + + Shutdown_Modem(); + } + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Show_Mouse(); + + /*------------------------------------------------------------------------ + Save any changes made to our options + ------------------------------------------------------------------------*/ + if (changed) { + Write_MultiPlayer_Settings (); + } + + return(rc); + +} /* end of Com_Scenario_Dialog */ + + + + + + + + + + + + + + + + + + + + + + + + + + +int Com_Show_Fake_Scenario_Dialog(void) +{ + /*........................................................................ + Dialog variables + ........................................................................*/ + bool display = true; // redraw level + BOOL process = true; // process while true + + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int transmit; // 1 = re-transmit new game options + int first; // 1 = no packets received yet + int parms_received = 0; // 1 = game options received + int changed = 0; // 1 = user has changed an option + + int rc; + int recsignedoff = 0; + int i; + int version; + unsigned long starttime; + unsigned long timingtime; + unsigned long lastmsgtime; + unsigned long lastredrawtime; + unsigned long transmittime = 0; + int packetlen; + bool oppscorescreen = false; + bool gameoptions = false; + EventClass *event; // event ptr + unsigned long msg_timeout = 60*60; // init to 60 seconds + + + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_CANCEL = 100, + }; + + /*........................................................................ + Dialog variables + ........................................................................*/ + TextButtonClass * buttons = 0; + + KeyNumType input; + + int x,y; + int width=0; + int height=0; + char text_buffer[80*3]; + + const char *current_status_string = Text_String(TXT_WINSOCK_CONNECTING); + strcpy(text_buffer, current_status_string); + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + + Format_Window_String(text_buffer, SeenBuff.Get_Height(), width, height); + + width = MAX(width, 50*factor); + width += 40*factor; + height += 60*factor; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8*factor)) >> 1), + y + height - (FontHeight + FontYSpacing + 2*factor) - 5*factor); + + Set_Logic_Page(SeenBuff); + buttons = &cancelbtn; + + buttons->Flag_List_To_Redraw(); + + + transmit = 1; + first = 1; + + + /* + ---------------------------- Processing loop ----------------------------- + */ + NullModem.Reset_Response_Time(); // clear response time + timingtime = lastmsgtime = lastredrawtime = TickCount.Time(); + + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=true; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + Set_Palette(Palette); + /* + ..................... Draw the background ....................... + */ + Dialog_Box(x, y, width, height); + /* + ....................... Draw the labels ......................... + */ + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(text_buffer, x + 20*factor, y + 25*factor, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + buttons->Draw_All(); + Show_Mouse(); + display = false; + } + + + /* + ............................ Process input ............................ + */ + input = buttons->Input(); + switch (input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + + default: + break; + } + + + + /*--------------------------------------------------------------------- + If our Transmit flag is set, we need to send out a game option packet + ---------------------------------------------------------------------*/ + if (transmit && (TickCount.Time() - transmittime) > PACKET_RETRANS_TIME) { + SendPacket.Command = SERIAL_GAME_OPTIONS; + strcpy (SendPacket.Name, MPlayerName); +#ifdef PATCH + if (IsV107) { + SendPacket.Version = 1; + } else { + SendPacket.Version = 2; + } +#else + SendPacket.Version = Version_Number(); +#endif + SendPacket.House = MPlayerHouse; + SendPacket.Color = MPlayerColorIdx; + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + transmittime = TickCount.Time(); + transmit = 0; + } + + // + // send a timing packet if enough time has gone by. + // + if ( (TickCount.Time() - timingtime) > PACKET_TIMING_TIMEOUT) { + SendPacket.Command = SERIAL_TIMING; + SendPacket.ResponseTime = NullModem.Response_Time(); + SendPacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 0); + timingtime = TickCount.Time(); + } + + /*--------------------------------------------------------------------- + Check for an incoming message + ---------------------------------------------------------------------*/ + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + + lastmsgtime = TickCount.Time(); + + msg_timeout = 600; + + // are we getting our own packets back?? + + if (ReceivePacket.Command >= SERIAL_CONNECT && + ReceivePacket.Command < SERIAL_LAST_COMMAND && + ReceivePacket.Command != SERIAL_MESSAGE && + ReceivePacket.ID == ModemGameToPlay) { + + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + break; + } + + event = (EventClass *)&ReceivePacket; + if (event->Type <= EventClass::FRAMEINFO) { + if ( (TickCount.Time() - lastredrawtime) > PACKET_REDRAW_TIME) { + lastredrawtime = TickCount.Time(); + oppscorescreen = true; + display = false; + parms_received = 1; + } + } else { + switch ( ReceivePacket.Command ) { + /*.................................................................. + Other system signs off: Give it time to receive my ACK, then show + a message. + ..................................................................*/ + case (SERIAL_SIGN_OFF): + starttime = TickCount.Time(); + while ( (TickCount.Time() - starttime) < 60) + NullModem.Service(); + CCMessageBox().Process(TXT_USER_SIGNED_OFF); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + recsignedoff = true; + break; + + /*.................................................................. + Game Options: Store all options; check my color & game version. + ..................................................................*/ + case (SERIAL_GAME_OPTIONS): + oppscorescreen = false; + gameoptions = true; + display = false; + parms_received = 1; + + strcpy (TheirName, ReceivePacket.Name); + TheirColor = ReceivePacket.Color; + TheirHouse = ReceivePacket.House; + + /*............................................................... + Save scenario settings. + ...............................................................*/ + MPlayerCredits = ReceivePacket.Credits; + MPlayerBases = ReceivePacket.IsBases; + MPlayerTiberium = ReceivePacket.IsTiberium; + MPlayerGoodies = ReceivePacket.IsGoodies; + MPlayerGhosts = ReceivePacket.IsGhosties; + BuildLevel = ReceivePacket.BuildLevel; + MPlayerUnitCount = ReceivePacket.UnitCount; + Seed = ReceivePacket.Seed; + Special = ReceivePacket.Special; + Options.GameSpeed = ReceivePacket.GameSpeed; + + if (MPlayerTiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + + /*............................................................... + Find the index of the scenario number; if it's not found, leave + it at -1. + ...............................................................*/ + ScenarioIdx = -1; + for (i = 0; i < MPlayerFilenum.Count(); i++) { + if (ReceivePacket.Scenario == MPlayerFilenum[i]) + ScenarioIdx = i; + } + + /*............................................................... + Check our version numbers; if they're incompatible, sign off. + ...............................................................*/ +#ifdef PATCH + if (IsV107) { + version = 1; + } else { + version = 2; + } +#else + version = Version_Number(); +#endif + if (ReceivePacket.Version > version) { + CCMessageBox().Process (TXT_YOURGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + } else { + if (ReceivePacket.Version < version) { + CCMessageBox().Process (TXT_DESTGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount.Time(); + + process = false; + rc = false; + } + } + + /*............................................................... + If this is the first game-options packet we've received, transmit + our options to him. + ...............................................................*/ + if (first) { + first = 0; + + // force transmitting of game options packet + + transmit = 1; + transmittime = 0; + } + break; + + /*.................................................................. + GO: Exit this routine with a success code. + ..................................................................*/ + case (SERIAL_GO): + + // + // calculated one way delay for a packet and overall delay + // to execute a packet + // + ////////MPlayerMaxAhead = MAX( (ReceivePacket.ResponseTime / 8), 2); + + process = false; + rc = true; + break; + + // + // throw away timing packet + // + case (SERIAL_TIMING): + oppscorescreen = false; + break; + + // + // print msg waiting for opponent + // + case (SERIAL_SCORE_SCREEN): +// Smart_Printf( "received score screen\n" ); + oppscorescreen = true; + display = false; + parms_received = 1; + break; + + default: + break; + } + } + } + + // if we haven't received a msg for 10 seconds exit + + if ( (TickCount.Time() - lastmsgtime) > msg_timeout) { + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + } + + + /*--------------------------------------------------------------------- + Service the connection + ---------------------------------------------------------------------*/ + NullModem.Service(); + + } /* end of while */ + + /*------------------------------------------------------------------------ + Sort player ID's, so we can execute commands in the same order on both + machines. + ------------------------------------------------------------------------*/ + if (rc) { + /*..................................................................... + Set the number of players in this game, and my ID + .....................................................................*/ + MPlayerCount = 2; + MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + TheirID = Build_MPlayerID (TheirColor,TheirHouse); + + /*..................................................................... + Store every player's ID in the MPlayerID[] array. This array will + determine the order of event execution, so the ID's must be stored + in the same order on all systems. + .....................................................................*/ + if (TheirID < MPlayerLocalID) { + MPlayerID[0] = TheirID; + MPlayerID[1] = MPlayerLocalID; + strcpy (MPlayerNames[0], TheirName); + strcpy (MPlayerNames[1], MPlayerName); + } else { + MPlayerID[0] = MPlayerLocalID; + MPlayerID[1] = TheirID; + strcpy (MPlayerNames[0], MPlayerName); + strcpy (MPlayerNames[1], TheirName); + } + + /*..................................................................... + Get the scenario filename + .....................................................................*/ + Scenario = MPlayerFilenum[ScenarioIdx]; + + starttime = TickCount.Time(); + while ( ( NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_SENDING_TIMEOUT) ) + || ((TickCount.Time() - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + NullModem.Service(); + Keyboard::Check(); //Make sure the message loop gets called + } + + // clear queue to keep from doing any resends + NullModem.Init_Send_Queue(); + + } else { + if ( !recsignedoff ) { + /*..................................................................... + Broadcast my sign-off over my network + .....................................................................*/ + SendPacket.Command = SERIAL_SIGN_OFF; + SendPacket.Color = MPlayerLocalID; // use Color for ID + SendPacket.ID = ModemGameToPlay; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount.Time(); + while ( (NullModem.Num_Send() + && ((TickCount.Time() - starttime) < PACKET_CANCEL_TIMEOUT) ) + || ((TickCount.Time() - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + if ( NullModem.Get_Message( &ReceivePacket, &packetlen ) > 0) { + + // are we getting our own packets back?? + + if (ReceivePacket.Command == SERIAL_SIGN_OFF + && ReceivePacket.ID == ModemGameToPlay) { + + // exit while + break; + } + } + + NullModem.Service(); + } + } + + Shutdown_Modem(); + } + + /*------------------------------------------------------------------------ + Save any changes made to our options + ------------------------------------------------------------------------*/ + if (changed) { + Write_MultiPlayer_Settings (); + } + + return(rc); + +} /* end of Com_Show_Scenario_Dialog */ + + +#endif //(0) + + + + + + + + + + + diff --git a/NULLMGR.CPP b/NULLMGR.CPP new file mode 100644 index 0000000..ec9b137 --- /dev/null +++ b/NULLMGR.CPP @@ -0,0 +1,2350 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\nullmgr.cpv 1.10 16 Oct 1995 16:51:52 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NULLMGR.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : April 5, 1995 * + * * + * Last Update : May 1, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * NullModemClass::NullModemClass -- class constructor * + * NullModemClass::~NullModemClass -- class destructor * + * NullModemClass::Init -- initialization * + * NullModemClass::Send_Message -- sends a message * + * NullModemClass::Get_Message -- polls the Queue for a message * + * NullModemClass::Service -- main polling routine * + * NullModemClass::Num_Send -- Returns # of unACK'd send entries * + * NullModemClass::Num_Receive -- Returns # entries in the receive queue * + * NullModemClass::Response_Time -- Returns Queue's avg response time * + * NullModemClass::Reset_Response_Time -- Resets response time computatio* + * NullModemClass::Oldest_Send -- Returns ptr to oldest unACK'd send buf * + * NullModemClass::Detect_Modem -- Detects and initializes the modem * + * NullModemClass::Dial_Modem -- dials a number passed * + * NullModemClass::Answer_Modem -- waits for call and answers * + * NullModemClass::Hangup_Modem -- hangs up the modem * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "wincomm.h" +#include "modemreg.h" +#include "i86.h" +#include "tcpip.h" + + +extern ModemRegistryEntryClass *ModemRegistry; + +// the following line was taken from Greenleaf's +// because of other define conflicts + +#define ESC 27 +#define NOKEY 0xffff +#define INIT_COMMAND_RETRIES 2 + +// this time is in milliseconds + +#define DEFAULT_TIMEOUT 2000 + +// +// the following is for a fix around a greenleaf bug +// where they do not check for the value of abortkey +// to determine whether or not they call the abort modem function. +// +extern "C" { + extern void (*_AbortModemFunctionPtr)(int); +} + +static void (*NullModemClass::OrigAbortModemFunc)(int); + +static KeyNumType NullModemClass::Input; +static GadgetClass *NullModemClass::Commands; // button list + +/* +** Ugly hack: this string stores the string received from the modem +*/ +char ModemRXString[80]; + +/*************************************************************************** + * NullModemClass::NullModemClass -- class constructor * + * * + * INPUT: * + * numsend # desired entries for the send queue * + * numreceive # desired entries for the receive queue * + * maxlen application's max packet length * + * magicnum application-specific magic # (so we don't * + * accidentally end up talking to another one of our own * + * products using the same protocol) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NullModemClass::NullModemClass (int numsend, int numreceive, int maxlen, + unsigned short magicnum) : ConnManClass() +{ + /*------------------------------------------------------------------------ + Init Port to NULL; we haven't opened Greenleaf yet. + ------------------------------------------------------------------------*/ + PortHandle = NULL; + Connection = NULL; + + NumSend = numsend; + NumReceive = numreceive; + MaxLen = maxlen; + MagicNum = magicnum; + + RXBuf = 0; + BuildBuf = 0; + + EchoSize = 500; + EchoBuf = 0; + + OldIRQPri = -1; + + ModemVerboseOn = false; // default true + ModemEchoOn = false; // default true + ModemWaitCarrier = 50000; // default 50 * 1000ms = 50 secs + ModemCarrierDetect = 600; // default 6 * 100ms = .6 secs + ModemCarrierLoss = 1400; // default 14 * 100ms = 1.4 secs + ModemHangupDelay = 20000; // default 20 * 1000ms = 20 secs + ModemGuardTime = 1000; // default 50 * 20ms = 1 sec + ModemEscapeCode = '+'; // default ASCII 43 + + SendOverflows = 0; + ReceiveOverflows = 0; + CRCErrors = 0; + + NumConnections = 0; + + /*........................................................................ + Init timing parameters + ........................................................................*/ + RetryDelta = 60; // 60 ticks between retries + MaxRetries = -1; // disregard # retries + Timeout = 1200; // report bad connection after 20 seconds + +} /* end of NullModemClass */ + + +/*************************************************************************** + * NullModemClass::~NullModemClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NullModemClass::~NullModemClass () +{ + Delete_Connection(); +} /* end of ~NullModemClass */ + + +/*************************************************************************** + * NullModemClass::Init -- initialization * + * * + * INPUT: * + * port address * + * irq 2-15 * + * baud 300, 1200, 9600, etc * + * parity 'O' (odd), 'E' (even), 'N' (none), 'S' (space), * + * 'M' (mark) * + * wordlength 5, 6, 7, or 8 * + * stopbits 1 or 2 * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +//int NullModemClass::Init (int port, int irq, int baud, char parity, int wordlen, int stopbits) +#pragma off (unreferenced) +int NullModemClass::Init (int port, int ,char *dev_name, int baud, char parity, int wordlen, int stopbits, int flowcontrol) +{ + int com; + //int irqnum; + //int address; + //int status; + + + if (PortHandle) { + CloseHandle(PortHandle); + PortHandle = NULL; + } + + if (!Connection){ + /*------------------------------------------------------------------------ + Init our Connection + ------------------------------------------------------------------------*/ + Connection = new NullModemConnClass (NumSend, NumReceive, MaxLen, + MagicNum); + + Connection->Set_Retry_Delta (RetryDelta); + Connection->Set_Max_Retries (MaxRetries); + Connection->Set_TimeOut (Timeout); + + /*------------------------------------------------------------------------ + Allocate our packet parsing buffer; make it the same # of packets as the + # of receive queue entries the application has requested. Use the + "Actual" maximum packet size, given from the connection; this allows for + both headers that get added to the packet. + ------------------------------------------------------------------------*/ + RXSize = Connection->Actual_Max_Packet() * NumReceive; + RXBuf = new char [RXSize]; + + BuildBuf = new char [MaxLen]; + + EchoBuf = new char [ EchoSize ]; + } + + RXCount = 0; + EchoCount = 0; + + + /*------------------------------------------------------------------------ + This call allocates all necessary queue buffers + ------------------------------------------------------------------------*/ + switch (port) { + case 0x3f8: + com = COM1; + break; + + case 0x2f8: + com = COM2; + break; + + case 0x3e8: + com = COM3; + break; + + case 0x2e8: + com = COM4; + break; + + default: + com = COM5; + break; + } + + int i; + + /* + ** Create a new modem class for our com port + */ + if (!SerialPort) { + SerialPort = new WinModemClass; + } + + /* + ** Shift up the baud rate to sensible values + */ +// if (baud == 14400) baud = 19200; +// if (baud == 28800) baud = 38400; + + static char com_ids[9][5]={ + "COM1", + "COM2", + "COM3", + "COM4", + "COM5", + "COM6", + "COM7", + "COM8", + "COM9" + }; + + char *device; + + switch ( port ) { + + case 0x3f8: + device = com_ids[0]; + break; + + case 0x2f8: + device = com_ids[1]; + break; + + case 0x3e8: + device = com_ids[2]; + break; + + case 0x2e8: + device = com_ids[3]; + break; + + case 1: + /* + ** 1 is a special value. It means use the device name not the port address. + */ + device = dev_name; + + /* + ** If we can match a registry entry with the device name then use that, otherwise use + ** the device name directly to open the port with. + */ + if (ModemRegistry){ + delete ModemRegistry; + ModemRegistry = NULL; + } + for ( i=0 ; i<10 ; i++ ){ + ModemRegistry = new ModemRegistryEntryClass (i); + if (ModemRegistry->Get_Modem_Name()){ + if (!strcmp (dev_name, ModemRegistry->Get_Modem_Name() )){ + device = ModemRegistry->Get_Modem_Device_Name(); + break; + } + } + delete ModemRegistry; + ModemRegistry = NULL; + } + break; + + default: + device = NULL; + } + + /* + ** Open the com port + */ + PortHandle = SerialPort->Serial_Port_Open (device, baud, parity, wordlen, stopbits, flowcontrol); + if (PortHandle == INVALID_HANDLE_VALUE) { + Shutdown(); + return(false); + } + + /*------------------------------------------------------------------------ + Init the Connection + ------------------------------------------------------------------------*/ + Connection->Init(PortHandle); + + NumConnections = 1; + + return(true); +} + + +/*********************************************************************************************** + * NMC::Num_Connections -- returns NumConnections member * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: NumConnections * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 11:44AM ST : Documented / Win32 support * + *=============================================================================================*/ +int NullModemClass::Num_Connections( void ) +{ + return(NumConnections); +} + + + +/*********************************************************************************************** + * NMC::Delete_Connection -- removes the connection * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: true * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 11:44AM ST : Documented / Win32 support * + *=============================================================================================*/ +int NullModemClass::Delete_Connection( void ) +{ + if (Connection) { + delete Connection; + Connection = NULL; + } + + if (RXBuf) { + delete [] RXBuf; + RXBuf = NULL; + } + + if (BuildBuf) { + delete [] BuildBuf; + BuildBuf = NULL; + } + + if (EchoBuf) { + delete [] EchoBuf; + EchoBuf = NULL; + } + + NumConnections = 0; + + return( true ); +} /* end of Delete_Connection */ + + +/*********************************************************************************************** + * NMC::Init_Send_Queue -- Initialises the connections send queue * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: true * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 11:46AM ST : Documented / Win32 support * + *=============================================================================================*/ +int NullModemClass::Init_Send_Queue( void ) +{ + + /*--------------------------------------------------------------- + Init the send queue + -----------------------------------------------------------------*/ + if ( Connection ) { + Connection->Queue->Init_Send_Queue(); + } + + return(true); +} + + +//DetectPortType NullModemClass::Detect_Port( SerialSettingsType *settings ) +/*********************************************************************************************** + * NMC::Detect_Port -- Checks that the specified com port exists * + * * + * * + * * + * INPUT: ptr to SerialSettingsType * + * * + * OUTPUT: true if port is valid * + * * + * HISTORY: * + * 8/2/96 11:47AM ST : Documented / Win32 support * + *=============================================================================================*/ +DetectPortType NullModemClass::Detect_Port( SerialSettingsType *settings) +{ + + static char com_ids[9][5]={ + "COM1", + "COM2", + "COM3", + "COM4", + "COM5", + "COM6", + "COM7", + "COM8", + "COM9" + }; + + int i; + + /* + ** Create a new modem class for our com port + */ + if (!SerialPort) { + SerialPort = new WinModemClass; + }else{ + SerialPort->Serial_Port_Close(); + } + + /* + ** Shift up the baud rate to sensible values + */ + int baud = settings->Baud; +// if (baud == 14400) baud = 19200; +// if (baud == 28800) baud = 38400; + + + /* + ** Translate the port address into a usable device name + */ + char *device; + + switch ( settings->Port ) { + + case 0x3f8: + device = com_ids[0]; + break; + + case 0x2f8: + device = com_ids[1]; + break; + + case 0x3e8: + device = com_ids[2]; + break; + + case 0x2e8: + device = com_ids[3]; + break; + + case 1: + /* + ** 1 is a special value. It means use the device name not the port address. + */ + device = settings->ModemName; + + /* + ** If we can match a registry entry with the device name then use that, otherwise use + ** the device name directly to open the port with. + */ + if (ModemRegistry){ + delete ModemRegistry; + ModemRegistry = NULL; + } + for ( i=0 ; i<10 ; i++){ + ModemRegistry = new ModemRegistryEntryClass (i); + if (ModemRegistry->Get_Modem_Name()){ + if (!strcmp (device, ModemRegistry->Get_Modem_Name() )){ + /* + ** Got a match. Break out leaving the registry info intact. + */ + device = ModemRegistry->Get_Modem_Device_Name(); + break; + } + } + delete ModemRegistry; + ModemRegistry = NULL; + } + break; + + default: + return (PORT_INVALID); + } + + /* + ** Open the com port + */ + HANDLE porthandle = SerialPort->Serial_Port_Open (device, baud, 0, 8, 1, settings->HardwareFlowControl); + + if (porthandle == INVALID_HANDLE_VALUE){ + return (PORT_INVALID); + } + + SerialPort->Serial_Port_Close(); + return (PORT_VALID); + +} + + + + +/*********************************************************************************************** + * NullModemClass::ShutDown -- Closes serial port and removes the connection * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 11:43AM ST : Documented / Win32 support * + *=============================================================================================*/ +void NullModemClass::Shutdown ( void ) +{ + if (PortHandle && SerialPort) { + SerialPort->Serial_Port_Close(); + delete SerialPort; + SerialPort = NULL; + PortHandle = NULL; + Delete_Connection(); + } + +#ifdef FORCE_WINSOCK + if (Winsock.Get_Connected()){ + Delete_Connection(); + } +#endif + +} /* end of Shutdown */ + + +/*************************************************************************** + * NullModemClass::Set_Timing -- sets timing for all connections * + * * + * This will set the timing parameters. This allows an application to * + * measure the Response_Time while running, and adjust timing accordingly. * + * * + * INPUT: * + * retrydelta value to set for retry delta * + * maxretries value to set for max # retries * + * timeout value to set for connection timeout * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/07/1995 DRD : Created. * + *=========================================================================*/ +void NullModemClass::Set_Timing (unsigned long retrydelta, + unsigned long maxretries, unsigned long timeout) +{ + RetryDelta = retrydelta; + MaxRetries = maxretries; + Timeout = timeout; + + Connection->Set_Retry_Delta (RetryDelta); + Connection->Set_Max_Retries (MaxRetries); + Connection->Set_TimeOut (Timeout); + +} /* end of Set_Timing */ + + +/*************************************************************************** + * NullModemClass::Send_Message -- sends a message * + * * + * For clarity's sake, here's what happens to the buffer passed in: * + * - It gets passed to the Connection's Send_Packet() routine * + * - The CommHeaderType header gets tacked onto it * + * - The resulting buffer gets added to the Connection's Send Queue * + * - When Service() determines that it needs to send the data, it * + * copies the entire packet (CommHeaderType and all) into its local * + * SendBuf, adds the packet start ID, length, and CRC, then sends it out.* + * * + * The ack_req argument will almost always be '1' (the default). The only * + * reason to use 0 is if you don't know whether the other system is * + * ready or not, so you have to periodically send out a query packet, * + * and wait for a response. (Using the connection's built-in retry * + * system would just blast out useless data if the other system isn't * + * even there.) * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * ack_req 1 = ACK is required; 0 = not * + * * + * OUTPUT: * + * 1 = OK; 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NullModemClass::Send_Message (void *buf, int buflen, int ack_req) +{ + int rc; + + if (NumConnections == 0) { + return( false ); + } + + rc = Connection->Send_Packet(buf,buflen,ack_req); + if (!rc) + SendOverflows++; + + return(rc); + +} /* end of Send_Message */ + + +/*************************************************************************** + * NullModemClass::Get_Message -- polls the Queue for a message * + * * + * INPUT: * + * buf buffer to store message in * + * buflen ptr filled in with length of message * + * * + * OUTPUT: * + * 1 = message was received; 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NullModemClass::Get_Message (void *buf, int *buflen) +{ + if (NumConnections == 0) { + return( false ); + } + return( Connection->Get_Packet( buf, buflen ) ); +} + + +/*************************************************************************** + * NullModemClass::Service -- main polling routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = connection has gone bad * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NullModemClass::Service (void) +{ + int pos; // current position in RXBuf + int i; // loop counter + //int status; + unsigned short length; + SerialHeaderType *header; // decoded packet start, length + SerialCRCType *crc; // decoded packet CRC + char moredata = 0; + + if (NumConnections == 0) { + return( false ); + } + + /*------------------------------------------------------------------------ + First, copy all the bytes we can from the Greenleaf RX buffer to our + own buffer. + ------------------------------------------------------------------------*/ + RXCount += SerialPort->Read_From_Serial_Port((unsigned char*)(RXBuf + RXCount), int(RXSize - RXCount) ); + +// if (RXCount){ +//char port[128]; +//sprintf (port, "C&C95 - RXCount = %d bytes.\n", RXCount); +//CCDebugString (port); +// } + +BOOL enabled = FALSE; + +#if (0) +if (SerialPort->FramingErrors || + SerialPort->IOErrors || + SerialPort->InBufferOverflows || + SerialPort->BufferOverruns || + SerialPort->InBufferOverflows || + SerialPort->OutBufferOverflows){ + + +if (!MonoClass::Is_Enabled()) { +MonoClass::Enable(); +enabled = TRUE; +} +Special.IsMonoEnabled = TRUE; +Debug_Smart_Print = TRUE; +Mono_Set_Cursor(0,0); +Smart_Printf( " In Queue: %5d \n", SerialPort->InQueue); +Smart_Printf( " Out Queue: %5d \n", SerialPort->OutQueue); +Smart_Printf( " Framing errors: %5d \n", SerialPort->FramingErrors); +Smart_Printf( " IO errors: %5d \n", SerialPort->IOErrors); +Smart_Printf( " Parity Errors: %5d \n", SerialPort->InBufferOverflows); +Smart_Printf( " Buffer overruns: %5d \n", SerialPort->BufferOverruns); +Smart_Printf( " In buffer overflows: %5d \n", SerialPort->InBufferOverflows); +Smart_Printf( "Out buffer overflows: %5d \n", SerialPort->OutBufferOverflows); + +MonoClass::Disable(); +Debug_Smart_Print = FALSE; +} +#endif //(0) + + // minimum packet size + + if ( RXCount < (PACKET_SERIAL_OVERHEAD_SIZE + 1) ) { + return( Connection->Service() ); + } + + /*------------------------------------------------------------------------ + Now scan the buffer for the start of a packet. + ------------------------------------------------------------------------*/ + pos = -1; + for (i = 0; i <= RXCount - sizeof( short ); i++) { + if ( *((unsigned short *)(RXBuf + i)) == PACKET_SERIAL_START ) { + pos = i; + break; + } + } + + /*------------------------------------------------------------------------ + No start code was found; throw away all bytes except the last few, and + return. + ------------------------------------------------------------------------*/ + if (pos==-1) { +// Smart_Printf( "No magic number found \n" ); + /*..................................................................... + move the remaining, un-checked bytes to the start of the buffer + .....................................................................*/ + memmove (RXBuf, RXBuf + i, sizeof( short ) - 1); + RXCount = sizeof( short ) - 1; + return( Connection->Service() ); + } + + /*........................................................................ + Check to see if there are enough bytes for the header to be decoded + ........................................................................*/ + if ( (RXCount - pos) < sizeof( SerialHeaderType ) ) { + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + return(Connection->Service()); + } + + /*------------------------------------------------------------------------ + A start code was found; check the packet's length & CRC + ------------------------------------------------------------------------*/ + header = (SerialHeaderType *)(RXBuf + pos); + + /*........................................................................ + If we lost a byte in the length, we may end up waiting a very long time + for the buffer to get to the right length; check the verify value to + make sure this didn't happen. + ........................................................................*/ + if ( header->MagicNumber2 != PACKET_SERIAL_VERIFY ) { +// Smart_Printf( "Verify failed\n"); +// Hex_Dump_Data( (RXBuf + pos), PACKET_SERIAL_OVERHEAD_SIZE ); + + pos += sizeof ( short ); // throw away the bogus start code + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + return(Connection->Service()); + } + + length = header->Length; + + /*........................................................................ + Special case: if the length comes out too long for us to process: + - Assume the packet is bad + - Throw away the bogus packet-start code + - Return; we'll search for another packet-start code next time. + ........................................................................*/ + if (length > MaxLen) { +#if (CONN_DEBUG) + printf( "length too lonnng\n" ); +#endif +// Smart_Printf( "length too lonnng %d, max %d \n", length, MaxLen ); + + pos += sizeof ( short ); // throw away the bogus start code + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + return(Connection->Service()); + } + + /*........................................................................ + If the entire packet isn't stored in our buffer, copy the remaining bytes + to the front of the buffer & return. + ........................................................................*/ + if ( (pos + length + PACKET_SERIAL_OVERHEAD_SIZE) > RXCount) { + + if ( moredata ) { +// Smart_Printf( "waiting for more data %d, pos = %d \n", ((length + PACKET_SERIAL_OVERHEAD_SIZE) - (RXCount - pos)), pos ); + } + + if (pos) { + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + } + return(Connection->Service()); + } + + /*........................................................................ + Now grab the CRC value in the packet, & compare it to the CRC value + computed from the actual data. If they don't match, throw away the bogus + start-code, move the rest to the front of the buffer, & return. + We'll continue parsing this data when we're called next time. + ........................................................................*/ + crc = (SerialCRCType *)(RXBuf + pos + sizeof( SerialHeaderType ) + length); + if (NullModemConnClass::Compute_CRC(RXBuf + pos + + sizeof( SerialHeaderType ), length) != crc->SerialCRC) { + + CRCErrors++; + +#if (CONN_DEBUG) + printf( "CRC check failed\n" ); +#endif +// Smart_Printf( "CRC check failed for packet of length %d \n", length ); + +// if (length < 100) { +// Hex_Dump_Data( (RXBuf + pos), (PACKET_SERIAL_OVERHEAD_SIZE + length) ); +// } + + pos += sizeof ( short ); // throw away the bogus start code + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + return(Connection->Service()); + } + + /*------------------------------------------------------------------------ + Give the new packet to the Connection to process. + ------------------------------------------------------------------------*/ + if (!Connection->Receive_Packet(RXBuf + pos + sizeof( SerialHeaderType ), length)) { + ReceiveOverflows++; +// Smart_Printf( "Received overflows %d \n", ReceiveOverflows ); + } + +#if (0) + Hex_Dump_Data( (RXBuf + pos), (PACKET_SERIAL_OVERHEAD_SIZE + length) ); +#endif + + /*------------------------------------------------------------------------ + Move all data past this packet to the front of the buffer. + ------------------------------------------------------------------------*/ + pos += (PACKET_SERIAL_OVERHEAD_SIZE + length); + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + + /*------------------------------------------------------------------------ + Now, service the connection's Queue's; this will handle ACK & Retries. + ------------------------------------------------------------------------*/ + return(Connection->Service()); + +} /* end of Service */ + + +/*************************************************************************** + * NullModemClass::Num_Send -- Returns # of unACK'd send entries * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/01/1995 BRR : Created. * + *=========================================================================*/ +int NullModemClass::Num_Send(void) +{ + if (Connection) + return( Connection->Queue->Num_Send() ); + else + return (0); + +} /* end of Num_Send */ + + +/*************************************************************************** + * NullModemClass::Num_Receive -- Returns # entries in the receive queue * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/01/1995 BRR : Created. * + *=========================================================================*/ +int NullModemClass::Num_Receive(void) +{ + if (Connection) + return( Connection->Queue->Num_Receive() ); + else + return (0); + +} /* end of Num_Receive */ + + +/*************************************************************************** + * NullModemClass::Response_Time -- Returns Queue's avg response time * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/01/1995 BRR : Created. * + *=========================================================================*/ +unsigned long NullModemClass::Response_Time(void) +{ + if (Connection) + return( Connection->Queue->Avg_Response_Time() ); + else + return (0); + +} /* end of Response_Time */ + + +/*************************************************************************** + * NullModemClass::Reset_Response_Time -- Resets response time computation * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/01/1995 BRR : Created. * + *=========================================================================*/ +void NullModemClass::Reset_Response_Time(void) +{ + if (Connection) + Connection->Queue->Reset_Response_Time(); + +} /* end of Reset_Response_Time */ + + +/*************************************************************************** + * Oldest_Send -- Returns ptr to oldest unACK'd send buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/01/1995 BRR : Created. * + *=========================================================================*/ +void * NullModemClass::Oldest_Send(void) +{ + int i; + SendQueueType *send_entry; // ptr to send entry header + CommHeaderType *packet; + void *buf = NULL; + + for (i = 0; i < Connection->Queue->Num_Send(); i++) { + send_entry = Connection->Queue->Get_Send(i); + if (send_entry) { + packet = (CommHeaderType *)send_entry->Buffer; + if (packet->Code == ConnectionClass::PACKET_DATA_ACK && send_entry->IsACK == 0) { + buf = send_entry->Buffer; + break; + } + } + } + + return(buf); + +} /* end of Oldest_Send */ + + +/*************************************************************************** + * NullModemClass::Configure_Debug -- sets up special debug values * + * * + * Mono_Debug_Print2() can look into a packet to pull out a particular * + * ID, and can print both that ID and a string corresponding to * + * that ID. This routine configures these values so it can find * + * and decode the ID. This ID is used in addition to the normal * + * CommHeaderType values. * + * * + * INPUT: * + * index connection index to configure (-1 = Global Channel) * + * offset ID's byte offset into packet * + * size size of ID, in bytes; 0 if none * + * names ptr to array of names; use ID as an index into this * + * maxnames max # in the names array; 0 if none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Names shouldn't be longer than 12 characters. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void NullModemClass::Configure_Debug(int , int offset, int size, + char **names, int maxnames) +{ + if (Connection) + Connection->Queue->Configure_Debug (offset, size, names, maxnames); +} + + +/*************************************************************************** + * Mono_Debug_Print -- Debug output routine * + * * + * INPUT: * + * refresh 1 = clear screen & completely refresh * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/02/1995 BRR : Created. * + *=========================================================================*/ +void NullModemClass::Mono_Debug_Print(int, int refresh) +{ + if (!Connection) + return; + + Connection->Queue->Mono_Debug_Print (refresh); + + if (refresh) { + Mono_Set_Cursor (31,1); + Mono_Printf ("Serial Port Queues"); + + Mono_Set_Cursor (9,2); + Mono_Printf ("Average Response Time:"); + + Mono_Set_Cursor (20,3); + Mono_Printf ("CRC Errors:"); + + Mono_Set_Cursor (43,2); + Mono_Printf ("Send Overflows:"); + + Mono_Set_Cursor (40,3); + Mono_Printf ("Receive Overflows:"); + } + + Mono_Set_Cursor (32,2); + Mono_Printf ("%d ", Connection->Queue->Avg_Response_Time()); + + Mono_Set_Cursor (32,3); + Mono_Printf ("%d ", CRCErrors); + + Mono_Set_Cursor (59,2); + Mono_Printf ("%d ", SendOverflows); + + Mono_Set_Cursor (59,3); + Mono_Printf ("%d ", ReceiveOverflows); + + Mono_Set_Cursor (2,5); + Mono_Printf ("%d ", Num_Send()); + + Mono_Set_Cursor (41,5); + Mono_Printf ("%d ", Num_Receive()); + +} /* end of Mono_Debug_Print */ + + +void Timer_Test (int line, char *file) +{ + + char abuffer [128]; + + sprintf (abuffer, "Testing timer at line %d in file %s", line, file); + CCDebugString (abuffer); + + CountDownTimerClass timer; + + timer.Set (1); + + while (timer.Time()){ + CCDebugString ("."); + } + + CCDebugString ("OK\n"); +} + + + + +/*************************************************************************** + * NullModemClass::Detect_Modem -- Detects and initializes the modem * + * * + * INPUT: * + * settings ptr to SerialSettings structure * + * * + * OUTPUT: * + * -1 init string invalid * + * 0 no modem found * + * 1 modem found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/02/1995 DRD : Created. * + *=========================================================================*/ +int NullModemClass::Detect_Modem( SerialSettingsType *settings, bool reconnect ) +{ + /*........................................................................ + Button Enumerations + ........................................................................*/ +// enum { +// BUTTON_CANCEL = 100, +// }; + + int status; +// int modemstatus; + int value; + int error_count = 0; + + int x,y,width,height; // dialog dimensions + char buffer[80*3]; + + int factor = SeenBuff.Get_Width()/320; + + + +//Timer_Test(__LINE__, __FILE__); + + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + strcpy( buffer, Text_String( TXT_INITIALIZING_MODEM ) ); + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(buffer, SeenBuff.Get_Height(), width, height); + + width = MAX(width, 50*factor); + width += 40*factor; + height += 60*factor; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + /* + ............................ Draw the dialog ............................. + */ + Hide_Mouse(); + if ( !reconnect ) { + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + } + + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(buffer, x + 20*factor, y + 25*factor, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Show_Mouse(); + + HMWaitForOK( 0, NULL ); + + + + + /* + ** OK, lets not mess about any more. Just turn on echo, verbose, and result codes + ** before we even begin. At least this way when we get an error later on we have already + ** removed all the steps we use to try and recover. + ** The timeouts need to be quite small in case the modem is turned off. + */ + + /* + ** Turn on result codes. + */ + Send_Modem_Command ( "ATQ0", '\r', buffer, 81, DEFAULT_TIMEOUT / 2, 2); + + /* + ** Make result codes verbose. + */ + Send_Modem_Command ( "ATV1", '\r', buffer, 81, DEFAULT_TIMEOUT / 2, 2); + + /* + ** Turn on echo. + */ + Send_Modem_Command ( "ATE1", '\r', buffer, 81, DEFAULT_TIMEOUT / 2, 2); + + ModemVerboseOn = true; + ModemEchoOn = true; + + + + /* + ** Try sending a plain old AT command to the modem. Now that we have theoretically + ** turned on verbose result codes we should get an 'OK' back. + ** + */ + status = Send_Modem_Command( "AT", '\r', buffer, 81, DEFAULT_TIMEOUT, 2 ); + + if (status < ASSUCCESS) { + /* + ** No 'OK' came back so return failure. We cant print an error message here because + ** the calling function may want to upshift the baud rate (eg from 14400 to 19200) and + ** try again. + */ + return (false); + } + + /* + ** Send the user supplied modem init string + */ + if (settings->InitStringIndex == -1) { + status = Send_Modem_Command( "", '\r', buffer, 81, 300, 1 ); + } else { + /* + ** Split up the init string into seperate strings if it contains one or more '|' characters. + ** This character acts as a carriage return/pause. + */ + char *istr = new char [2 + strlen( InitStrings [settings->InitStringIndex] )]; + char *tokenptr; + strcpy (istr, InitStrings [settings->InitStringIndex] ); + + /* + ** Tokenise the string and send it in chunks + */ + tokenptr = strtok ( istr, "|" ); + while ( tokenptr ) { + + status = Send_Modem_Command( tokenptr, '\r', buffer, 81, DEFAULT_TIMEOUT, 1 ); + /* + ** Handle error case. + */ + if (status < ASSUCCESS) { + if (CCMessageBox().Process(TXT_ERROR_NO_INIT, TXT_IGNORE, TXT_CANCEL)) { + delete istr; + return( false ); + } + error_count++; + break; + } + + tokenptr = strtok ( NULL, "|"); + + } + } + /* + ** Use the settings from the registry to further initialise the modem + */ + if (settings->Port == 1 && ModemRegistry) { + /* + ** Send the init strings from the registry if available + */ + char send_string[256] = {"AT"}; + + if (settings->HardwareFlowControl){ + /* + ** Send the init string for hardware flow control + */ + if (ModemRegistry->Get_Modem_Hardware_Flow_Control()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_Hardware_Flow_Control()); + status = Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (CCMessageBox().Process(TXT_NO_FLOW_CONTROL_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + }else{ + /* + ** Send the init string for no flow control + */ + if (ModemRegistry->Get_Modem_No_Flow_Control()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_No_Flow_Control()); + status = Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (CCMessageBox().Process(TXT_NO_FLOW_CONTROL_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + } + + + /* + ** Send the string for data compresseion + */ + if (settings->Compression){ + + if (ModemRegistry->Get_Modem_Compression_Enable()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_Compression_Enable()); + Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (CCMessageBox().Process(TXT_NO_COMPRESSION_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + }else{ + + if (ModemRegistry->Get_Modem_Compression_Disable()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_Compression_Disable()); + Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (CCMessageBox().Process(TXT_NO_COMPRESSION_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + } + + + /* + ** Send the string for error correction + */ + if (settings->ErrorCorrection){ + + if (ModemRegistry->Get_Modem_Error_Correction_Enable()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_Error_Correction_Enable()); + Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (CCMessageBox().Process(TXT_NO_ERROR_CORRECTION_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + }else{ + if (ModemRegistry->Get_Modem_Error_Correction_Disable()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_Error_Correction_Disable()); + Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (CCMessageBox().Process(TXT_NO_ERROR_CORRECTION_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + } + + + } + /* + ** We require that auto-answer be disabled so turn it off now. + */ + status = Send_Modem_Command( "ATS0=0", '\r', buffer, 81, DEFAULT_TIMEOUT, INIT_COMMAND_RETRIES ); + if (status != MODEM_CMD_OK) { + if (CCMessageBox().Process(TXT_ERROR_NO_DISABLE, TXT_IGNORE, TXT_CANCEL)) return( false ); + error_count++; + } + + /* + ** If we had an unreasonable number of ignored errors then return failure + */ + if (error_count >= 3) { + CCMessageBox().Process(TXT_ERROR_TOO_MANY, TXT_OK); + return (false); + } + + return( true ); +} + + +/*************************************************************************** + * NullModemClass::Dial_Modem -- dials a number passed * + * * + * INPUT: * + * settings ptr to SerialSettings structure * + * * + * OUTPUT: * + * status DialStatus * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/02/1995 DRD : Created. * + *=========================================================================*/ +DialStatusType NullModemClass::Dial_Modem( char *string, DialMethodType method, bool reconnect ) +{ + +//Timer_Test(__LINE__, __FILE__); + + + int factor = SeenBuff.Get_Width()/320; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_CANCEL = 100, + }; + + /*........................................................................ + Dialog variables + ........................................................................*/ + bool process = true; // process while true + + //int status; + int delay; + DialStatusType dialstatus; + + int x,y,width,height; // dialog dimensions + char buffer[80*3]; + + Input = 0; + + +//Timer_Test(__LINE__, __FILE__); + + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + if (reconnect) { + strcpy( buffer, Text_String( TXT_MODEM_CONNERR_REDIALING ) ); + } else { + strcpy( buffer, Text_String( TXT_DIALING ) ); + } + + +//Timer_Test(__LINE__, __FILE__); + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(buffer, SeenBuff.Get_Height(), width, height); + + int text_width = width; + width = MAX(width, 50*factor); + width += 40*factor; + height += 60*factor; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8*factor)) >> 1), + y + height - (FontHeight + FontYSpacing + 2*factor) - 5*factor); + + +//Timer_Test(__LINE__, __FILE__); + + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + /* + ............................ Create the list ............................. + */ + Commands = &cancelbtn; + + +//Timer_Test(__LINE__, __FILE__); + + Commands->Flag_List_To_Redraw(); + + +//Timer_Test(__LINE__, __FILE__); + + + /* + ............................ Draw the dialog ............................. + */ + Hide_Mouse(); + if ( !reconnect ) { + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + } + + +//Timer_Test(__LINE__, __FILE__); + + + + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_NONE, x, y, width); + + +//Timer_Test(__LINE__, __FILE__); + + + Fancy_Text_Print(buffer, SeenBuff.Get_Width()/2 - text_width/2, y + 25*factor, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + +//Timer_Test(__LINE__, __FILE__); + + + CCDebugString ("C&C95 - About to draw buttons.\n"); + Commands->Draw_All(); + CCDebugString ("C&C95 - About to show mouse.\n"); + Show_Mouse(); + + +//Timer_Test(__LINE__, __FILE__); + + // start waiting + + //CCDebugString ("C&C95 - About to delay for 2 seconds.\n"); + //Delay(120); + //HMSetDialingMethod( Port, (int)method ); + CCDebugString ("C&C95 - About to set modem dial type.\n"); + SerialPort->Set_Modem_Dial_Type((WinCommDialMethodType) method); + + +//Timer_Test(__LINE__, __FILE__); + + //Clear out any old modem result codes + CCDebugString ("C&C95 - About to call 'Get_Modem_Result'.\n"); + SerialPort->Get_Modem_Result(60, buffer, 81); + //status = HMDial( Port, string ); + CCDebugString ("C&C95 - About to call 'SerialPort->Dial_Modem'.\n"); + SerialPort->Dial_Modem(string); + + +//Timer_Test(__LINE__, __FILE__); + + + // + // Sets up the ability to abort modem commands when any input is in the + // Keyboard buffer. This also calls the game CallBack(). + // + CCDebugString ("C&C95 - About to call 'Setup_Abort_Modem'.\n"); + Setup_Abort_Modem(); + + +//Timer_Test(__LINE__, __FILE__); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + process = true; + delay = ModemWaitCarrier; + CCDebugString ("C&C95 - About to enter main process loop.\n"); + while (process) { + + +//Timer_Test(__LINE__, __FILE__); + + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + CCDebugString ("C&C95 - About to restore video surfaces.\n"); + AllSurfaces.SurfacesRestored=FALSE; + Commands->Draw_All(); + } + +//Timer_Test(__LINE__, __FILE__); + + + //delay = HMInputLine( Port, delay, buffer, 81 ); + CCDebugString ("C&C95 - About to call 'Get_Modem_Result 2'.\n"); + + +//Timer_Test(__LINE__, __FILE__); + + + char abuffer [128]; + sprintf (abuffer, "C&C95 - ModemWaitCarrier delay = %d\n", delay); + CCDebugString (abuffer); + delay = SerialPort->Get_Modem_Result(delay, buffer, 81); + + + /* + ............................ Process input ............................ + */ + CCDebugString ("C&C95 - About to check for keyboard input.\n"); + if (!Input) Input = Commands->Input(); + + + switch (Input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + dialstatus = DIAL_CANCELED; + process = false; + break; + + default: + break; + } + + if (process) { + if ( strncmp( buffer, "CON", 3 ) == 0 ) { + memset (ModemRXString, 0, 80); + strncpy (ModemRXString, buffer, 79); + dialstatus = DIAL_CONNECTED; + process = false; + } + else if ( strncmp( buffer, "BUSY", 4 ) == 0 ) { + dialstatus = DIAL_BUSY; + process = false; + } + else if ( strncmp( buffer, "NO C", 4 ) == 0 ) { + dialstatus = DIAL_NO_CARRIER; + process = false; + } + else if ( strncmp( buffer, "NO D", 4 ) == 0 ) { + dialstatus = DIAL_NO_DIAL_TONE; + process = false; + } + else if ( strncmp( buffer, "ERRO", 4 ) == 0 ) { + dialstatus = DIAL_ERROR; + process = false; + } + } + + if (delay <= 0) { + if (delay < 0) { + } + process = false; + } + } + + Remove_Abort_Modem(); + + return( dialstatus ); + +} /* end of Dial_Modem */ + + +/*************************************************************************** + * NullModemClass::Answer_Modem -- waits for call and answers * + * * + * INPUT: * + * reconnect whether this is to reconnect * + * * + * OUTPUT: * + * status DialStatus * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/02/1995 DRD : Created. * + *=========================================================================*/ +DialStatusType NullModemClass::Answer_Modem( bool reconnect ) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_CANCEL = 100, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + + //int status; + int delay; + DialStatusType dialstatus; + bool ring = false; + + int x,y,width,height; // dialog dimensions + char text_buffer[80*3]; + char comm_buffer[80*3]; + + int text_width; + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + if (reconnect) { + strcpy( text_buffer, Text_String( TXT_MODEM_CONNERR_WAITING ) ); + } else { + strcpy( text_buffer, Text_String( TXT_WAITING_FOR_CALL ) ); + } + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(text_buffer, SeenBuff.Get_Height(), width, height); + + text_width = width; + width = MAX(width, 50*factor); + width += 40*factor; + height += 60*factor; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8*factor)) >> 1), + y + height - (FontHeight + FontYSpacing + 2*factor) - 5*factor); + + /* + ------------------------------- Initialize ------------------------------- + */ + Set_Logic_Page(SeenBuff); + + //Load_Picture("TITLE.CPS", HidPage, HidPage, Palette, BM_DEFAULT); + + Input = 0; + + /* + ............................ Create the list ............................. + */ + Commands = &cancelbtn; + + Commands->Flag_List_To_Redraw(); + + // start waiting + + +// HMWaitForOK( 1000, NULL ); +// status = HMSendString( Port, "ATS0=1" ); + + + // + // Sets up the ability to abort modem commands when any input is in the + // Keyboard buffer. This also calls the game CallBack() and Input(). + // + Setup_Abort_Modem(); + + /* + -------------------------- Main Processing Loop -------------------------- + */ + process = true; + delay = 60000; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + /* + ..................... Refresh the backdrop ...................... + */ + if ( !reconnect ) { + Load_Title_Screen("HTITLE.PCX", &HidPage, Palette); + HidPage.Blit(SeenBuff); + } + /* + ..................... Draw the background ....................... + */ + Dialog_Box(x, y, width, height); + /* + ....................... Draw the labels ......................... + */ + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(text_buffer, SeenBuff.Get_Width()/2 - text_width/2, y + 25*factor, CC_GREEN, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Commands->Draw_All(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + //delay = HMInputLine( Port, delay, buffer, 81 ); + delay = SerialPort->Get_Modem_Result(delay, comm_buffer, 81); + + /* + ............................ Process input ............................ + */ + if (!Input) Input = Commands->Input(); + switch (Input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): +// Sound_Effect(VOC_BUTTON,255); + dialstatus = DIAL_CANCELED; + process = false; + break; + + default: + break; + } + + if (process) { + if ( strncmp( comm_buffer, "RING", 4 ) == 0 ) { + + strcpy( text_buffer, Text_String( TXT_ANSWERING ) ); + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(text_buffer, SeenBuff.Get_Height(), width, height); + + text_width = width; + width = MAX(width, 50*factor); + width += 40*factor; + height += 60*factor; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + //PortKillTime( Port, 100 ); + + //HMWaitForOK( 0, NULL ); + //status = HMAnswer( Port ); + SerialPort->Write_To_Serial_Port ((unsigned char*)"ATA\r", strlen("ATA\r")); + + ring = true; + delay = ModemWaitCarrier; + display = REDRAW_ALL; + } + else if ( strncmp( comm_buffer, "CON", 3 ) == 0 ) { + memset (ModemRXString, 0, 80); + strncpy (ModemRXString, comm_buffer, 79); + dialstatus = DIAL_CONNECTED; + process = false; + } + else if ( strncmp( comm_buffer, "BUSY", 4 ) == 0 ) { + dialstatus = DIAL_BUSY; + process = false; + } + else if ( strncmp( comm_buffer, "NO C", 4 ) == 0 ) { + dialstatus = DIAL_NO_CARRIER; + process = false; + } + else if ( strncmp( comm_buffer, "NO D", 4 ) == 0 ) { + dialstatus = DIAL_NO_DIAL_TONE; + process = false; + } + else if ( strncmp( comm_buffer, "ERRO", 4 ) == 0 ) { + dialstatus = DIAL_ERROR; + CCMessageBox().Process ("Error - Modem returned error status.", TXT_OK); + process = false; + } + } + + if (delay <= 0) { + if (ring) { + if (SerialPort->Get_Modem_Status() & CD_SET){ + sprintf(ModemRXString, "%s", "Connected"); + dialstatus = DIAL_CONNECTED; + }else{ + dialstatus = DIAL_ERROR; + CCMessageBox().Process ("Error - TIme out waiting for connect.", TXT_OK); + } + process = false; + } else { + delay = 60000; + } + } + } + + Remove_Abort_Modem(); + + return( dialstatus ); + +} /* end of Answer_Modem */ + + +/*************************************************************************** + * NullModemClass::Hangup_Modem -- hangs up the modem * + * * + * INPUT: * + * none * + * * + * OUTPUT: * + * status successful or not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/02/1995 DRD : Created. * + *=========================================================================*/ +bool NullModemClass::Hangup_Modem( void ) +{ + int status; + int delay; + char buffer[81]; + char escape[4]; + + /* + ** Turn modem servicing off in the callback routine. + */ + ModemService = false; + + + status = Send_Modem_Command( "AT", '\r', buffer, 81, DEFAULT_TIMEOUT, 1 ); + + if (status == MODEM_CMD_OK) { + ModemService = true; + return( true ); + } + + SerialPort->Set_Serial_DTR(FALSE); + Delay(3200/60); + SerialPort->Set_Serial_DTR(TRUE); + + //SetDtr( Port, 0 ); + //PortKillTime( Port, 3200 ); + //SetDtr( Port, 1 ); + + status = Send_Modem_Command( "AT", '\r', buffer, 81, DEFAULT_TIMEOUT, 1 ); + + if (status == MODEM_CMD_OK) { + ModemService = true; + return( true ); + } + + delay = ModemGuardTime; + while ( delay > 0 ) { + //delay = HMInputLine( Port, delay, buffer, 81 ); + delay = SerialPort->Get_Modem_Result(delay, buffer, 81); + + } + + escape[0] = ModemEscapeCode; + escape[1] = ModemEscapeCode; + escape[2] = ModemEscapeCode; + escape[3] = 0; + + //status = HMSendStringNoWait( Port, escape, -1 ); + SerialPort->Write_To_Serial_Port((unsigned char*)escape, 3); + + delay = ModemGuardTime; + while ( delay > 0 ) { + delay = SerialPort->Get_Modem_Result(delay, buffer, 81); + //delay = HMInputLine( Port, delay, buffer, 81 ); + + if ( strncmp( buffer, "OK", 2 ) == 0 ) { + break; + } + } + + status = Send_Modem_Command( "ATH", '\r', buffer, 81, ModemHangupDelay, 1 ); + + if (status == MODEM_CMD_OK) { + } else { + ModemService = true; + return( false ); + } + + status = Send_Modem_Command( "ATZ", '\r', buffer, 81, DEFAULT_TIMEOUT, 1 ); + + if (status == MODEM_CMD_OK) { + } else { + ModemService = true; + return( false ); + } + + ModemService = true; + return( true ); + +} /* end of Hangup_Modem */ + + +/*********************************************************************************************** + * NMC::Setup_Modem_Echo -- Sets the echo callback function pointer * + * * + * * + * * + * INPUT: Ptr to callback function * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 12:48PM ST : Documented and added WIn32 support * + *=============================================================================================*/ +void NullModemClass::Setup_Modem_Echo( void ( *func )( char c) ) +{ + SerialPort->Set_Echo_Function(func); + //HMSetUpEchoRoutine( func ); + +} /* end of Setup_Modem_Echo */ + + +/*********************************************************************************************** + * NMC::Remove_Modem_Echo -- Set the echo function callback pointer to null * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 12:50PM ST : Documented / Win32 support added * + *=============================================================================================*/ +void NullModemClass::Remove_Modem_Echo( void ) +{ + SerialPort->Set_Echo_Function(NULL); + //HMSetUpEchoRoutine( NULL ); + +} /* end of Remove_Modem_Echo */ + + +/*********************************************************************************************** + * NMC::Print_EchoBuf -- Print out the contents of the echo buffer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 12:51PM ST : Documented * + *=============================================================================================*/ +void NullModemClass::Print_EchoBuf( void ) +{ + for (int i = 0; i < strlen(NullModem.EchoBuf); i++) { + if (NullModem.EchoBuf[i] == '\r') { + NullModem.EchoBuf[i] = 1; + } else { + if (NullModem.EchoBuf[i] == '\n') { + NullModem.EchoBuf[i] = 2; + } + } + } +} + + +/*********************************************************************************************** + * NMC::Reset_EchoBuf -- Empties the echo buffer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 12:51PM ST : Documented * + *=============================================================================================*/ +void NullModemClass::Reset_EchoBuf( void ) +{ + *EchoBuf = 0; + EchoCount = 0; +} + + +/*********************************************************************************************** + * NMC::Abort_Modem -- Checks for user input so that modem operations can be aborted * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: ASUSERABORT if abort key pressed. ASSUCESS otherwise. * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 12:52PM ST : Documented * + *=============================================================================================*/ +int NullModemClass::Abort_Modem(void) +//int NullModemClass::Abort_Modem( PORT * ) +{ + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_CANCEL = 100, + }; + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ........................... Get user input ............................ + */ + Input = Commands->Input(); + + switch ( Input ) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + return( ASUSERABORT ); + } + + return( ASSUCCESS ); + +} /* end of Abort_Modem */ + + +/*********************************************************************************************** + * NMC::Setup_Abort_Modem -- sets the modem abort function pointer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 2:59PM ST : Documented / Win32 support added * + *=============================================================================================*/ +void NullModemClass::Setup_Abort_Modem( void ) +{ + SerialPort->Set_Abort_Function((int(*)(void))Abort_Modem); +} /* end of Setup_Abort_Modem */ + + +/*********************************************************************************************** + * NMC::Remove_Abort_Modem -- Removes the modem abort function pointer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 3:01PM ST : Documented / Win32 support added * + *=============================================================================================*/ +void NullModemClass::Remove_Abort_Modem( void ) +{ + SerialPort->Set_Abort_Function(NULL); +} /* end of Remove_Abort_Modem */ + + + +/*********************************************************************************************** + * NMC::Change_IRQ_Priority -- Increases the priority of the serial interrupt * + * * + * * + * * + * INPUT: Interrupt request number * + * * + * OUTPUT: ASSUCCESS if changed * + * * + * WARNINGS: The Win32 version of this function does nothing. * + * Priorities are controlled by windoze * + * * + * HISTORY: * + * 8/2/96 3:03PM ST : Documented / Win32 support added * + *=============================================================================================*/ +int NullModemClass::Change_IRQ_Priority( int ) +{ + return (ASSUCCESS); +} /* end of Change_IRQ_Priority */ + + +/*********************************************************************************************** + * NMC::Get_Modem_Status -- returns status of modem control bits * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Modem status * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 3:06PM ST : Documented / Win32 support added * + *=============================================================================================*/ +int NullModemClass::Get_Modem_Status( void ) +{ + int modemstatus; + int status; + char buffer[81]; + + //modemstatus = GetModemStatus( Port ); + modemstatus = SerialPort->Get_Modem_Status(); + + status = Send_Modem_Command( "AT", '\r', buffer, 81, DEFAULT_TIMEOUT, 1 ); + + if (status == MODEM_CMD_OK) { + modemstatus &= (~CD_SET); + } + + return( modemstatus ); + +} /* end of Get_Modem_Status */ + + +/*********************************************************************************************** + * NMC::Send_Modem_Command -- Sends an 'AT' command to the modem and gets the response * + * * + * * + * * + * INPUT: command to send to modem. e.g. 'ATZ' * + * terminator byte for command string * + * buffer to put modem response into * + * length of above buffer * + * delay to wait for response * + * number of times to retry when modem doesnt respond * + * * + * OUTPUT: input delay less the time it took the modem to respond * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 3:09PM ST : Documented / Win32 support added * + *=============================================================================================*/ +int NullModemClass::Send_Modem_Command( char *command, char terminator, char *buffer, int buflen, int delay, int retries ) +{ + return (SerialPort->Send_Command_To_Modem(command, terminator, buffer, buflen, delay, retries)); +} + + +/*********************************************************************************************** + * NMC::Verify_And_Convert_To_Int -- converts a text string of numbers to an int * + * * + * * + * * + * INPUT: ptr to buffer * + * * + * OUTPUT: value of text number in buffer * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 3:13PM ST : Documented * + *=============================================================================================*/ +int NullModemClass::Verify_And_Convert_To_Int( char *buffer ) +{ + int value = 0; + int len = strlen(buffer); + + + for (int i = 0; i < len; i++) { + if ( !isdigit( *(buffer + i) ) ) { + value = -1; + break; + } + } + + if (value == 0) { + value = atoi( buffer ); + } + + return( value ); + +} /* end of Verify_And_Convert_To_Int */ + +/*************************** end of nullmgr.cpp ****************************/ diff --git a/NULLMGR.H b/NULLMGR.H new file mode 100644 index 0000000..8e3c8a6 --- /dev/null +++ b/NULLMGR.H @@ -0,0 +1,217 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\nullmgr.h_v 1.14 16 Oct 1995 16:45:26 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONNECT.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 3, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This is the Connection Manager for a NULL-Modem connection. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef NULLMODEM_H +#define NULLMODEM_H + + +/* +********************************* Includes ********************************** +*/ +#include "nullconn.h" +#include "connmgr.h" +#include "commlib.h" + +/* +***************************** Class Declaration ***************************** +*/ +class NullModemClass : public ConnManClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + enum SendModemEnum { + MODEM_CMD_TIMEOUT = 0, + MODEM_CMD_OK, + MODEM_CMD_0, + MODEM_CMD_ERROR + }; + + char *BuildBuf; + int MaxLen; + + char *EchoBuf; + int EchoSize; + int EchoCount; + + int OldIRQPri; + + bool ModemVerboseOn; + bool ModemEchoOn; + int ModemWaitCarrier; + int ModemCarrierDetect; + int ModemCarrierLoss; + int ModemHangupDelay; + int ModemGuardTime; + char ModemEscapeCode; + + static void (*OrigAbortModemFunc)(int); + static KeyNumType Input; + static GadgetClass *Commands; // button list + + /* + ** Constructor/destructor. + */ + NullModemClass (int numsend, int numreceive, int maxlen, unsigned short magicnum); + virtual ~NullModemClass (); + + /* + ** This is the main initialization routine. + */ + int Init( int port, int irq, char *dev_name, int baud, char parity, int wordlength, int stopbits, int flowcontrol ); + int Delete_Connection( void ); + virtual int Num_Connections(void); + virtual int Connection_ID(int ) {return (0);} + virtual int Connection_Index(int ) {return (0);} + int Init_Send_Queue( void ); + void Shutdown( void ); + + virtual void Set_Timing (unsigned long retrydelta, + unsigned long maxretries, unsigned long timeout); + + /* + ** This is how the application sends & receives messages. + */ + int Send_Message (void *buf, int buflen, int ack_req = 1); + int Get_Message (void *buf, int *buflen); + + /* + ** These are for compatibility + */ + virtual int Send_Private_Message (void *buf, int buflen, + int ack_req = 1, int = CONNECTION_NONE) + {return (Send_Message(buf,buflen,ack_req));} + virtual int Get_Private_Message (void *buf, int *buflen, int *) + {return (Get_Message(buf,buflen));} + + /* + ** The main polling routine; should be called as often as possible. + */ + virtual int Service (void); + + /* + ** Queue utility routines. The application can determine how many + ** messages are in the send/receive queues, and the queue's average + ** response time (in clock ticks). + */ + int Num_Send(void); + int Num_Receive(void); + virtual unsigned long Response_Time(void); + virtual void Reset_Response_Time(void); + void * Oldest_Send(void); + virtual void Configure_Debug(int index, int offset, int size, + char **names, int maxnames); + virtual void Mono_Debug_Print(int index, int refresh = 0); + + /* + ** These are for compatibility + */ + virtual int Global_Num_Send(void) {return (Num_Send());} + virtual int Global_Num_Receive(void) {return (Num_Receive());} + virtual int Private_Num_Send(int = CONNECTION_NONE) + {return (Num_Send());} + virtual int Private_Num_Receive(int = CONNECTION_NONE) + {return (Num_Receive());} + + DetectPortType Detect_Port( SerialSettingsType *settings ); + int Detect_Modem( SerialSettingsType *settings, bool reconnect = false ); + DialStatusType Dial_Modem(char *string, DialMethodType method, bool reconnect = false); + DialStatusType Answer_Modem(bool reconnect = false); + bool Hangup_Modem(void); + void Setup_Modem_Echo(void (*func)(char c)); + void Remove_Modem_Echo(void); + void Print_EchoBuf(void); + void Reset_EchoBuf(void); + //static int Abort_Modem(PORT *); + static int Abort_Modem(void); + void Setup_Abort_Modem(void); + void Remove_Abort_Modem(void); + + int Change_IRQ_Priority(int irq); + int Get_Modem_Status(void); + int Send_Modem_Command( char *command, char terminator, char *buffer, int buflen, int delay, int retries ); + int Verify_And_Convert_To_Int( char *buffer ); + + /* + ** Private Interface. + */ + private: + + /* + ** This is a pointer to the NULL-Modem Connection object. + */ + NullModemConnClass *Connection; + int NumConnections; // # connection objects in use + + /* + ** This is the Greenleaf port handle. + */ + PORT *Port; + HANDLE PortHandle; + + int NumSend; + int NumReceive; + unsigned short MagicNum; + + /* + ** This is the staging buffer for parsing incoming packets. + ** RXSize is the allocated size of the RX buffer. + ** RXCount is the # of characters we currently have in our buffer. + */ + char *RXBuf; + int RXSize; + int RXCount; + + /*..................................................................... + Timing parameters for all connections + .....................................................................*/ + unsigned long RetryDelta; + unsigned long MaxRetries; + unsigned long Timeout; + + /* + ** Various Statistics + */ + int SendOverflows; + int ReceiveOverflows; + int CRCErrors; +}; + +#endif diff --git a/OBJECT.CPP b/OBJECT.CPP new file mode 100644 index 0000000..f26cfb9 --- /dev/null +++ b/OBJECT.CPP @@ -0,0 +1,1507 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\object.cpv 2.17 16 Oct 1995 16:49:22 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OBJECT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : August 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ObjectClass::Debug_Dump -- Displays status of the object class to the mono monitor. * + * ObjectClass::Detach_All -- Removes the object from all tracking systems. * + * ObjectClass::Detach_This_From_All -- Detatches this object from all others. * + * ObjectClass::Fire_Out -- Informs object that attached animation has finished. * + * ObjectClass::Get_Mission -- Fetches the current mission of this object. * + * ObjectClass::Init -- Initializes the basic object system. * + * ObjectClass::Limbo -- Brings the object into a state of limbo. * + * ObjectClass::Mark -- Handles basic marking logic. * + * ObjectClass::Mark_For_Redraw -- Marks object and system for redraw. * + * ObjectClass::Move -- Moves (by force) the object in the desired direction. * + * ObjectClass::ObjectClass -- Default constructor for objects. * + * ObjectClass::Passive_Click_With -- Right mouse button click process. * + * ObjectClass::Receive_Message -- Processes an incoming radio message. * + * ObjectClass::Render -- Displays the object onto the map. * + * ObjectClass::Repair -- Handles object repair control. * + * ObjectClass::Revealed -- Reveals this object to the house specified. * + * ObjectClass::Select -- Try to make this object the "selected" object. * + * ObjectClass::Sell_Back -- Sells the object -- if possible. * + * ObjectClass::Take_Damage -- Applies damage to the object. * + * ObjectClass::Unlimbo -- Brings the object into the game system. * + * ObjectClass::Unselect -- This will un-select the object if it was selected. * + * ObjectClass::Value -- Fetches the target value of this object. * + * ObjectClass::What_Action -- Deteremines what action to perform on specified object. * + * ObjectClass::What_Am_I -- RTTI query of this object type. * + * ObjectTypeClass::Cost_Of -- Returns the cost to buy this unit. * + * ObjectTypeClass::Dimensions -- Gets the dimensions of the object in pixels. * + * ObjectTypeClass::Get_Cameo_Data -- Fetches pointer to cameo data for this object type. * + * ObjectTypeClass::Max_Pips -- Fetches the maximum pips allowed for this object. * + * ObjectTypeClass::ObjectTypeClass -- Normal constructor for object type class objects. * + * ObjectTypeClass::Occupy_List -- Returns with simple occupation list for object. * + * ObjectTypeClass::One_Time -- Handles one time processing for object types. * + * ObjectTypeClass::Overlap_List -- Returns a pointer to a simple overlap list. * + * ObjectTypeClass::Time_To_Build -- Fetches the time to construct this object. * + * ObjectTypeClass::Who_Can_Build_Me -- Finds the factory building that can build this object* + * ObjectClass::What_Action -- Returns with the action to perform for this object. * + * ObjectClass::In_Which_Layer -- Fetches what layer this object is located in. * + * ObjectClass::Is_Techno -- Checks to see if this object is a techno type. * + * ObjectClass::Get_Ownable -- Fetches the house owner legality options for this object. * + * ObjectClass::Can_Repair -- Queries whether this object can be repaired. * + * ObjectClass::Can_Demolish -- Queries whether this object can be sold back. * + * ObjectClass::Can_Player_Fire -- Can the player give this object an attack mission? * + * ObjectClass::Can_Player_Move -- Can the player give this object a movement mission? * + * ObjectClass::Target_Coord -- Fetches the coordinate if this object is a target. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/* +** Selected objects have a special marking box around them. This is the shapes that are +** used for this purpose. +*/ +void const * ObjectTypeClass::SelectShapes = 0; + +void const * ObjectTypeClass::PipShapes = 0; + + +bool ObjectClass::Is_Infantry(void) const +{ + return(false); +} + + + +/*********************************************************************************************** + * ObjectTypeClass::ObjectTypeClass -- Normal constructor for object type class objects. * + * * + * This is the base constructor that is used when constructing the object type classes. * + * Every tangible game piece type calls this constructor for the ObjectTypeClass. This * + * class holds static information that is common to objects in general. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/23/1995 JLB : Created. * + *=============================================================================================*/ +ObjectTypeClass::ObjectTypeClass( + bool is_sentient, + bool is_flammable, + bool is_crushable, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + int name, + char const *ini, + ArmorType armor, + unsigned short strength) : + AbstractTypeClass(name, ini) +{ + IsSentient = is_sentient; + IsFlammable = is_flammable; + IsCrushable = is_crushable; + IsStealthy = is_stealthy; + IsSelectable = is_selectable; + IsLegalTarget = is_legal_target; + IsInsignificant = is_insignificant; + IsImmune = is_immune; + Armor = armor; + MaxStrength = strength; + ImageData = NULL; + //RadarIcon = NULL; +} + + +/*********************************************************************************************** + * ObjectTypeClass::Max_Pips -- Fetches the maximum pips allowed for this object. * + * * + * This routine will return the maximum number of pips that can be displayed for this * + * object. When dealing with generic objects, this value is always zero. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of pip boxes (empty or otherwise) to display. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectTypeClass::Max_Pips(void) const +{ + return(0); +} + + +/*********************************************************************************************** + * ObjectTypeClass::Dimensions -- Gets the dimensions of the object in pixels. * + * * + * This routine will fetch the dimensions of this object expressed as pixels width and * + * pixels height. This information can be used to intelligently update the clipping * + * rectangles. * + * * + * INPUT: width -- Reference to the width variable that will be filled in. * + * * + * height -- Reference to the height variable that will be filled in. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectTypeClass::Dimensions(int &width, int &height) const +{ + width = 10; + height = 10; +} + + +/*********************************************************************************************** + * ObjectTypeClass::Cost_Of -- Returns the cost to buy this unit. * + * * + * This routine will return the cost to purchase this unit. This routine is expected to be * + * overridden by the objects that can actually be purchased. All other object types can * + * simply return zero since this value won't be used. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the cost of the object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectTypeClass::Cost_Of(void) const +{ + return(0); +} + + +/*********************************************************************************************** + * ObjectTypeClass::Time_To_Build -- Fetches the time to construct this object. * + * * + * This routine will fetch the time in takes to construct this object. Objects that can * + * be constructed will override this routine in order to return a useful value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the time units (arbitrary) that it takes to construct this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectTypeClass::Time_To_Build(HousesType ) const +{ + return(0); +} + + +/*********************************************************************************************** + * ObjectTypeClass::Who_Can_Build_Me -- Finds the factory building that can build this object. * + * * + * This routine will search for a factory building that can build this object type. * + * * + * INPUT: this routine is just here to be overridden by other classes. * + * * + * OUTPUT: Returns with a pointer to the building that can construct the object specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +BuildingClass * ObjectTypeClass::Who_Can_Build_Me(bool, bool, HousesType) const +{ + return(NULL); +} + + +/*********************************************************************************************** + * ObjectTypeClass::Get_Cameo_Data -- Fetches pointer to cameo data for this object type. * + * * + * This routine will return with the cameo data pointer for this object type. It is * + * expected that objects that can appear on the sidebar will override this routine in order * + * to provide proper cameo data pointer. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the cameo shape data. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +void const * ObjectTypeClass::Get_Cameo_Data(void) const +{ + return(NULL); +} + + + +/*********************************************************************************************** + * ObjectClass::ObjectClass -- Default constructor for objects. * + * * + * This is the default constructor for objects. It is called as an inherent part of the * + * construction process for all the normal game objects instantiated. It serves merely to * + * initialize the object values to a common (default) state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Objects always start in a state of limbo. They must be Unlimbo()ed before they * + * can be used. * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass::ObjectClass(void) +{ + Coord = 0xFFFFFFFFL; // Some bogus illegal value. + Next = 0; // Not part of any object list. + Trigger = 0; // No associated trigger. + IsToDamage = false; + IsToDisplay = false; // Redraw is presumed unnecessary. + IsInLimbo = true; // Always presumed to start in limbo state. + IsSelected = false; // Limboed units cannot be selected. + IsDown = false; // Limboed units cannot be on the map. + IsAnimAttached = false; // Anim is not attached. + Strength = 255; // nominal strength value +} + + +/*********************************************************************************************** + * ObjectClass::What_Am_I -- RTTI query of this object type. * + * * + * This routine will never be called, but is here for completeness. Every object that * + * is derived from object class must overload this function and return their own proper * + * object RTTI value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the RTTI value that coresponds to the object's type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +RTTIType ObjectClass::What_Am_I(void) const +{ + return(RTTI_OBJECT); +} + + +/*********************************************************************************************** + * ObjectClass::What_Action -- Deteremines what action to perform on specified object. * + * * + * This routine will return that action that this object could perform if the mouse were * + * clicked over the object specified. * + * * + * INPUT: object -- Pointer to the object to check this object against when determining * + * the action to perform. * + * * + * OUTPUT: It returns that action that will be performed if the mouse were clicked over the * + * object. Since non-derived objects cannot do anything, and cannot even be * + * instantiated, this routine will always return ACTION_NONE. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +ActionType ObjectClass::What_Action(ObjectClass *) const +{ + return(ACTION_NONE); +} + + +/*********************************************************************************************** + * ObjectClass::What_Action -- Returns with the action to perform for this object. * + * * + * This routine is called when information on a potential action if the mouse were clicked * + * on the cell specified. This routine merely serves as a virtual placeholder so that * + * object types that can actually perform some action will override this routine to provide * + * true functionality. * + * * + * INPUT: cell -- The cell that the mouse is over and might be clicked on. * + * * + * OUTPUT: Returns with the action that this object would try to perform if the mouse were * + * clicked. Since objects at this level have no ability to do anything, this routine * + * will always returns ACTION_NONE unless it is overridden. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +ActionType ObjectClass::What_Action(CELL) const +{ + return(ACTION_NONE); +} + + +/*********************************************************************************************** + * ObjectClass::In_Which_Layer -- Fetches what layer this object is located in. * + * * + * The default layer for object location is the LAYER_GROUND. Aircraft will override this * + * routine and make adjustments as necessary according to the aircraft's altitude. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the layer that this object is located in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +LayerType ObjectClass::In_Which_Layer(void) const +{ + return(LAYER_GROUND); +} + + +/*********************************************************************************************** + * ObjectClass::Is_Techno -- Checks to see if this object is a techno type. * + * * + * Most active objects in the game are of the techno type. This routine will return true * + * if called on an object that is derived from TechnoClass. The RTTI interface is * + * insufficient for this purpose -- hence the existence of this routine. * + * * + * INPUT: none * + * * + * OUTPUT: Is this object derived from the TechnoClass object? This is true for units, * + * infantry, aircraft, and buildings. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Is_Techno(void) const +{ + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Get_Ownable -- Fetches the house owner legality options for this object. * + * * + * This routine will return the ownable bits for this object. Objects at this level can't * + * really be owned by anyone, but return the full spectrum of legality just to be safe. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the ownable flags (as a combined bitfield) for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +unsigned char ObjectClass::Get_Ownable(void) const +{ + return(0xff); +} + + +/*********************************************************************************************** + * ObjectClass::Can_Repair -- Queries whether this object can be repaired. * + * * + * Most objects cannot be repaired. This routine defaults to returning "false", but is * + * overridden by derived functions defined by object types that can support repair. * + * * + * INPUT: none * + * * + * OUTPUT: Can this object be repaired? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Can_Repair(void) const +{ + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Can_Demolish -- Queries whether this object can be sold back. * + * * + * This routine is used to determine if this object can be sold. Most objects cannot be * + * but for those objects that can, this routine will be overridden as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: Can this object be sold back? Typically, the answer is no. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Can_Demolish(void) const +{ + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Can_Player_Fire -- Can the player give this object an attack mission? * + * * + * This routine is used to determine if attacking is an option under player control with * + * respect to this unit. This routine will be overridden as necessary for those objects * + * that have the ability to attack. * + * * + * INPUT: none * + * * + * OUTPUT: Can this object be given an attack order by the player? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Can_Player_Fire(void) const +{ + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Can_Player_Move -- Can the player give this object a movement mission? * + * * + * This routine is used to determine if the player has the ability to command this object * + * with a movement mission. This routine will be overridden as necessary to support this * + * ability. * + * * + * INPUT: none * + * * + * OUTPUT: Can this object be given a movement mission by the player? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Can_Player_Move(void) const +{ + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Target_Coord -- Fetches the coordinate if this object is a target. * + * * + * When the coordinate to use when firing at this object is needed, this routine will * + * provide it. Normal objects just use the center of the object for this, but there are * + * some more sophisticated objects that are not fired upon the center. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to fire at if this object is a target. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE ObjectClass::Target_Coord(void) const +{ + return(Center_Coord()); +} + +COORDINATE ObjectClass::Center_Coord(void) const {return Coord;}; +COORDINATE ObjectClass::Render_Coord(void) const {return(Center_Coord());} +COORDINATE ObjectClass::Docking_Coord(void) const {return(Center_Coord());} +COORDINATE ObjectClass::Sort_Y(void) const {return Coord;}; +COORDINATE ObjectClass::Fire_Coord(int ) const {return Coord;}; +void ObjectClass::Record_The_Kill(TechnoClass * ) {}; +void ObjectClass::Do_Shimmer(void) {}; +int ObjectClass::Exit_Object(TechnoClass *) {return 0;}; +void ObjectClass::Hidden(void) {}; +void ObjectClass::Look(bool ) {}; +void ObjectClass::Active_Click_With(ActionType , ObjectClass *) {}; +void ObjectClass::Active_Click_With(ActionType , CELL ) {}; +void ObjectClass::Clicked_As_Target(int) {}; +bool ObjectClass::In_Range(COORDINATE , int) const {return false;}; +int ObjectClass::Weapon_Range(int) const {return 0x0000;}; +TARGET ObjectClass::As_Target(void) const {return TARGET_NONE;}; +void ObjectClass::Scatter(COORDINATE , bool) {}; +bool ObjectClass::Catch_Fire(void) {return false;}; + + +/*********************************************************************************************** + * ObjectClass::Fire_Out -- Informs object that attached animation has finished. * + * * + * This routine is called if there is an attached animation on this object and that * + * animation has finished. Typically, this is necessary for when trees are on fire. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Fire_Out(void) +{ +} + + +/*********************************************************************************************** + * ObjectClass::Value -- Fetches the target value of this object. * + * * + * This routine will return the target value of this object. The higher the number, the * + * better the object will be as a target. This routine is called when searching for * + * targets. Generic objects have no target potential, and this routine returns zero to * + * reflect that. Other object types will override this routine to return the appropriate * + * target value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the value of this object as a target. Higher values mean better * + * target. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectClass::Value(void) const +{ + return(0); +} + + +/*********************************************************************************************** + * ObjectClass::Get_Mission -- Fetches the current mission of this object. * + * * + * Generic objects don't have a mission, so this routine will just return MISSION_NONE. * + * However, techno objects do have a mission and this routine is overloaded to handle * + * those objects in order to return the correct mission value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current mission being followed by this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +MissionType ObjectClass::Get_Mission(void) const +{ + return(MISSION_NONE); +} + + +/*********************************************************************************************** + * ObjectClass::Repair -- Handles object repair control. * + * * + * This routine will control object repair mode. At the object level, no repair is * + * possible, so it is expected that any object that can repair will override this function * + * as necessary. * + * * + * INPUT: control -- The repair control parameter. * + * 0 = turn repair off * + * 1 = turn repair on * + * -1 = toggle repair state * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Repair(int ) +{ +} + + +/*********************************************************************************************** + * ObjectClass::Sell_Back -- Sells the object -- if possible. * + * * + * This routine is called to sell back the object. Override this routine for the more * + * sophisticated objects that can actually be sold back. Normal objects can't be sold and * + * this routine does nothing as a consequence. * + * * + * INPUT: control -- How to control the sell state of this object. * + * 0 = stop selling. * + * 1 = start selling. * + * -1 = toggle selling state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Sell_Back(int ) +{ +} + + +/*********************************************************************************************** + * ObjectClass::Move -- Moves (by force) the object in the desired direction. * + * * + * This routine will instantly move the object one cell in the specified direction. It * + * moves the object by force. This is typically ONLY used by the scenario editor * + * process. * + * * + * INPUT: facing -- The direction to move the object. * + * * + * OUTPUT: none * + * * + * WARNINGS: Naturally, this can cause illegal placement situations -- use with caution. * + * * + * HISTORY: * + * 06/19/1994 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Move(FacingType facing) +{ + COORDINATE coord; + + Mark(MARK_UP); + coord = Adjacent_Cell(Coord, facing); + if (Can_Enter_Cell(Coord_Cell(coord)) == MOVE_OK) { + Coord = coord; + } + Mark(MARK_DOWN); +} + + +/*********************************************************************************************** + * ObjectClass::Unselect -- This will un-select the object if it was selected. * + * * + * This routine brings a currently selected object into an unselected state. This is * + * needed when another object becomes selected as well as if the object is destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1994 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Unselect(void) +{ + if (IsSelected) { + CurrentObject.Delete(this); + + if (In_Which_Layer()==LAYER_GROUND){ + Mark(MARK_OVERLAP_UP); + } + + IsSelected = false; + + if (In_Which_Layer()==LAYER_GROUND){ + Mark(MARK_OVERLAP_DOWN); + } + } +} + + +/*********************************************************************************************** + * ObjectClass::Select -- Try to make this object the "selected" object. * + * * + * This routine is used to make this object into the one that is "selected". A selected * + * object usually displays a floating bar graph and is available to be given orders from * + * the player's I/O. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1994 JLB : Created. * + * 06/12/1995 JLB : Cannot select a loaner object. * + * 07/23/1995 JLB : Adds to head or tail depending on leader type flag. * + *=============================================================================================*/ +bool ObjectClass::Select(void) +{ + if (!Debug_Map && (IsSelected || !Class_Of().IsSelectable)) return(false); + + if (Can_Player_Move() && Is_Techno() && ((TechnoClass *)this)->IsALoaner) return(false); + + /* + ** Don't allow selection of object when in building placement mode. + */ + if (Map.PendingObject) return(false); + + /* + ** If selecting an object of a different house than the player's, make sure that + ** the entire selection list is cleared. + */ + if (CurrentObject.Count() > 0) { + if (Owner() != CurrentObject[0]->Owner() || CurrentObject[0]->Owner() != PlayerPtr->Class->House) { + Unselect_All(); + } + } + if (((TechnoTypeClass const &)Class_Of()).IsLeader) { + CurrentObject.Add_Head(this); + } else { + CurrentObject.Add(this); + } + + if (In_Which_Layer()==LAYER_GROUND){ + Mark(MARK_OVERLAP_UP); + } + + IsSelected = true; + + if (In_Which_Layer()==LAYER_GROUND){ + Mark(MARK_OVERLAP_DOWN); + } + return(true); +} + + +/*********************************************************************************************** + * ObjectClass::Render -- Displays the object onto the map. * + * * + * This routine will determine the location of the object and if it is roughly on the * + * visible screen, it will display it. Not displaying objects that are not on the screen * + * will save valuable time. * + * * + * INPUT: bool; Should the render be forced regardless of whether the object is flagged to * + * be redrawn? * + * * + * OUTPUT: bool; Was the draw code called for this object? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1994 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Render(bool forced) +{ + int x,y; + COORDINATE coord = Render_Coord(); + CELL cell = Coord_Cell(coord); + + if (Debug_Map || Debug_Unshroud || ((forced || IsToDisplay) && IsDown && !IsInLimbo)) { + IsToDisplay = false; + + /* + ** Draw the path as lines on the map if so directed and the object is one that + ** contains a path. + */ + if (Special.IsShowPath && IsSelected) { + switch (What_Am_I()) { + case RTTI_INFANTRY: + case RTTI_UNIT: + FootClass * foot = (FootClass *)this; + CELL cell; + int oldx, oldy; + + if (foot->Head_To_Coord() && foot->Path[0] != FACING_NONE) { + cell = Adjacent_Cell(Coord_Cell(foot->Head_To_Coord()), (FacingType)((foot->Path[0] + FACING_S) & FACING_NW)); + Map.Coord_To_Pixel(Cell_Coord(cell), oldx, oldy); + for (int index = 0; index < PATH_MAX; index++) { + if (foot->Path[index] == FACING_NONE) break; + cell = Adjacent_Cell(cell, foot->Path[index]); + if (Map.Coord_To_Pixel(Cell_Coord(cell), x, y)) { + LogicPage->Draw_Line(oldx, 8+oldy, x, 8+y, BLACK); + } + oldx = x; + oldy = y; + } + } + break; + } + } + + if (Map.Coord_To_Pixel(coord, x, y)) { + + /* + ** Draw the object itself + */ + Draw_It(x, y, WINDOW_TACTICAL); + +#ifdef SCENARIO_EDITOR + /* + ** Draw the trigger attached to the object. Draw_It is window- + ** relative, so add the window's x-coord to 'x'. + */ + if (Debug_Map && Trigger) { + Fancy_Text_Print(Trigger->Get_Name(), x + (WinX<<3), y, PINK, TBLACK, TPF_CENTER | TPF_NOSHADOW | TPF_6POINT); + } +#endif + + return(true); + } + } + return(false); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * ObjectClass::Debug_Dump -- Displays status of the object class to the mono monitor. * + * * + * This routine is used to display the current status of the object class to the mono * + * monitor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Debug_Dump(MonoClass *mono) const +{ + mono->Text_Print("X", 16 + (IsToDisplay?2:0), 18); + mono->Text_Print("X", 16 + (IsActive?2:0), 3); + mono->Text_Print("X", 16 + (IsInLimbo?2:0), 4); + mono->Text_Print("X", 16 + (IsSelected?2:0), 7); + mono->Set_Cursor(56, 1); + mono->Printf("%08lX", Coord); + mono->Set_Cursor(14, 1);mono->Printf("[%04X]", As_Target()); + mono->Set_Cursor(20, 3);mono->Printf("%2d[%d]", Strength, Class_Of().MaxStrength); +} +#endif + + +/*********************************************************************************************** + * ObjectTypeClass::Occupy_List -- Returns with simple occupation list for object. * + * * + * This routine returns a pointer to a simple occupation list for this object. Since at * + * this tier of the object class chain, the exact shape of the object is indeterminate, * + * this function merely returns a single cell occupation list. This actually works for * + * most vehicles. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to a simple occupation list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +short const * ObjectTypeClass::Occupy_List(bool) const +{ + static short const _list[] = {0, REFRESH_EOL}; + return(_list); +} + + +/*********************************************************************************************** + * ObjectTypeClass::Overlap_List -- Returns a pointer to a simple overlap list. * + * * + * This function returns a pointer to an overlap list for the object. An overlap list is * + * the offsets from the object's cell to get the cells the imagery overlaps, but is object * + * is not considered to occupy. Since at this stage, the overlap information is not * + * available, this function merely returns a pointer to an empty list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to the generic overlap list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +short const * ObjectTypeClass::Overlap_List(void) const +{ + static short const _list[] = {REFRESH_EOL}; + return(_list); +} + + +/*********************************************************************************************** + * ObjectTypeClass::One_Time -- Handles one time processing for object types. * + * * + * This routine is used to handle the once per game processing required for object types. * + * This consists of loading any data and initializing any data tables the game requires. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine goes to disk. * + * * + * HISTORY: * + * 11/01/1994 JLB : Created. * + *=============================================================================================*/ +void ObjectTypeClass::One_Time(void) +{ + SelectShapes = MixFileClass::Retrieve("SELECT.SHP"); +#if (FRENCH) + PipShapes = Hires_Retrieve("PIPS_F.SHP"); +#else +#if (GERMAN) + PipShapes = Hires_Retrieve("PIPS_G.SHP"); +#else + PipShapes = Hires_Retrieve("PIPS.SHP"); +#endif +#endif +} + + +/*********************************************************************************************** + * ObjectClass::Mark_For_Redraw -- Marks object and system for redraw. * + * * + * This routine will mark the object and inform the display system * + * that appropriate rendering is needed. Whenever it is determined * + * that an object needs to be redrawn, call this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This is a subordinate function to the function Mark(). If an object needs to * + * be redrawn it is probably better to call the function Mark(MARK_CHANGE) rather * + * than this function. This function does not inform the map system that * + * overlapping objects are to be redrawn and thus unless you are really sure that * + * this routine should be called, don't. * + * * + * HISTORY: * + * 05/08/1994 JLB : Created. * + * 12/23/1994 JLB : Flags map and flags unit only. * + *=============================================================================================*/ +void ObjectClass::Mark_For_Redraw(void) +{ + if (!IsToDisplay) { + IsToDisplay = true; + + /* + ** This tells the map rendering logic to "go through the motions" and call the + ** rendering function. In the rendering function, it will sort out what gets + ** rendered and what doesn't. + */ + Map.Flag_To_Redraw(false); + } +} + + +/*********************************************************************************************** + * ObjectClass::Limbo -- Brings the object into a state of limbo. * + * * + * An object brought into a state of limbo by this routine can be safely deleted. This * + * routine will remove the object from all game lists and tracking systems. It is called * + * prior to deleting the object or placing the object "on ice". * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the object successfully placed in limbo? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Limbo(void) +{ + if (GameActive && !IsInLimbo) { + + Unselect(); + Detach_All(); + Mark(MARK_UP); + + /* + ** Remove the object from the appropriate display list. + */ + Map.Remove(this, In_Which_Layer()); + + /* + ** Remove the object from the logic processing list. + */ + if (Class_Of().IsSentient) { + Logic.Delete(this); + } + + Hidden(); + IsInLimbo = true; + IsToDisplay = false; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Unlimbo -- Brings the object into the game system. * + * * + * This routine will place the object into the game tracking and display systems. It is * + * called as a consequence of creating the object. Every game object must be unlimboed at * + * some point. * + * * + * INPUT: coord -- The coordinate to place the object into the game system. * + * * + * dir (optional) -- initial facing direction for this object * + * * + * OUTPUT: bool; Was the game object successfully unlimboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 12/23/1994 JLB : Sets object strength. * + *=============================================================================================*/ +bool ObjectClass::Unlimbo(COORDINATE coord, DirType ) +{ + if (GameActive && IsInLimbo && !IsDown) { + if (ScenarioInit || Can_Enter_Cell(Coord_Cell(coord), FACING_NONE) == MOVE_OK) { + IsInLimbo = false; + IsToDisplay = false; + Coord = Class_Of().Coord_Fixup(coord); + + if (Mark(MARK_DOWN)) { + if (IsActive) { + + /* + ** Add the object to the appropriate map layer. This layer is used + ** for rendering purposes. + */ + if (In_Which_Layer() != LAYER_NONE) { + Map.Submit(this, In_Which_Layer()); + } + + if (Class_Of().IsSentient) { + Logic.Submit(this); + } + } + return(true); + } + } + } + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Detach_All -- Removes the object from all tracking systems. * + * * + * This routine will take the object and see that it is removed from all miscellaneous * + * tracking systems in the game. This operation is vital when deleting an object. It is * + * necessary so that when the object is removed from the game, existing game objects won't * + * be referencing a now invalid game object. This typically affects the targeting * + * and navigation computers of other game objects. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Detach_All(bool all) +{ + /* + ** Unselect this object if it was selected. + */ + if (all || Owner() != PlayerPtr->Class->House) { + Unselect(); + } + + Map.Detach(this); + + /* + ** Remove from targeting computers. + */ + Detach_This_From_All(As_Target(), all); +} + + +/*********************************************************************************************** + * ObjectClass::Detach_This_From_All -- Detatches this object from all others. * + * * + * This routine sweeps through all game objects and makes sure that it is no longer * + * referenced by them. Typically, this is called in preparation for the object's death * + * or limbo state. * + * * + * INPUT: target -- This object expressed as a target number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Detach_This_From_All(TARGET target, bool all) +{ + int index; + if (Target_Legal(target)) { + + for (index = 0; index < Houses.Count(); index++) { + Houses.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Teams.Count(); index++) { + Teams.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Units.Count(); index++) { + Units.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Infantry.Count(); index++) { + Infantry.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Aircraft.Count(); index++) { + Aircraft.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Buildings.Count(); index++) { + Buildings.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Bullets.Count(); index++) { + Bullets.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Anims.Count(); index++) { + Anims.Ptr(index)->Detach(target, all); + } + } +} + + +/*********************************************************************************************** + * ObjectClass::Receive_Message -- Processes an incoming radio message. * + * * + * Any radio message received that applies to objects in general are handled by this * + * routine. Typically, this is the "redraw" message, which occurs when another object is * + * loading or unloading and thus overlapping. * + * * + * INPUT: message -- The message received. * + * * + * OUTPUT: Returns with the appropriate radio response. If the message was recognized, then * + * RADIO_ROGER is returned, otherwise, just RADIO_STATIC is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +RadioMessageType ObjectClass::Receive_Message(RadioClass *, RadioMessageType message, long & ) +{ + switch (message) { + + /* + ** This message serves as a rendering convenience. It lets the system + ** know that there might be a visual conflict and the unit in radio + ** contact should be redrawn. This typically occurs when a vehicle + ** is being unloaded from a hover lander. + */ + case RADIO_REDRAW: + Mark(MARK_CHANGE); + return(RADIO_ROGER); + + default: + break; + } + return(RADIO_STATIC); +} + + +/*********************************************************************************************** + * ObjectClass::Take_Damage -- Applies damage to the object. * + * * + * This routine applies damage to the object according to the damage parameters. It handles * + * reducing the strength of the object and also returns the result of that damage. The * + * result value can be examined to determine if the object was destroyed, greatly damaged, * + * or other results. * + * * + * INPUT: damage -- Reference to the damage number to apply. This number will be adjusted * + * according to defensive armor and distance. Examine this value after * + * the call to determine the actual amount of damage applied. * + * * + * distance -- The distance (in leptons) from the center of the damage causing * + * explosion to the object itself. * + * * + * warhead -- The warhead type that is causing the damage. * + * * + * OUTPUT: Returns the ResultType that indicates what the affect of the damage was. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/29/1994 JLB : Created. * + * 12/27/1994 JLB : Trigger event processing for attacked or destroyed. * + * 01/01/1995 JLB : Reduces damage greatly depending on range. * + *=============================================================================================*/ +ResultType ObjectClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source) +{ + ResultType result = RESULT_NONE; + int oldstrength = Strength; + + if (oldstrength && damage && !Class_Of().IsImmune) { + int maxstrength = Class_Of().MaxStrength; + + /* + ** Modify damage based on the warhead type and the armor of the object. This results + ** in a reduced damage value, but never below 1 damage point. + */ + damage = Modify_Damage(damage, warhead, Class_Of().Armor, distance); + if (!damage) return(RESULT_NONE); + + /* + ** At this point, we KNOW that at least light damage has occurred. + */ + result = RESULT_LIGHT; + + /* + ** A non-fatal blow has occurred. Check to see if the object transitioned to below + ** half strength or if it is now down to one hit point. + */ + if (oldstrength > damage) { + + if (oldstrength >= (maxstrength >> 1) && (oldstrength-damage) < (maxstrength >> 1)) { + result = RESULT_HALF; + } + } else { + + /* + ** When an object is damaged to destruction, it will instead stop at one + ** damage point. This will prolong the damage state as well as + ** give greater satisfaction when it is finally destroyed. + */ + damage = oldstrength; + } + + /* + ** Apply the damage to the object. + */ + Strength = oldstrength - damage; + + /* + ** Check to see if the object is majorly damaged or destroyed. + */ + switch (Strength) { + case 0: + Record_The_Kill(source); + result = RESULT_DESTROYED; + Detach_All(); + break; + + case 1: + result = RESULT_MAJOR; + break; + + default: + break; + } + + /* + ** Handle any trigger event associated with this object. + */ + if (source && Trigger && result != RESULT_DESTROYED) { + Trigger->Spring(EVENT_ATTACKED, this); + } + + /* + ** If any damage was assessed and this object is selected, then flag + ** the object to be redrawn so that the health bar will be updated. + */ + if (result != RESULT_NONE && IsSelected) { + Mark(MARK_CHANGE); + } + } + + /* + ** Return with the result of the damage taken. + */ + return(result); +} + + +/*********************************************************************************************** + * ObjectClass::Mark -- Handles basic marking logic. * + * * + * This routine handles the base logic for marking an object up or down on the map. It * + * manages the IsDown flag as well as flagging the object to be redrawn if necessary. * + * Whenever an object is to be marked, it should call this base class function first. If * + * this function returns true, then the higher level function should proceed with its own * + * logic. * + * * + * INPUT: mark -- The marking method to use for this object. It can be either MARK_DOWN, * + * MARK_UP, or MARK_CHANGE. * + * * + * OUTPUT: bool; Was the object marked successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Mark(MarkType mark) +{ + TechnoClass *tech; + CELL cell; + int threat; + HousesType house; + + if (!IsInLimbo && IsActive) { + + /* + ** A mark for change is always successful UNLESS the object + ** is not placed down or has already been flagged as changed + ** this game frame. + */ + if (mark == MARK_CHANGE) { + if (IsToDisplay) return(false); + if (IsDown == true) { + Mark_For_Redraw(); + return(true); + } + return(false); + } + + /* + ** Handle adding or removing the object in the cells' overlap lists + */ + if (mark == MARK_OVERLAP_UP) { + if (IsDown == true) { + Map.Overlap_Up(Coord_Cell(Coord),this); + Mark_For_Redraw(); + return(true); + } + } + if (mark == MARK_OVERLAP_DOWN) { + if (IsDown == true) { + Map.Overlap_Down(Coord_Cell(Coord),this); + Mark_For_Redraw(); + return(true); + } + } + + /* + ** It is important to know whether the object is a techno class + ** or not to see if we have to adjust the regional threat ratings + */ + if (Is_Techno()) { + tech = (TechnoClass *)this; + threat = tech->Risk(); + house = tech->Owner(); + cell = Coord_Cell(Coord); + } else + tech = NULL; + + /* + ** Marking down is only successful if the object isn't already + ** placed down. + */ + if (mark == MARK_DOWN && !IsDown) { + if (tech && GameToPlay == GAME_NORMAL) { + Map[cell].Adjust_Threat(house, threat); + } + IsDown = true; + Mark_For_Redraw(); + return(true); + } + + /* + ** Lifting up is only successful if the object isn't already + ** lifted up from the map. + */ + if (mark == MARK_UP && IsDown) { + if (tech && GameToPlay == GAME_NORMAL) { + Map[cell].Adjust_Threat(house, -threat); + } + IsDown = false; + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Init -- Initializes the basic object system. * + * * + * This routine should be called when the basic object system needs to be initialized. This * + * occurs when the scenario is about to be loaded. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Init(void) +{ + CurrentObject.Clear(); +} + + +/*********************************************************************************************** + * ObjectClass::Revealed -- Reveals this object to the house specified. * + * * + * This routine is called when this object gets revealed to the house specified. * + * * + * INPUT: house -- Pointer to the house that this object is being revealed to. * + * * + * OUTPUT: Was this object revealed for the first time to this house? Generic objects always * + * return true unless an invalid house pointer was specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Revealed(HouseClass * house) +{ + return(house != NULL); +} + +// These can't be made inline (for various reasons). +short const * ObjectClass::Occupy_List(bool placement) const {return(Class_Of().Occupy_List(placement));}; +short const * ObjectClass::Overlap_List(void) const {return(Class_Of().Overlap_List());}; +BuildingClass * ObjectClass::Who_Can_Build_Me(bool intheory, bool legal) const {return(Class_Of().Who_Can_Build_Me(intheory, legal, Owner()));}; +unsigned ObjectClass::Health_Ratio(void) const {return(Cardinal_To_Fixed(Class_Of().MaxStrength, Strength));}; +int ObjectClass::Full_Name(void) const {return Class_Of().Full_Name();}; + diff --git a/OBJECT.H b/OBJECT.H new file mode 100644 index 0000000..2d35cb7 --- /dev/null +++ b/OBJECT.H @@ -0,0 +1,245 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\object.h_v 2.15 16 Oct 1995 16:46:16 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OBJECT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : April 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef OBJECT_H +#define OBJECT_H + +#include "abstract.h" + +class ObjectClass; +class TechnoClass; +class ObjectTypeClass; +class HouseClass; +class TriggerClass; +class BuildingClass; +class RadioClass; + +//extern "C" { +//unsigned Cardinal_To_Fixed(unsigned base, unsigned cardinal); +//} + +/********************************************************************** +** Every game object (that can exist on the map) is ultimately derived from this object +** class. It holds the common information between all objects. This is primarily the +** object unique ID number and its location in the world. All common operations between +** game objects are represented by virtual functions in this class. +*/ +class ObjectClass : public AbstractClass +{ + public: + /* + ** The object can be in one of two states -- placed down on the map, or not. If the + ** object is placed down on the map, then this flag will be true. + */ + unsigned IsDown:1; + + /* + ** This is a support flag that is only used while building a list of objects to + ** be damaged by a proximity affect (explosion). When this flag is set, this object + ** will not be added to the list of units to damage. When damage is applied to the + ** object, this flag is cleared again. This process ensures that an object is never + ** subject to "double jeapordy". + */ + unsigned IsToDamage:1; + +// private: + /* + ** Is this object flagged to be displayed during the next rendering process? This + ** flag could be set by many different circumstances. It is automatically cleared + ** when the object is rerendered. + */ + unsigned IsToDisplay:1; + + + public: + /* + ** An object in the game may be valid yet held in a state of "limbo". Units are in such + ** a state if they are being transported or are otherwise "inside" another unit. They can + ** also be in limbo if they have been created but are being held until the proper time + ** for delivery. + */ + unsigned IsInLimbo:1; + + /* + ** When an object is "selected" it is given a floating bar graph or other graphic imagery + ** to display this fact. When the player performs I/O, the actions may have a direct + ** bearing on the actions of the currently selected object. For quick checking purposes, + ** if this object is the one that is "selected", this flag will be true. + */ + unsigned IsSelected:1; + + /* + ** If an animation is attached to this object, then this flag will be true. + */ + unsigned IsAnimAttached:1; + + /* + ** Several objects could exist in the same cell list. This is a pointer to the + ** next object in the cell list. The objects in this list are not in any + ** significant order. + */ + ObjectClass * Next; + + /* + ** Every object can be assigned a trigger; the same trigger can be assigned + ** to multiple objects. + */ + TriggerClass * Trigger; + + /* + ** This is the current strength of this object. + */ + short Strength; + + /*----------------------------------------------------------------------------------- + ** Constructor & destructors. + */ + ObjectClass(void); + virtual ~ObjectClass(void) {}; + virtual RTTIType What_Am_I(void) const; + int operator < (ObjectClass const & object) const {return Sort_Y() < object.Sort_Y();}; + int operator > (ObjectClass const & object) const {return Sort_Y() > object.Sort_Y();}; + + /* + ** Object selection control. + */ + static void Init(void); + + /* + ** Query functions. + */ + virtual ActionType What_Action(ObjectClass *) const; + virtual ActionType What_Action(CELL) const; + virtual LayerType In_Which_Layer(void) const; + virtual bool Is_Infantry(void) const; + virtual bool Is_Techno(void) const; + virtual unsigned char Get_Ownable(void) const; + virtual ObjectTypeClass const & Class_Of(void) const = 0; + virtual int Full_Name(void) const; + virtual bool Can_Repair(void) const; + virtual bool Can_Demolish(void) const; + virtual bool Can_Player_Fire(void) const; + virtual bool Can_Player_Move(void) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Docking_Coord(void) const; + virtual COORDINATE Target_Coord(void) const; + virtual COORDINATE Center_Coord(void) const; + virtual COORDINATE Render_Coord(void) const; + virtual COORDINATE Sort_Y(void) const; + virtual COORDINATE Fire_Coord(int ) const; + + /* + ** Object entry and exit from the game system. + */ + virtual bool Limbo(void); + virtual bool Unlimbo(COORDINATE , DirType facing = DIR_N); + virtual void Detach(TARGET, bool) {}; + virtual void Detach_All(bool all=true); + static void Detach_This_From_All(TARGET target, bool all=true); + virtual void Record_The_Kill(TechnoClass * ); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void Do_Shimmer(void); + virtual int Exit_Object(TechnoClass *); + virtual bool Render(bool forced); + virtual short const * Occupy_List(bool placement=false) const; + virtual short const * Overlap_List(void) const; + virtual unsigned Health_Ratio(void) const; + virtual void Draw_It(int x, int y, WindowNumberType ) = 0; + virtual void Hidden(void); + virtual void Look(bool =false); + virtual bool Mark(MarkType); + + private: + virtual void Mark_For_Redraw(void); + + public: + + /* + ** User I/O. + */ + virtual void Active_Click_With(ActionType , ObjectClass *); + virtual void Active_Click_With(ActionType , CELL ); + virtual void Clicked_As_Target(int = 7); + virtual bool Select(void); + virtual void Unselect(void); + + /* + ** Combat related. + */ + virtual bool In_Range(COORDINATE , int=0) const; + virtual int Weapon_Range(int =0) const; + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0); + virtual TARGET As_Target(void) const; + virtual void Scatter(COORDINATE , bool=false); + virtual bool Catch_Fire(void); + virtual void Fire_Out(void); + virtual int Value(void) const; + virtual MissionType Get_Mission(void) const; + + /* + ** AI. + */ + virtual BuildingClass * Who_Can_Build_Me(bool intheory, bool legal) const; + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual bool Revealed(HouseClass * house); + virtual void Repair(int ); + virtual void Sell_Back(int ); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + virtual void Move(FacingType); +}; + +#endif diff --git a/ODATA.CPP b/ODATA.CPP new file mode 100644 index 0000000..1d34cc4 --- /dev/null +++ b/ODATA.CPP @@ -0,0 +1,925 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\odata.cpv 2.16 16 Oct 1995 16:50:36 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ODATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 16, 1994 * + * * + * Last Update : April 19, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * OverlayTypeClass::Display -- Displays a generic representation of overlay. * + * OverlayTypeClass::From_Name -- Determine overlay from ASCII name. * + * OverlayTypeClass::Init -- Loads graphic data for overlays. * + * OverlayTypeClass::Occupy_List -- Determines occupation list. * + * OverlayTypeClass::Prep_For_Add -- Prepares to add overlay to scenario. * + * OverlayTypeClass::Create_And_Place -- Creates and places a overlay object on the map. * + * OverlayTypeClass::Create_One_Of -- Creates an object of this overlay type. * + * OverlayTypeClass::OverlayTypeClass -- Constructor for overlay type objects. * + * OverlayTypeClass::Draw_It -- Draws the overlay image at location specified. * + * OverlayTypeClass::One_Time -- Loads all the necessary general overlay shape data. * + * OverlayTypeClass::Init -- Initialize the overlay graphic data per theater. * + * OverlayTypeClass::Radar_Icon -- Gets a pointer to the radar icons * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "type.h" + + +static OverlayTypeClass const Road( + OVERLAY_ROAD, // Overlay type number. + "ROAD", // INI name of overlay. + TXT_CONCRETE, // Full name of overlay. + LAND_ROAD, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Concrete( + OVERLAY_CONCRETE, // Overlay type number. + "CONC", // INI name of overlay. + TXT_CONCRETE, // Full name of overlay. + LAND_ROAD, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Sandbag( + OVERLAY_SANDBAG_WALL, // Overlay type number. + "SBAG", // INI name of overlay. + TXT_SANDBAG_WALL, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 1, // If this is a wall, how many damage levels? + 20, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Cyclone( + OVERLAY_CYCLONE_WALL, // Overlay type number. + "CYCL", // INI name of overlay. + TXT_CYCLONE_WALL, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 2, // If this is a wall, how many damage levels? + 10, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Brick( + OVERLAY_BRICK_WALL, // Overlay type number. + "BRIK", // INI name of overlay. + TXT_BRICK_WALL, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 3, // If this is a wall, how many damage levels? + 70, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + true, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Barbwire( + OVERLAY_BARBWIRE_WALL, // Overlay type number. + "BARB", // INI name of overlay. + TXT_BARBWIRE_WALL, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 1, // If this is a wall, how many damage levels? + 2, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Wood( + OVERLAY_WOOD_WALL, // Overlay type number. + "WOOD", // INI name of overlay. + TXT_WOOD_WALL, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 1, // If this is a wall, how many damage levels? + 2, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + true, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Tiberium1( + OVERLAY_TIBERIUM1, // Overlay type number. + "TI1", // INI name of overlay. + TXT_TIBERIUM, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Tiberium2( + OVERLAY_TIBERIUM2, // Overlay type number. + "TI2", // INI name of overlay. + TXT_TIBERIUM, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Tiberium3( + OVERLAY_TIBERIUM3, // Overlay type number. + "TI3", // INI name of overlay. + TXT_TIBERIUM, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Tiberium4( + OVERLAY_TIBERIUM4, // Overlay type number. + "TI4", // INI name of overlay. + TXT_TIBERIUM, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Tiberium5( + OVERLAY_TIBERIUM5, // Overlay type number. + "TI5", // INI name of overlay. + TXT_TIBERIUM, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Tiberium6( + OVERLAY_TIBERIUM6, // Overlay type number. + "TI6", // INI name of overlay. + TXT_TIBERIUM, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Tiberium7( + OVERLAY_TIBERIUM7, // Overlay type number. + "TI7", // INI name of overlay. + TXT_TIBERIUM, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Tiberium8( + OVERLAY_TIBERIUM8, // Overlay type number. + "TI8", // INI name of overlay. + TXT_TIBERIUM, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Tiberium9( + OVERLAY_TIBERIUM9, // Overlay type number. + "TI9", // INI name of overlay. + TXT_TIBERIUM, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Tiberium10( + OVERLAY_TIBERIUM10, // Overlay type number. + "TI10", // INI name of overlay. + TXT_TIBERIUM, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Tiberium11( + OVERLAY_TIBERIUM11, // Overlay type number. + "TI11", // INI name of overlay. + TXT_TIBERIUM, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Tiberium12( + OVERLAY_TIBERIUM12, // Overlay type number. + "TI12", // INI name of overlay. + TXT_TIBERIUM, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Squish( + OVERLAY_SQUISH, // Overlay type number. + "SQUISH", // INI name of overlay. + TXT_SQUISH, // Full name of overlay. + LAND_CLEAR, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); + +static OverlayTypeClass const V12( + OVERLAY_V12, // Overlay type number. + "V12", // INI name of overlay. + TXT_CIV12, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V13( + OVERLAY_V13, // Overlay type number. + "V13", // INI name of overlay. + TXT_CIV13, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V14( + OVERLAY_V14, // Overlay type number. + "V14", // INI name of overlay. + TXT_CIV14, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V15( + OVERLAY_V15, // Overlay type number. + "V15", // INI name of overlay. + TXT_CIV15, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V16( + OVERLAY_V16, // Overlay type number. + "V16", // INI name of overlay. + TXT_CIV16, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V17( + OVERLAY_V17, // Overlay type number. + "V17", // INI name of overlay. + TXT_CIV17, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V18( + OVERLAY_V18, // Overlay type number. + "V18", // INI name of overlay. + TXT_CIV18, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const FlagSpot( + OVERLAY_FLAG_SPOT, // Overlay type number. + "FPLS", // INI name of overlay. + TXT_FLAG_SPOT, // Full name of overlay. + LAND_CLEAR, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const WoodCrate( + OVERLAY_WOOD_CRATE, // Overlay type number. + "WCRATE", // INI name of overlay. + TXT_WOOD_CRATE, // Full name of overlay. + LAND_CLEAR, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + false, // Is this a wall type? + true // Is this a crate? +); +static OverlayTypeClass const SteelCrate( + OVERLAY_STEEL_CRATE, // Overlay type number. + "SCRATE", // INI name of overlay. + TXT_STEEL_CRATE, // Full name of overlay. + LAND_CLEAR, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + false, // Is this a wall type? + true // Is this a crate? +); + + +OverlayTypeClass const * const OverlayTypeClass::Pointers[OVERLAY_COUNT] = { + &Concrete, // OVERLAY_CONCRETE + &Sandbag, // OVERLAY_SANDBAG_WALL + &Cyclone, // OVERLAY_CYCLONE_WALL + &Brick, // OVERLAY_BRICK_WALL + &Barbwire, // OVERLAY_BARBWIRE_WALL + &Wood, // OVERLAY_WOOD_WALL + &Tiberium1, // OVERLAY_TIBERIUM1 + &Tiberium2, // OVERLAY_TIBERIUM2 + &Tiberium3, // OVERLAY_TIBERIUM3 + &Tiberium4, // OVERLAY_TIBERIUM4 + &Tiberium5, // OVERLAY_TIBERIUM5 + &Tiberium6, // OVERLAY_TIBERIUM6 + &Tiberium7, // OVERLAY_TIBERIUM7 + &Tiberium8, // OVERLAY_TIBERIUM8 + &Tiberium9, // OVERLAY_TIBERIUM9 + &Tiberium10, // OVERLAY_TIBERIUM10 + &Tiberium11, // OVERLAY_TIBERIUM11 + &Tiberium12, // OVERLAY_TIBERIUM12 + &Road, // OVERLAY_ROAD + &Squish, // OVERLAY_SQUISH + &V12, // OVERLAY_V12 + &V13, // OVERLAY_V13 + &V14, // OVERLAY_V14 + &V15, // OVERLAY_V15 + &V16, // OVERLAY_V16 + &V17, // OVERLAY_V17 + &V18, // OVERLAY_V18 + &FlagSpot, // OVERLAY_FLAG_SPOT + &WoodCrate, // OVERLAY_WOOD_CRATE + &SteelCrate, // OVERLAY_STEEL_CRATE +}; + + +/*********************************************************************************************** + * OverlayTypeClass::OverlayTypeClass -- Constructor for overlay type objects. * + * * + * This is the constructor for the overlay types. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1994 JLB : Created. * + *=============================================================================================*/ +OverlayTypeClass::OverlayTypeClass( + OverlayType iconset, + char const *ininame, + int fullname, + LandType ground, + int damagelevels, + int damagepoints, + bool isradarvisible, + bool iswooden, + bool istarget, + bool iscrushable, + bool istiberium, + bool high, + bool theater, + bool walltype, + bool iscrate) : + ObjectTypeClass(false, + false, + iscrushable, + true, + false, + istarget, + true, + false, + fullname, + ininame, + ARMOR_NONE, + 0) +{ + IsRadarVisible = isradarvisible; + IsCrate = iscrate; + IsWooden = iswooden; + IsHigh = high; + IsTheater = theater; + IsTiberium = istiberium; + Type = iconset; + Land = ground; + IsWall = walltype; + DamageLevels = damagelevels; + DamagePoints = damagepoints; +} + + +/*********************************************************************************************** + * OverlayTypeClass::One_Time -- Loads all the necessary general overlay shape data. * + * * + * This routine should be called once when the game first starts. It will establish * + * pointers to the graphic data of the overlay objects. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayTypeClass::One_Time(void) +{ +} + + +/*********************************************************************************************** + * OverlayTypeClass::From_Name -- Determine overlay from ASCII name. * + * * + * This routine is used to determine the overlay number given only * + * an ASCII representation. The scenario loader uses this routine * + * to construct the map from the INI control file. * + * * + * INPUT: name -- Pointer to the ASCII name of the overlay. * + * * + * OUTPUT: Returns with the overlay number. If the name had no match, * + * then returns with OVERLAY_NONE. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +OverlayType OverlayTypeClass::From_Name(char const *name) +{ + if (name) { + for (OverlayType index = OVERLAY_FIRST; index < OVERLAY_COUNT; index++) { + if (stricmp(As_Reference(index).IniName, name) == 0) { + return(index); + } + } + } + return(OVERLAY_NONE); +} + + +/*********************************************************************************************** + * OverlayTypeClass::Occupy_List -- Determines occupation list. * + * * + * This routine is used to examine the overlay map and build an * + * occupation list. This list is used to render a overlay cursor as * + * well as placement of icon numbers. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a pointer to the overlay occupation list. * + * * + * WARNINGS: The return pointer is valid only until the next time that * + * this routine is called. * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +short const * OverlayTypeClass::Occupy_List(bool) const +{ + static short _simple[] = {0, REFRESH_EOL}; + + return(_simple); +} + + +/*************************************************************************** + * OverlayTypeClass::Radar_Icon -- Gets a pointer to the radar icons * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1995 PWG : Created. * + *=========================================================================*/ +unsigned char * OverlayTypeClass::Radar_Icon(int data) const +{ + unsigned char *icon = (unsigned char *)Get_Radar_Data(); // Get pointer to radar icons + icon += (data * 9) + 2; // move icon ptr to correct icon + return(icon); // Return the correct icon +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * OverlayTypeClass::Display -- Displays a generic representation of overlay. * + * * + * This routine is used to display a generic view of the overlay * + * object. This is necessary for selection in the scenario editor. * + * * + * INPUT: x,y -- The coordinates to center the display about. * + * * + * window-- The window to base the coordinates upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayTypeClass::Display(int x, int y, WindowNumberType window, HousesType ) const +{ + /* + ---------------------------- Draw the shape ------------------------------ + */ + if (Get_Image_Data()) { + int frame = 0; + + if (IsTiberium) { + frame = 7; + } + + CC_Draw_Shape(Get_Image_Data(), frame, x, y, window, SHAPE_NORMAL|SHAPE_CENTER|SHAPE_WIN_REL); + } +} + + +/*********************************************************************************************** + * OverlayTypeClass::Prep_For_Add -- Prepares to add overlay to scenario. * + * * + * This routine prepares a list of overlay objects so that the * + * scenario editor can use this list to display a dialog box. The * + * selection of a overlay object will allow its placement upon the * + * map. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1994 JLB : Created * + *=============================================================================================*/ +void OverlayTypeClass::Prep_For_Add(void) +{ + for (OverlayType index = OVERLAY_FIRST; index < OVERLAY_COUNT; index++) { + OverlayTypeClass const & overlay = As_Reference(index); + if (overlay.Get_Image_Data() && !overlay.IsWall && (!overlay.IsTiberium || index == OVERLAY_TIBERIUM1)) { + Map.Add_To_List(&overlay); + } + } +} +#endif + + +/*********************************************************************************************** + * OverlayTypeClass::Create_And_Place -- Creates and places a overlay object on the map. * + * * + * This support routine is used by the scenario editor to add a overlay object to the map * + * and to the game. * + * * + * INPUT: cell -- The cell to place the overlay object. * + * * + * OUTPUT: bool; Was the overlay object placed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +bool OverlayTypeClass::Create_And_Place(CELL cell, HousesType ) const +{ + if (new OverlayClass(Type, cell)) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * OverlayTypeClass::Create_One_Of -- Creates an object of this overlay type. * + * * + * This routine will create an object of this type. For certain overlay objects, such * + * as walls, it is actually created as a building. The "building" wall is converted into * + * a overlay at the moment of placing down on the map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the appropriate object for this overlay type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * OverlayTypeClass::Create_One_Of(HouseClass *) const +{ + return(new OverlayClass(Type, -1)); +} + + +/*********************************************************************************************** + * OverlayTypeClass::Draw_It -- Draws the overlay image at location specified. * + * * + * This routine will draw the overlay shape at the coordinates specified. It is presumed * + * that all the underlying layers have already been rendered by the time this routine is * + * called. * + * * + * INPUT: x, y -- Coordinate (upper left) of cell where overlay image is to be drawn. * + * * + * data -- Cell specific data that controls the imagery of the overlay. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayTypeClass::Draw_It(int x, int y, int data) const +{ + CC_Draw_Shape(Get_Image_Data(), data, Map.TacPixelX+x+(CELL_PIXEL_W>>1), Map.TacPixelY+y+(CELL_PIXEL_H>>1), WINDOW_MAIN, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, NULL, Map.UnitShadow); +} + + +/*********************************************************************************************** + * OverlayTypeClass::Init -- Initialize the overlay graphic data per theater. * + * * + * This routine will update the overlay graphic data according to the theater specified. * + * It is typically called when the scenario is first loaded (theater change). * + * * + * INPUT: theater -- The theater to load specific data for. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + * 4/25/1996 ST : Modified to load theater specific sidebar icons if available * + *=============================================================================================*/ +void OverlayTypeClass::Init(TheaterType theater) +{ + if (theater != LastTheater){ + for (OverlayType index = OVERLAY_FIRST; index < OVERLAY_COUNT; index++) { + OverlayTypeClass const & overlay = As_Reference(index); + char fullname[_MAX_FNAME+_MAX_EXT]; // Fully constructed iconset name. + + if (overlay.IsTheater) { + _makepath(fullname, NULL, NULL, overlay.IniName, Theaters[theater].Suffix); + } else { + _makepath(fullname, NULL, NULL, overlay.IniName, ".SHP"); + } + ((void const *&)overlay.ImageData) = MixFileClass::Retrieve(fullname); + + IsTheaterShape = overlay.IsTheater; + if (overlay.RadarIcon) delete[] (char *)overlay.RadarIcon; + ((void const *&)overlay.RadarIcon) = Get_Radar_Icon(overlay.Get_Image_Data(), 0, -1, 3); + IsTheaterShape = false; + } + } +} diff --git a/OPTIONS.CPP b/OPTIONS.CPP new file mode 100644 index 0000000..7e59193 --- /dev/null +++ b/OPTIONS.CPP @@ -0,0 +1,797 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\options.cpv 2.17 16 Oct 1995 16:51:28 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : June 30, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * OptionsClass::Adjust_Palette -- Adjusts the palette according to the settings specified. * + * OptionsClass::Get_Brightness -- Fetches the current brightness setting. * + * OptionsClass::Get_Color -- Fetches the current color setting. * + * OptionsClass::Get_Contrast -- Gets the current contrast setting. * + * OptionsClass::Get_Game_Speed -- Fetches the current game speed setting. * + * OptionsClass::Get_Scroll_Rate -- Fetches the current scroll rate setting. * + * OptionsClass::Get_Tint -- Fetches the current tint setting. * + * OptionsClass::Load_Settings -- reads options settings from the INI file * + * OptionsClass::Normalize_Delay -- Normalizes delay factor to keep rate constant. * + * OptionsClass::One_Time -- This performs any one time initialization for the options class.* + * OptionsClass::OptionsClass -- The default constructor for the options class. * + * OptionsClass::Process -- Handles all the options graphic interface. * + * OptionsClass::Save_Settings -- writes options settings to the INI file * + * OptionsClass::Set -- Sets options based on current settings * + * OptionsClass::Set_Brightness -- Sets the brightness level to that specified. * + * OptionsClass::Set_Color -- Sets the color to the value specified. * + * OptionsClass::Set_Contrast -- Sets the contrast to the value specified. * + * OptionsClass::Set_Game_Speed -- Sets the game speed as specified. * + * OptionsClass::Set_Repeat -- Controls the score repeat option. * + * OptionsClass::Set_Score_Volume -- Sets the global score volume to that specified. * + * OptionsClass::Set_Scroll_Rate -- Sets the scroll rate as specified. * + * OptionsClass::Set_Shuffle -- Controls the play shuffle setting. * + * OptionsClass::Set_Sound_Volume -- Sets the sound effects volume level. * + * OptionsClass::Set_Tint -- Sets the tint setting. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "options.h" + + +/*********************************************************************************************** + * OptionsClass::OptionsClass -- The default constructor for the options class. * + * * + * This is the constructor for the options class. It handles setting up all the globals * + * necessary for the options. This includes setting them to their default state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/21/1994 JLB : Created. * + *=============================================================================================*/ +OptionsClass::OptionsClass(void) +{ + GameSpeed = TIMER_SECOND / TICKS_PER_SECOND; + ScrollRate = TIMER_SECOND / TICKS_PER_SECOND; + Volume = 0xE0; + ScoreVolume = 0x90; + Contrast = 0x80; + Color = 0x80; + Contrast = 0x80; + Tint = 0x80; + Brightness = 0x80; + AutoScroll = true; +#if (GERMAN | FRENCH) + IsDeathAnnounce = true; +#else + IsDeathAnnounce = false; +#endif + IsScoreRepeat = false; + IsScoreShuffle = false; + IsFreeScroll = false; +} + + +/*********************************************************************************************** + * OptionsClass::One_Time -- This performs any one time initialization for the options class. * + * * + * This routine should be called only once and it will perform any inializations for the * + * options class that is needed. This may include things like file loading and memory * + * allocation. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once. * + * * + * HISTORY: * + * 07/21/1994 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::One_Time(void) +{ + Set_Score_Vol(ScoreVolume); +} + + +/*********************************************************************************************** + * OptionsClass::Process -- Handles all the options graphic interface. * + * * + * This routine is the main control for the visual representation of the options * + * screen. It handles the visual overlay and the player input. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/21/1994 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Process(void) +{ +} + + +/*********************************************************************************************** + * OptionsClass::Set_Shuffle -- Controls the play shuffle setting. * + * * + * This routine will control the score shuffle flag. The setting to use is provided as * + * a parameter. When shuffling is on, the score play order is scrambled. * + * * + * INPUT: on -- Should the shuffle option be activated? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Shuffle(int on) +{ + IsScoreShuffle = on; +} + + +/*********************************************************************************************** + * OptionsClass::Set_Repeat -- Controls the score repeat option. * + * * + * This routine is used to control whether scores repeat or not. The setting to use for * + * the repeat flag is provided as a parameter. * + * * + * INPUT: on -- Should the scores repeat? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Repeat(int on) +{ + IsScoreRepeat = on; +} + + +/*********************************************************************************************** + * OptionsClass::Set_Score_Volume -- Sets the global score volume to that specified. * + * * + * This routine will set the global score volume to the value specified. The value ranges * + * from zero to 255. * + * * + * INPUT: volume -- The new volume setting to use for scores. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Score_Volume(int volume) +{ + volume = Bound(volume, 0, 255); + ScoreVolume = volume; + Set_Score_Vol(ScoreVolume); +} + + +/*********************************************************************************************** + * OptionsClass::Set_Sound_Volume -- Sets the sound effects volume level. * + * * + * This routine will set the sound effect volume level as indicated. It can generate a * + * sound effect for feedback purposes if desired. The volume setting can range from zero * + * to 255. The value of 255 is the loudest. * + * * + * INPUT: volume -- The volume setting to use for the new value. 0 to 255. * + * * + * feedback -- Should a feedback sound effect be generated? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Sound_Volume(int volume, int feedback) +{ + volume = Bound(volume, 0, 255); + Volume = volume; + if (feedback) { + Sound_Effect(VOC_BLEEPY3, NULL); + } +} + + +/*********************************************************************************************** + * OptionsClass::Set_Brightness -- Sets the brightness level to that specified. * + * * + * This routine will set the current brightness level to the value specified. This value * + * can range from zero to 255, with 128 being the normal (default) brightness level. * + * * + * INPUT: brightness -- The brightness level to set as current. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Brightness(int brightness) +{ + Brightness = 0x40 + Fixed_To_Cardinal(0x80, brightness); + Adjust_Palette(OriginalPalette, GamePalette, Brightness, Color, Tint, Contrast); + if (InMainLoop){ + Set_Palette(GamePalette); + } +} + + +/*********************************************************************************************** + * OptionsClass::Get_Brightness -- Fetches the current brightness setting. * + * * + * This routine will fetch the current setting for the brightness level. The value ranges * + * from zero to 255, with 128 being the normal (default) value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current brightness setting. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +int OptionsClass::Get_Brightness(void) const +{ + return(Cardinal_To_Fixed(0x80, Brightness-0x40)); +} + + +/*********************************************************************************************** + * OptionsClass::Set_Color -- Sets the color to the value specified. * + * * + * This routine will set the color value to that specified. The value specified can range * + * from zero to 255. The value of 128 is the normal default color setting. * + * * + * INPUT: color -- The new color value to set as current. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Color(int color) +{ + Color = color; + Adjust_Palette(OriginalPalette, GamePalette, Brightness, Color, Tint, Contrast); + if (InMainLoop){ + Set_Palette(GamePalette); + } +} + + +/*********************************************************************************************** + * OptionsClass::Get_Color -- Fetches the current color setting. * + * * + * This routine will fetch the current color setting. This value ranges from zero to * + * 255. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current color setting. The value of 128 is the normal (default) * + * color setting. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +int OptionsClass::Get_Color(void) const +{ + return(Color); +} + + +/*********************************************************************************************** + * OptionsClass::Set_Contrast -- Sets the contrast to the value specified. * + * * + * This routine will set the constrast to the setting specified. This setting ranges from * + * zero to 255. The value o 128 is the normal default value. * + * * + * INPUT: contrast -- The constrast setting to make as the current setting. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Contrast(int contrast) +{ + Contrast = 0x40 + Fixed_To_Cardinal(0x80, contrast); + Adjust_Palette(OriginalPalette, GamePalette, Brightness, Color, Tint, Contrast); + if (InMainLoop){ + Set_Palette(GamePalette); + } +} + + +/*********************************************************************************************** + * OptionsClass::Get_Contrast -- Gets the current contrast setting. * + * * + * This routine will get the current contrast setting. The value returned is in the range * + * of zero to 255. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the current contrast setting. A setting of 128 is the normal default value.* + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +int OptionsClass::Get_Contrast(void) const +{ + return(Cardinal_To_Fixed(0x80, Contrast-0x40)); +} + + +/*********************************************************************************************** + * OptionsClass::Set_Tint -- Sets the tint setting. * + * * + * This routine will change the current tint setting according to the value specified. * + * * + * INPUT: tint -- The desired tint setting. This value ranges from zero to 255. * + * * + * OUTPUT: none * + * * + * WARNINGS: The value of 128 is the default (normal) tint setting. * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Tint(int tint) +{ + Tint = tint; + Adjust_Palette(OriginalPalette, GamePalette, Brightness, Color, Tint, Contrast); + if (InMainLoop){ + Set_Palette(GamePalette); + } +} + + +/*********************************************************************************************** + * OptionsClass::Get_Tint -- Fetches the current tint setting. * + * * + * This fetches the current tint setting. The value is returned as a number between * + * zero and 255. This has been adjusted for the valid range allowed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current tint setting. Normal tint setting is 128. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +int OptionsClass::Get_Tint(void) const +{ + return(Tint); +} + + +/*********************************************************************************************** + * OptionsClass::Adjust_Palette -- Adjusts the palette according to the settings specified. * + * * + * This routine is used to adjust the palette according to the settings provided. It is * + * used by the options class to monkey with the palette. * + * * + * INPUT: oldpal -- Pointer to the original (unmodified) palette. * + * * + * newpal -- The new palette to create according to the settings provided. * + * * + * brightness -- The brightness level (0..255). * + * * + * color -- The color level (0..255). * + * * + * tint -- The tint (hue) level (0..255). * + * * + * contrast -- The contrast level (0..255). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/21/1994 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Adjust_Palette(void *oldpal, void *newpal, unsigned char brightness, unsigned char color, unsigned char tint, unsigned char contrast) const +{ + int index; + unsigned h,s,v; + unsigned r,g,b; + + if (!oldpal || !newpal) return; + + /* + ** Adjust for palette. + */ + for (index = 0; index < 256; index++) { + if (/*index == LTGREEN ||*/ index == 255) { + memcpy(&((char*)newpal)[index*3], &((char*)oldpal)[index*3], 3); + } else { + r = ((char*)oldpal)[(index*3)+0]; + g = ((char*)oldpal)[(index*3)+1]; + b = ((char*)oldpal)[(index*3)+2]; + Convert_RGB_To_HSV(r, g, b, &h, &s, &v); + + /* + ** Adjust contrast by moving the value toward the center according to the + ** percentage indicated. + */ + int temp; + + temp = (v * brightness) / 0x80; // Brightness + temp = Bound(temp, 0, 0xFF); + v = temp; + temp = (((((int)v) - 0x80) * contrast) / 0x80) + 0x80; // Contrast + temp = Bound(temp, 0, 0xFF); + v = temp; + temp = (s * color) / 0x80; // Color + temp = Bound(temp, 0, 0xFF); + s = temp; + temp = (h * tint) / 0x80; // Tint + temp = Bound(temp, 0, 0xFF); + h = temp; + Convert_HSV_To_RGB(h, s, v, &r, &g, &b); + ((char*)newpal)[(index*3)+0] = r; + ((char*)newpal)[(index*3)+1] = g; + ((char*)newpal)[(index*3)+2] = b; + } + } +} + + +/*********************************************************************************************** + * OptionsClass::Load_Settings -- reads options settings from the INI file * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +void OptionsClass::Load_Settings (void) +{ + char *buffer; // INI staging buffer pointer. + + /* + ** Fetch working pointer to the INI staging buffer. Make sure that the buffer + ** is cleared out before proceeding. (Don't use the HidPage for this, since + ** the HidPage may be needed for various uncompressions during the INI + ** parsing.) + */ + buffer = (char *)_ShapeBuffer; + memset(buffer, '\0', _ShapeBufferSize); + + /* + ** Create filename and read the file. + */ + CCFileClass file ("CONQUER.INI"); + if (!file.Is_Available()) { + return; + } else { + file.Read(buffer, _ShapeBufferSize-1); + } + file.Close(); + + /* + ** Read in the Options values + */ + GameSpeed = WWGetPrivateProfileInt("Options", "GameSpeed", 4, buffer); + ScrollRate = WWGetPrivateProfileInt("Options", "ScrollRate", 4, buffer); + Set_Brightness(WWGetPrivateProfileInt("Options", "Brightness", 0x80, buffer)); + Set_Sound_Volume(WWGetPrivateProfileInt("Options", "Volume", 0xA0, buffer),false); + Set_Score_Volume(WWGetPrivateProfileInt("Options", "ScoreVolume", 0xFF, buffer)); + Set_Contrast(WWGetPrivateProfileInt("Options", "Contrast", 0x80, buffer)); + Set_Color(WWGetPrivateProfileInt("Options", "Color", 0x80, buffer)); + Set_Tint(WWGetPrivateProfileInt("Options", "Tint", 0x80, buffer)); + AutoScroll = WWGetPrivateProfileInt("Options", "AutoScroll", 1, buffer); + Set_Repeat(WWGetPrivateProfileInt("Options", "IsScoreRepeat", 0, buffer)); + Set_Shuffle(WWGetPrivateProfileInt("Options", "IsScoreShuffle", 0, buffer)); + IsDeathAnnounce = WWGetPrivateProfileInt("Options", "DeathAnnounce", 0, buffer); + IsFreeScroll = WWGetPrivateProfileInt("Options", "FreeScrolling", 0, buffer); + SlowPalette = WWGetPrivateProfileInt("Options", "SlowPalette", 1, buffer); + + char workbuf[128]; + + /* + ** Check for and possible enable true object names. + */ + WWGetPrivateProfileString("Options", "TrueNames", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_TRUENAME) { + Special.IsNamed = true; + } + + /* + ** Enable 6 player games if special flag is detected. + */ + WWGetPrivateProfileString("Options", "Players", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_6PLAYER) { + MPlayerMax = 6; + } + + /* + ** Enable three point turning logic as indicated. + */ + WWGetPrivateProfileString("Options", "Rotation", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_3POINT) { + Special.IsThreePoint = true; + } + + /* + ** Allow purchase of the helipad separately from the helicopter. + */ + WWGetPrivateProfileString("Options", "Helipad", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_HELIPAD) { + Special.IsSeparate = true; + } + + /* + ** Allow the MCV to undeploy rather than sell. + */ + WWGetPrivateProfileString("Options", "MCV", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_MCV) { + Special.IsMCVDeploy = true; + } + + /* + ** Allow disabling of building bibs so that tigher building packing can occur. + */ + WWGetPrivateProfileString("Options", "Bibs", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_BIB) { + Special.IsRoad = true; + } + + /* + ** Allow targeting of trees without having to hold down the shift key. + */ + WWGetPrivateProfileString("Options", "TreeTarget", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_TREETARGET) { + Special.IsTreeTarget = true; + } + + /* + ** Allow infantry to fire while moving. Attacker gets advantage with this flag. + */ + WWGetPrivateProfileString("Options", "Combat", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_COMBAT) { + Special.IsDefenderAdvantage = false; + } + + /* + ** Allow custom scores. + */ + WWGetPrivateProfileString("Options", "Scores", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_SCORE) { + Special.IsVariation = true; + } + + /* + ** Smarter self defense logic. Tanks will try to run over adjacent infantry. Buildings + ** will automatically return fire if they are fired upon. Infantry will run from an + ** incoming explosive (grenade or napalm) or damage that can't be directly addressed. + */ + WWGetPrivateProfileString("Options", "CombatIQ", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_IQ) { + Special.IsSmartDefense = true; + Special.IsScatter = true; + } + + /* + ** Enable the infantry squish marks when run over by a vehicle. + */ + WWGetPrivateProfileString("Options", "Overrun", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_SQUISH) { + Special.IsGross = true; + } + + /* + ** Enable the human generated sound effects. + */ + WWGetPrivateProfileString("Options", "Sounds", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_HUMAN) { + Special.IsJuvenile = true; + } + + /* + ** Scrolling is disabled over the tabs with this option. + */ + WWGetPrivateProfileString("Options", "Scrolling", "", workbuf, sizeof(workbuf), buffer); + if (Obfuscate(workbuf) == PARM_SCROLLING) { + Special.IsScrollMod = true; + } +} + + +/*********************************************************************************************** + * OptionsClass::Save_Settings -- writes options settings to the INI file * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +void OptionsClass::Save_Settings (void) +{ + char * buffer; // INI staging buffer pointer. + CCFileClass file; + + /* + ** Get a working pointer to the INI staging buffer. Make sure that the buffer + ** starts cleared out of any data. + */ + buffer = (char *)_ShapeBuffer; + memset(buffer, '\0', _ShapeBufferSize); + + file.Set_Name("CONQUER.INI"); + if (file.Is_Available()) { + file.Read(buffer, _ShapeBufferSize-1); + } + + /* + ** Save Options settings + */ + WWWritePrivateProfileInt("Options", "GameSpeed", GameSpeed, buffer); + WWWritePrivateProfileInt("Options", "ScrollRate", ScrollRate, buffer); + WWWritePrivateProfileInt("Options", "Brightness", Brightness, buffer); + WWWritePrivateProfileInt("Options", "Volume", Volume, buffer); + WWWritePrivateProfileInt("Options", "ScoreVolume", ScoreVolume, buffer); + WWWritePrivateProfileInt("Options", "Contrast", Contrast, buffer); + WWWritePrivateProfileInt("Options", "Color", Color, buffer); + WWWritePrivateProfileInt("Options", "Tint", Tint, buffer); + WWWritePrivateProfileInt("Options", "AutoScroll", AutoScroll, buffer); + WWWritePrivateProfileInt("Options", "IsScoreRepeat", IsScoreRepeat, buffer); + WWWritePrivateProfileInt("Options", "IsScoreShuffle", IsScoreShuffle, buffer); + WWWritePrivateProfileInt("Options", "DeathAnnounce", IsDeathAnnounce, buffer); + WWWritePrivateProfileInt("Options", "FreeScrolling", IsFreeScroll, buffer); + + /* + ** Write the INI data out to a file. + */ + file.Write(buffer,strlen(buffer)); +} + + +/*********************************************************************************************** + * OptionsClass::Set -- Sets options based on current settings * + * * + * Use this routine to adjust the palette or sound settings after a fresh scenario load. * + * It assumes the values needed are already loaded into OptionsClass. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/24/1995 BRR : Created. * + *=============================================================================================*/ +void OptionsClass::Set(void) +{ + Set_Brightness(Brightness); + Set_Contrast(Contrast); + Set_Color(Color); + Set_Tint(Tint); + Set_Sound_Volume(Volume,false); + Set_Score_Volume(ScoreVolume); + Set_Repeat(IsScoreRepeat); + Set_Shuffle(IsScoreShuffle); +} + + +/*********************************************************************************************** + * OptionsClass::Normalize_Delay -- Normalizes delay factor to keep rate constant. * + * * + * This routine is used to adjust delay factors that MUST be synchronized on all machines * + * but should maintain a speed as close to constant as possible. Building animations are * + * a good example of this. * + * * + * INPUT: delay -- The normal delay factor. * + * * + * OUTPUT: Returns with the delay to use that has been modified so that a reasonably constant * + * rate will result. * + * * + * WARNINGS: This calculation is crude due to the coarse resolution that a 1/15 second timer * + * allows. * + * * + * Use of this routine ASSUMES that the GameSpeed is synchronized on all machines. * + * * + * HISTORY: * + * 06/18/1995 JLB : Created. * + * 06/30/1995 JLB : Handles low values in a more consistent manner. * + *=============================================================================================*/ +int OptionsClass::Normalize_Delay(int delay) const +{ + static int _adjust[][8] = { + {2,2,1,1,1,1,1,1}, + {3,3,3,2,2,2,1,1}, + {5,4,4,3,3,2,2,1}, + {7,6,5,4,4,4,3,2} + }; + if (delay) { + if (delay < 5) { + delay = _adjust[delay-1][GameSpeed]; + } else { + delay = ((delay * 8) / (GameSpeed+1)); + } + } + return(delay); +} + + + +void OptionsClass::Fixup_Palette(void) const +{ + Adjust_Palette(OriginalPalette, GamePalette, Brightness, Color, Tint, Contrast); +} + + +int OptionsClass::Normalize_Sound(int volume) const +{ + return(Fixed_To_Cardinal(volume, Volume)); +} diff --git a/OPTIONS.H b/OPTIONS.H new file mode 100644 index 0000000..b363965 --- /dev/null +++ b/OPTIONS.H @@ -0,0 +1,104 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\options.h_v 2.18 16 Oct 1995 16:46:20 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : June 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef OPTIONS_H +#define OPTIONS_H + +class OptionsClass { + public: + enum { + MAX_SCROLL_SETTING=7, + MAX_SPEED_SETTING=7 + }; + + OptionsClass(void); + + void One_Time(void); + void Process(void); + + void Fixup_Palette(void) const; + void Set_Shuffle(int on); + void Set_Repeat(int on); + void Set_Score_Volume(int volume); + void Set_Sound_Volume(int volume, int feedback); + void Set_Brightness(int brightness); + int Get_Brightness(void) const; + void Set_Color(int color); + int Get_Color(void) const; + void Set_Contrast(int contrast); + int Get_Contrast(void) const; + void Set_Tint(int tint); + int Get_Tint(void) const; + int Normalize_Delay(int delay) const; + int Normalize_Sound(int volume) const; + + /* + ** File I/O routines + */ + void Load_Settings(void); + void Save_Settings(void); + + void Set(void); + + /* + ** This is actually the delay between game frames expressed as 1/60 of + ** a second. The default value is 4 (1/15 second). + */ + unsigned int GameSpeed; + + int ScrollRate; // Distance to scroll. + unsigned char Brightness; + unsigned char Volume; // Volume for sound effects. + unsigned char ScoreVolume; // Volume for scores. + unsigned char Contrast; // Value + unsigned char Color; // Saturation + unsigned char Tint; // Hue + unsigned AutoScroll:1; // Does map autoscroll? + unsigned IsScoreRepeat:1; // Score should repeat? + unsigned IsScoreShuffle:1; // Score list should shuffle? + unsigned IsDeathAnnounce:1;// Announce enemy deaths? + unsigned IsFreeScroll:1; // Allow free direction scrolling? + + protected: + + void Adjust_Palette(void *oldpal, void *newpal, unsigned char brightness, unsigned char color, unsigned char tint, unsigned char contrast) const; + + private: +}; + + +#endif diff --git a/OPTIONS.LNT b/OPTIONS.LNT new file mode 100644 index 0000000..46e6057 --- /dev/null +++ b/OPTIONS.LNT @@ -0,0 +1,26 @@ +// Please note -- this is a representative set of error suppression +// options. Please adjust to suit your own policies +// See PC-lint for C/C++ manual (chapter LIVING WITH LINT) +// for further details. + +-e502 -e713 -e737 -eau // don't report on signed/unsigned mismatches +-e734 // allow sub-integer loss of information +-e701 -e703 // shifting int left is OK +-e537 // don't care about repeated include file +-e641 // converting enum to int is ok +-e1042 // operator ++/-- don't need class parameters +-e963 -e763 // redundant declarations are ok +-e1712 // no default constructor defined is ok +-e1704 // private constructors are ok +-e534 // ignoring return value is ok +-e732 // going from signed to unsigned parameter is ok +-e1411 // functions hiding base functions is ok +-e788 // switch with default doesn't need all values specified +-e655 -e656 // compatable enum bit and arithmetic operations are ok +-e1542 // members possibly not initialized isn't a valid warning +-e522 // calling 'new' without assignment isn't always an error + + +-e1401 // uninitialized by constructor warning disabled. + + diff --git a/OVERLAY.CPP b/OVERLAY.CPP new file mode 100644 index 0000000..5d5dc30 --- /dev/null +++ b/OVERLAY.CPP @@ -0,0 +1,429 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\overlay.cpv 2.17 16 Oct 1995 16:50:44 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OVERLAY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 17, 1994 * + * * + * Last Update : July 24, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * OverlayClass::Read_INI -- Reads the overlay data from an INI file. * + * OverlayClass::Write_INI -- Writes the overlay data to an INI file. * + * OverlayClass::delete -- Returns a overlay object to the pool. * + * OverlayClass::Init -- Resets the overlay object system. * + * OverlayClass::new -- Allocates a overlay object from pool * + * OverlayClass::OverlayClass -- Overlay object constructor. * + * OverlayClass::Mark -- Marks the overlay down on the map. * + * OverlayClass::Validate -- validates overlay * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "overlay.h" + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * OverlayClass::VTable; + +HousesType OverlayClass::ToOwn = HOUSE_NONE; + +OverlayClass::OverlayClass(void) : Class(0) {ToOwn = HOUSE_NONE;}; + + +/*********************************************************************************************** + * OverlayClass::Validate -- validates overlay * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int OverlayClass::Validate(void) const +{ + int num; + + num = Overlays.ID(this); + if (num < 0 || num >= OVERLAY_MAX) { + Validate_Error("OVERLAY"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * OverlayClass::Init -- Resets the overlay object system. * + * * + * This routine resets the overlay object system. It is called * + * prior to loading a new scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayClass::Init(void) +{ + OverlayClass *ptr; + + Overlays.Free_All(); + + ptr = new OverlayClass(); + VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0]; + delete ptr; +} + + +/*********************************************************************************************** + * OverlayClass::new -- Allocates a overlay object from pool * + * * + * This routine is used to allocate a overlay object from the * + * overlay object pool. * + * * + * INPUT: size -- The size of a overlay object (not used). * + * * + * OUTPUT: Returns with a pointer to an available overlay object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +void * OverlayClass::operator new(size_t ) +{ + void * ptr = Overlays.Allocate(); + if (ptr) { + ((OverlayClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * OverlayClass::delete -- Returns a overlay object to the pool. * + * * + * This routine will return a overlay object to the overlay object * + * pool. A overlay so returned is available for allocation again. * + * * + * INPUT: ptr -- Pointer to the object to be returned. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayClass::operator delete(void *ptr) +{ + if (ptr) { + ((OverlayClass *)ptr)->IsActive = false; + } + Overlays.Free((OverlayClass *)ptr); +} + + +/*********************************************************************************************** + * OverlayClass::OverlayClass -- Overlay object constructor. * + * * + * This is the constructor for a overlay object. * + * * + * INPUT: type -- The overlay object this is to become. * + * * + * pos -- The position on the map to place the object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +OverlayClass::OverlayClass(OverlayType type, CELL pos, HousesType house) : + Class(&OverlayTypeClass::As_Reference(type)) +{ + if (pos != -1) { + ToOwn = house; + Unlimbo(Cell_Coord(pos)); + ToOwn = HOUSE_NONE; + } +} + + +/*********************************************************************************************** + * OverlayClass::Mark -- Marks the overlay down on the map. * + * * + * This routine will place the overlay onto the map. The overlay object is deleted by this * + * operation. The map is updated to reflect the presence of the overlay. * + * * + * INPUT: mark -- The type of marking to perform. Only MARK_DOWN is supported. * + * * + * OUTPUT: bool; Was the overlay successfully marked? Failure occurs if it is not being * + * marked down. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 12/23/1994 JLB : Checks low level legality before proceeding. * + *=============================================================================================*/ +bool OverlayClass::Mark(MarkType mark) +{ + Validate(); + if (ObjectClass::Mark(mark)) { + if (mark == MARK_DOWN) { + CELL cell = Coord_Cell(Coord); + CellClass * cellptr = &Map[cell]; + + /* + ** Road placement occurs in two steps. First the foundation is placed, but only + ** on buildable terrain. Second, the road is completed, but only if the foundation + ** was previously placed. + */ + if (*this == OVERLAY_ROAD) { + if ((cellptr->Overlay == OVERLAY_ROAD && cellptr->OverlayData == 0) || + (cellptr->Overlay == OVERLAY_NONE && cellptr->Is_Generally_Clear())) { + + if (cellptr->Overlay == OVERLAY_ROAD) { + cellptr->OverlayData = 1; + } else { + cellptr->OverlayData = 0; + } + cellptr->Overlay = Class->Type; + cellptr->Redraw_Objects(); + } + } else { + + /* + ** Walls have special logic when they are marked down. + */ + if (Class->IsWall) { + if (cellptr->Is_Generally_Clear() && cellptr->Overlay != OVERLAY_FLAG_SPOT) { + cellptr->Overlay = Class->Type; + cellptr->OverlayData = 0; + cellptr->Redraw_Objects(); + cellptr->Wall_Update(); + + /* + ** Flag ownership of the cell if the 'global' ownership flag indicates that this + ** is necessary for the overlay. + */ + if (ToOwn != HOUSE_NONE) { + cellptr->Owner = ToOwn; + } + + } else { + delete this; + return(false); + } + } else { + if ((cellptr->Overlay == OVERLAY_NONE || cellptr->Overlay == OVERLAY_SQUISH) && !cellptr->Cell_Terrain() && Ground[cellptr->Land_Type()].Build) { + + /* + ** Increment the global crate counter. This is used to regulate + ** the crate generation. + */ + if (Class->IsCrate) CrateCount++; + + /* + ** Don't show the squish unless the gross flag is active. + */ + if (!Special.IsGross && Class->Type != OVERLAY_SQUISH) { + cellptr->Overlay = Class->Type; + cellptr->OverlayData = 0; + } + cellptr->Redraw_Objects(); + if (Class->Land == LAND_TIBERIUM) { + cellptr->OverlayData = 1; + cellptr->Tiberium_Adjust(); + } else { + if (*this == OVERLAY_CONCRETE) { + CELL newcell; + + /* + ** Smudges go away when concrete is laid down. + */ + cellptr->Smudge = SMUDGE_NONE; + cellptr->SmudgeData = 0; + cellptr->Concrete_Calc(); + + /* + ** Possibly add concrete to adjacent cells depending on whether this + ** concrete is in an odd or even row. + */ + if (Cell_X(cell) & 0x01) { + newcell = Adjacent_Cell((CELL)(cellptr->Cell_Number()), FACING_W); + } else { + newcell = Adjacent_Cell((CELL)(cellptr->Cell_Number()), FACING_E); + } + if (Map[newcell].Overlay != OVERLAY_CONCRETE) { + Class->Create_And_Place(newcell); + } + + /* + ** The display attributes must be recalculated for all adjacent + ** cells since their shape can be altered by the presence of + ** concrete at this location. + */ + static FacingType _face[4] = {FACING_N, FACING_E, FACING_S, FACING_W}; + + for (int index = 0; index < (sizeof(_face)/sizeof(_face[0])); index++) { + cellptr->Adjacent_Cell(_face[index]).Concrete_Calc(); + } + } + } + } + } + + /* + ** ***** Is this really needed? + */ + cellptr->Recalc_Attributes(); + } + delete this; + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * OverlayClass::Read_INI -- Reads the overlay data from an INI file. * + * * + * This routine is used to load a scenario's overlay data. The overlay objects are read * + * from the INI file and then created on the map. * + * * + * INPUT: buffer -- Pointer to the INI file staging buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + * 07/24/1995 JLB : Specifically forbid manual crates in multiplayer scenarios. * + *=============================================================================================*/ +void OverlayClass::Read_INI(char *buffer) +{ + char *tbuffer; + int len; // Length of data in buffer. + char buf[128]; + + len = strlen(buffer) + 2; + tbuffer = buffer + len; + + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); + while (*tbuffer != '\0') { + CELL cell; + OverlayType classid; + + cell = atoi(tbuffer); + WWGetPrivateProfileString(INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, buffer); + classid = OverlayTypeClass::From_Name(strtok(buf, ",\n\r")); + + /* + ** Don't allow placement of crates in the multiplayer scenarios. + */ + if (classid != OVERLAY_NONE && (GameToPlay == GAME_NORMAL || !OverlayTypeClass::As_Reference(classid).IsCrate)) { + + /* + ** Don't allow placement of overlays on the top or bottom rows of + ** the map. + */ + if (cell >= MAP_CELL_W && cell <= MAP_CELL_TOTAL - MAP_CELL_W) { + new OverlayClass(classid, cell); + } + } + tbuffer += strlen(tbuffer)+1; + } +} + + +/*********************************************************************************************** + * OverlayClass::Write_INI -- Writes the overlay data to an INI file. * + * * + * This is used to output the overlay data to a scenario INI file. Typically, this is * + * only used by the scenario editor. * + * * + * INPUT: buffer -- Pointer to the INI file staging buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayClass::Write_INI(char *buffer) +{ + int index; + char uname[10]; + char buf[128]; + char *tbuffer; // Accumulation buffer of unit IDs. + + /* + ** First, clear out all existing unit data from the ini file. + */ + tbuffer = buffer + strlen(buffer) + 2; + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-strlen(buffer), buffer); + while (*tbuffer != '\0') { + WWWritePrivateProfileString(INI_Name(), tbuffer, NULL, buffer); + tbuffer += strlen(tbuffer)+1; + } + + /* + ** Write the unit data out. + */ + for (index = 0; index < MAP_CELL_TOTAL; index++) { + CellClass * cellptr = &Map[index]; + + if (cellptr->Overlay != OVERLAY_NONE) { + sprintf(uname, "%03d", index); + sprintf(buf, "%s", OverlayTypeClass::As_Reference(cellptr->Overlay).IniName); + WWWritePrivateProfileString(INI_Name(), uname, buf, buffer); + } + } +} diff --git a/OVERLAY.H b/OVERLAY.H new file mode 100644 index 0000000..657b536 --- /dev/null +++ b/OVERLAY.H @@ -0,0 +1,107 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\overlay.h_v 2.16 16 Oct 1995 16:44:50 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OVERLAY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 17, 1994 * + * * + * Last Update : May 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef OVERLAY_H +#define OVERLAY_H + +#include "object.h" +#include "type.h" + +/****************************************************************************** +** This class controls the overlay object. Overlay objects function congruously +** to carpet on a floor. They have no depth, but merely control the icon to be rendered +** as the cell's bottom most layer. +*/ +class OverlayClass : public ObjectClass +{ + public: + /*------------------------------------------------------------------- + ** Constructors and destructors. + */ + static void * operator new(size_t size); + static void operator delete(void *ptr); + OverlayClass(void); + OverlayClass(OverlayType type, CELL pos=-1, HousesType = HOUSE_NONE); + virtual ~OverlayClass(void) {if (GameActive) OverlayClass::Limbo();}; + operator OverlayType(void) const {return Class->Type;}; + virtual RTTIType What_Am_I(void) const {return RTTI_OVERLAY;}; + + static void Init(void); + + /* + ** File I/O. + */ + static void Read_INI(char *); + static void Write_INI(char *); + static char *INI_Name(void) {return "OVERLAY";}; + bool Load(FileClass & file); + bool Save(FileClass & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Virtual support functionality. + */ + virtual bool Mark(MarkType); + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual void Draw_It(int , int , WindowNumberType ) {}; + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + private: + /* + ** This is used to control the marking process of the overlay. If this is + ** set to a valid house number, then the cell that the overlay is marked down + ** upon will be flagged as being owned by the specified house. + */ + static HousesType ToOwn; + + /* + ** This is a pointer to the overlay object's class. + */ + OverlayTypeClass const * const Class; + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; +}; + +#endif diff --git a/PACKET.CPP b/PACKET.CPP new file mode 100644 index 0000000..001e85d --- /dev/null +++ b/PACKET.CPP @@ -0,0 +1,463 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*************************************************************************** + * * + * Project Name : Westwood Auto Registration App * + * * + * File Name : PACKET.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 04/22/96 * + * * + * Last Update : April 24, 1996 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * *PacketClass::Find_Field -- Finds a field if it exists in the packets * + * Get_Field -- Find specified name and returns data * + * PacketClass::~PacketClass -- destroys a packet class be freeing list * + * PacketClass::Add_Field -- Adds a FieldClass entry to head of packet li* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include +#include +#include + +enum {false=0,true=1}; +typedef int bool; + +#include "packet.h" + + +/************************************************************************** + * PACKETCLASS::~PACKETCLASS -- destroys a packet class be freeing list * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 04/24/1996 PWG : Created. * + *========================================================================*/ +PacketClass::~PacketClass(void) +{ + FieldClass *current; + FieldClass *next; + // + // Loop through the entire field list and delete each entry. + // + for (current = Head; current; current = next) { + next = current->Next; + delete current; + } +} + + +/************************************************************************** + * PACKETCLASS::ADD_FIELD -- Adds a FieldClass entry to head of packet li * + * * + * INPUT: FieldClass * - a properly constructed field class entry. * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 04/24/1996 PWG : Created. * + *========================================================================*/ +void PacketClass::Add_Field(FieldClass *field) +{ + field->Next = Head; + Head = field; +} + +/************************************************************************** + * PACKETCLASS::PACKETCLASS -- Creates a Packet object from a COMMS packe * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/22/1996 PWG : Created. * + *========================================================================*/ +PacketClass::PacketClass(char *curbuf) +{ + int remaining_size; + // + // Pull the size and packet ID out of the linear packet stream. + // + Size = *(unsigned short *)curbuf; + curbuf += sizeof (unsigned short); + Size = ntohs(Size); + ID = *(short *)curbuf; + curbuf += sizeof (short); + ID = ntohs(ID); + Head = NULL; + + // + // Calculate the remaining size so that we can loop through the + // packets and extract them. + // + remaining_size = Size - 4; + + // + // Loop through the linear packet until we run out of room and + // create a field for each. + // + while (remaining_size > 0) { + FieldClass *field = new FieldClass; + + // + // Copy the adjusted header into the buffer and then advance the buffer + // + memcpy(field, curbuf, FIELD_HEADER_SIZE); + curbuf += FIELD_HEADER_SIZE; + remaining_size -= FIELD_HEADER_SIZE; + + // + // Copy the data into the buffer + // + int size = ntohs(field->Size); + field->Data = new char[size]; + memcpy(field->Data, curbuf, size); + curbuf += size; + remaining_size -= size; + // + // Make sure we allow for the pad bytes. + // + int pad = (4 - (ntohs(field->Size) & 3)) & 3; + curbuf += pad; + remaining_size -= pad; + + // + // Convert the field back to the host format + // + field->Net_To_Host(); + + // + // Finally add the field to the field list in the packet + // structure. + // + Add_Field(field); + } +} + +/************************************************************************** + * CREATE_COMMS_PACKET -- Walks field list creating a packet * + * * + * INPUT: short - the id of the packet so the server can identify it * + * unsigned short & - the size of the packet returned here * + * * + * OUTPUT: void * pointer to the linear packet data * + * * + * WARNINGS: This routine allocates memory that the user is responsible * + * for freeing. * + * * + * HISTORY: * + * 04/22/1996 PWG : Created. * + *========================================================================*/ +char *PacketClass::Create_Comms_Packet(int &size) +{ + FieldClass *current; + + // + // Size starts at four because that is the size of the packet header. + // + size = 4; + + // + // Take a quick spin through and calculate the size of the packet we + // are building. + // + for (current = Head; current; current=current->Next) { + size += (unsigned short)FIELD_HEADER_SIZE; // add in packet header size + size += current->Size; // add in data size + size += (4 - (size & 3)) & 3; // add in pad value to dword align next packet + } + + // + // Now that we know the size allocate a buffer big enough to hold the + // packet. + // + char *retval = new char[size]; + char *curbuf = retval; + + // + // write the size into the packet header + // + *(unsigned short *)curbuf = (unsigned short)htons((unsigned short)size); + curbuf += sizeof (unsigned short); + *(short *)curbuf = htons(ID); + curbuf += sizeof (short); + + // + // Ok now that the actual header information has been written we need to write out + // field information. + // + for (current = Head; current; current = current->Next) { + // + // Temporarily convert the packet to net format (this saves alot of + // effort, and seems safe...) + // + current->Host_To_Net(); + + // + // Copy the adjusted header into the buffer and then advance the buffer + // + memcpy(curbuf, current, FIELD_HEADER_SIZE); + curbuf += FIELD_HEADER_SIZE; + + // + // Copy the data into the buffer and then advance the buffer + // + memcpy(curbuf, current->Data, ntohs(current->Size)); + curbuf += ntohs(current->Size); + + // + // Finally take care of any pad bytes by setting them to 0 + // + int pad = (4 - (ntohs(current->Size) & 3)) & 3; + + // + // If there is any pad left over, make sure you memset it + // to zeros, so it looks like a pad. + // + if (pad) { + memset(curbuf, 0, pad); + curbuf += pad; + } + + current->Net_To_Host(); + } + return(retval); +} + + +/************************************************************************** + * PACKETCLASS::FIND_FIELD -- Finds a field if it exists in the packets * + * * + * INPUT: char * - the id of the field we are looking for. * + * * + * OUTPUT: FieldClass * pointer to the field class * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +FieldClass *PacketClass::Find_Field(char *id) +{ + for (FieldClass *current = Head; current; current = current->Next) { + if ( strncmp(id, current->ID, 4) == 0) + return current; + } + return NULL; +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * char & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, char &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((char *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * unsigned char & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, unsigned char &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((unsigned char *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * short & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, short &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((short *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * unsigned short & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, unsigned short &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((unsigned short *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * long & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, long &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((long *)field->Data); + } + return((field) ? true : false); +} + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data as a string * + * * + * INPUT: char * - the id of the field that holds the data. * + * char * - the string to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The string is not changed if the field is not found. It * + * is assumed that the string variabled specified by the * + * pointer is large enough to hold the data. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, char *data) +{ + FieldClass *field = Find_Field(id); + if (field) { + strcpy(data, (char *)field->Data); + } + return((field) ? true : false); +} + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * unsigned long & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, unsigned long &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((unsigned long *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * void * - the reference to store the data into * + * int - the length of the buffer passed in * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 6/4/96 4:46PM ST : Created * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, void *data, int &length) +{ + FieldClass *field = Find_Field(id); + if (field) { + memcpy (data, field->Data, min(field->Size, length)); + length = (int) field->Size; + } + return((field) ? true : false); +} diff --git a/PACKET.H b/PACKET.H new file mode 100644 index 0000000..83870de --- /dev/null +++ b/PACKET.H @@ -0,0 +1,101 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*************************************************************************** + * * + * Project Name : Westwood Auto Registration App * + * * + * File Name : PACKET.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 04/19/96 * + * * + * Last Update : April 19, 1996 [PWG] * + * * + * This header defines the functions for the PacketClass. The packet * + * class is used to create a linked list of field entries which can be * + * converted to a linear packet in a COMMS API compatible format. * + * * + * Packets can be created empty and then have fields added to them or can * + * be created from an existing linear packet. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef __PACKET_H +#define __PACKET_H + + +#include "field.h" + +class PacketClass { + public: + PacketClass(short id = 0) + { + Size = 0; + ID = id; + Head = 0; + } + PacketClass(char *cur_buf); + ~PacketClass(void); + + // + // This function allows us to add a field to the start of the list. As the field is just + // a big linked list it makes no difference which end we add a member to. + // + void Add_Field(FieldClass *field); + + // + // These conveniance functions allow us to add a field directly to the list without + // having to worry about newing one first. + // + void Add_Field(char *field, char data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, unsigned char data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, short data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, unsigned short data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, long data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, unsigned long data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, char *data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, void *data, int length) {Add_Field(new FieldClass(field, data, length));}; + + // + // These functions search for a field of a given name in the list and + // return the data via a reference value. + // + FieldClass *Find_Field(char *id); + bool Get_Field(char *id, char &data); + bool Get_Field(char *id, unsigned char &data); + bool Get_Field(char *id, short &data); + bool Get_Field(char *id, unsigned short &data); + bool Get_Field(char *id, long &data); + bool Get_Field(char *id, unsigned long &data); + bool Get_Field(char *id, char *data); + bool Get_Field(char *id, void *data, int &length); + + char *Create_Comms_Packet(int &size); + + private: + unsigned short Size; + short ID; + FieldClass *Head; + FieldClass *Current; +}; + + +#endif diff --git a/PAGFAULT.ASM b/PAGFAULT.ASM new file mode 100644 index 0000000..444c434 --- /dev/null +++ b/PAGFAULT.ASM @@ -0,0 +1,225 @@ +; +; Command & Conquer(tm) +; Copyright 2025 Electronic Arts Inc. +; +; 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 3 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 General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . +; + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Library * +;* * +;* File Name : PAGFAULT.ASM * +;* * +;* Programmer : Julio R Jerez * +;* * +;* Date : April 25,1995 * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +; +; Here are prototypes for the routines defined within this module: +; VOID Install_Page_Fault_Handle (void) ; +; +; ---------------------------------------------------------------- + +IDEAL ; the product runs in ideal mode +P386 ; use 386 real mode instructions +MODEL USE32 FLAT +LOCALS ?? ; ?? is the symbol for a local +WARN ; generate all warnings we can +JUMPS ; optimize jumps if possible + +;--------------------------------------------------------------------------- +; Make some general equates for easy compatability +;--------------------------------------------------------------------------- +DPMI_INTR EQU 31h +PAGE_FAULT equ 0eh +DIVIDE_ERROR equ 00h +RESET_VIDEO_MODE equ -1 + +global C Install_Page_Fault_Handle : NEAR +global C Set_Video_Mode : NEAR +global C Remove_Mouse : NEAR +global C Remove_Keyboard_Interrupt : NEAR +global C Remove_Timer_Interrupt : NEAR + +DATASEG + Old_Page_Fault_handle DF ? + Page_Fault_SS DD ? + Page_Fault_ESP DD ? + + Old_Div_Error_handle DF ? + Div_Error_SS DD ? + Div_Error_ESP DD ? + + +CODESEG +;*************************************************************************** +;* INSTALL_PAGE_FAULT_HANDLE -- Installs new page fault and div Error * +;* handles. * +;* This function will install a new page fault handle and Div Error * +;* so in the event that we have a program crash these handles will * +;* remove all interrupts and then will chain to the default Exception * +;* Handle * +;* * +;* INPUT: none * +;* * +;* * +;* OUTPUT: none * +;* * +;* PROTO: VOID Install_Page_Fault_Handle( void); * +;* * +;* HISTORY: 04/25/96 Created * +;*=========================================================================* + PROC Install_Page_Fault_Handle C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ; Install_Page_Fault_Handle + mov eax,0202h ; get address of exception handle + mov bl,PAGE_FAULT + int DPMI_INTR + jc ??exit ; not action is taken + + ; save addrees of default handle + mov [dword ptr Old_Page_Fault_handle],edx + mov [word ptr Old_Page_Fault_handle+4],cx + + ; redirect default handle to a new Page Fault Handle + mov eax,0203h + mov bl,PAGE_FAULT + mov cx,cs + lea edx,[Page_Fault_Handle] + int DPMI_INTR + + + ; Install_Divide_Error_Handle + mov eax,0202h ; get address of exception handle + mov bl,DIVIDE_ERROR + int DPMI_INTR + jc ??exit ; not action is taken + + ; save addrees of default fault handle + mov [dword ptr Old_Div_Error_handle],edx + mov [word ptr Old_Div_Error_handle+4],cx + + ; redirect default handle to a new Divede Handle + mov eax,0203h + mov bl,DIVIDE_ERROR + mov cx,cs + lea edx,[Divide_Error_Handle] + int DPMI_INTR + + ??exit: + + + ret + ENDP Install_Page_Fault_Handle + + +;*************************************************************************** +;* PAGE_FAULT_HANDLE -- This * +;* * +;* * +;* * +;* HISTORY: 04/25/96 Created * +;*=========================================================================* +PROC Page_Fault_Handle far + + ; preserve used registers + push eax + push ebx + ; save Page Fault satck frame + mov ax,ss + mov [Page_Fault_SS],eax + mov [Page_Fault_ESP],esp + + ; retrieve application original stack frame + mov eax , [ esp + ( 6 + 2 ) * 4 ] + mov ebx , [ esp + ( 7 + 2 ) * 4 ] + mov ss , bx + mov esp , eax + + ; set video mode to standard text mode + push RESET_VIDEO_MODE + call Set_Video_Mode + pop eax + call Remove_Mouse + call Remove_Keyboard_Interrupt + call Remove_Timer_Interrupt + + ; restore Page Fault stack frame + mov eax,[Page_Fault_SS] + mov ss , ax + mov esp, [Page_Fault_ESP] + + ; restore used registers and chain to default Page Fault Handle + pop ebx + pop eax + jmp [fword Old_Page_Fault_handle] + + ENDP Page_Fault_Handle + + + +;*************************************************************************** +;* Divide_Error -- * +;* * +;* * +;* * +;* HISTORY: 04/25/96 Created * +;*=========================================================================* +PROC Divide_Error_Handle far + + ; preserve used registers + push eax + push ebx + ; save Page Fault satck frame + mov ax,ss + mov [Div_Error_SS],eax + mov [Div_Error_ESP],esp + + ; retrieve application original stack frame + mov eax , [ esp + ( 6 + 2 ) * 4 ] + mov ebx , [ esp + ( 7 + 2 ) * 4 ] + mov ss , bx + mov esp , eax + + ; set video mode to standard text mode + push RESET_VIDEO_MODE + call Set_Video_Mode + pop eax + call Remove_Mouse + call Remove_Keyboard_Interrupt + call Remove_Timer_Interrupt + + ; restore Fault stack frame + mov eax,[Div_Error_SS] + mov ss , ax + mov esp, [Div_Error_ESP] + + ; restore used registers and chain to default Page Fault Handle + pop ebx + pop eax + jmp [fword Old_Div_Error_handle] + + ENDP Divide_Error_Handle + + +;*************************************************************************** +;* End of File. * +;*************************************************************************** +END diff --git a/PHONE.H b/PHONE.H new file mode 100644 index 0000000..c5cf5ed --- /dev/null +++ b/PHONE.H @@ -0,0 +1,69 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\phone.h_v 1.9 16 Oct 1995 16:47:58 JOE_BOSTIC $ */ +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : PHONE.H * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 04/28/95 * + * * + * Last Update : April 28, 1995 [BRR] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef PHONE_H +#define PHONE_H + +/* +***************************** Class Declaration ***************************** +*/ +class PhoneEntryClass +{ + public: + enum PhoneEntryEnum { + PHONE_MAX_NAME = 21, + PHONE_MAX_NUM = 21 + }; + + PhoneEntryClass(void) {}; + ~PhoneEntryClass() {}; + + operator == (PhoneEntryClass &obj) + { return (memcmp (Name,obj.Name,strlen(Name))==0);} + operator != (PhoneEntryClass &obj) + { return (memcmp (Name,obj.Name,strlen(Name))!=0);} + operator > (PhoneEntryClass &obj) + { return (memcmp(Name, obj.Name, strlen(Name)) > 0);} + operator < (PhoneEntryClass &obj) + { return (memcmp(Name, obj.Name, strlen(Name)) < 0);} + operator >= (PhoneEntryClass &obj) + { return (memcmp(Name, obj.Name, strlen(Name)) >= 0);} + operator <= (PhoneEntryClass &obj) + { return (memcmp(Name, obj.Name, strlen(Name)) <= 0);} + + SerialSettingsType Settings; + char Name[ PHONE_MAX_NAME ]; // destination person's name + char Number[ PHONE_MAX_NUM ]; // phone # +}; + +#endif diff --git a/POWER.CPP b/POWER.CPP new file mode 100644 index 0000000..3b19570 --- /dev/null +++ b/POWER.CPP @@ -0,0 +1,462 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\power.cpv 2.18 16 Oct 1995 16:52:10 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : POWER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : August 7, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * PowerClass::AI -- Process the power bar logic. * + * PowerClass::Draw_It -- Renders the power bar graphic. * + * PowerClass::Init_Clear -- Clears all the power bar variables. * + * PowerClass::One_Time -- One time processing for the power bar. * + * PowerClass::PowerButtonClass::Action -- Handles the mouse over the power bar area. * + * PowerClass::PowerClass -- Default constructor for the power bar class. * + * PowerClass::Refresh_Cells -- Intercepts the redraw logic to see if sidebar to redraw too. * + * Power_Height -- Given a value figure where it falls on bar * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/* +** Points to the shape to use for the "desired" power level indicator. +*/ +void const * PowerClass::PowerShape; +void const * PowerClass::PowerBarShape; + +PowerClass::PowerButtonClass PowerClass::PowerButton; + + +/*********************************************************************************************** + * PowerClass::PowerClass -- Default constructor for the power bar class. * + * * + * This is the default constructor for the power bar class. It doesn't really do anything. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + *=============================================================================================*/ +PowerClass::PowerClass(void) +{ + IsToRedraw = false; + RecordedDrain = -1; + RecordedPower = -1; + DesiredDrainHeight = 0; + DesiredPowerHeight = 0; + DrainHeight = 0; + PowerHeight = 0; + PowerBounce = 0; + DrainBounce = 0; + DrainDir = 0; + PowerDir = 0; +} + + +/*********************************************************************************************** + * PowerClass::Init_Clear -- Clears all the power bar variables. * + * * + * This routine is called in preparation for the start of a scenario. The power bar is * + * initialized into the null state by this routine. As soon as the scenario starts, the * + * power bar will rise to reflect the actual power output and drain. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/07/1995 JLB : Created. * + *=============================================================================================*/ +void PowerClass::Init_Clear(void) +{ + RadarClass::Init_Clear(); + RecordedDrain = -1; + RecordedPower = -1; + DesiredDrainHeight = 0; + DesiredPowerHeight = 0; + DrainHeight = 0; + PowerHeight = 0; + PowerBounce = 0; + DrainBounce = 0; + DrainDir = 0; + PowerDir = 0; +} + + +/*********************************************************************************************** + * PowerClass::One_Time -- One time processing for the power bar. * + * * + * This routine is for code that truly only needs to be done once per game run. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +void PowerClass::One_Time(void) +{ + RadarClass::One_Time(); + + int factor = Get_Resolution_Factor(); + PowX = SeenBuff.Get_Width() - Map.RadWidth; + PowY = Map.RadY+Map.RadHeight + (13 << factor); + PowWidth = 8 << factor; + PowHeight = SeenBuff.Get_Height() - PowY; + PowLineSpace = 5 << factor; + PowLineWidth = PowWidth - 4; + + PowerButton.X = PowX; + PowerButton.Y = PowY; + PowerButton.Width = PowWidth-1; + PowerButton.Height = PowHeight; + + PowerShape = MixFileClass::Retrieve((factor)? "HPOWER.SHP" :"POWER.SHP"); + PowerBarShape = Hires_Retrieve("PWRBAR.SHP"); +} + + +/*********************************************************************************************** + * PowerClass::Draw_It -- Renders the power bar graphic. * + * * + * This routine will draw the power bar graphic to the LogicPage. * + * * + * INPUT: complete -- Should the power bar be redrawn even if it isn't specifically flagged * + * to do so? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + * 12/27/1994 JLB : Changes power bar color depending on amount of power. * + *=============================================================================================*/ +void PowerClass::Draw_It(bool complete) +{ + static int _modtable[]={ + 0, -1, 0, 1, 0, -1, -2, -1, 0, 1, 2, 1 ,0 + }; + int power_color; + + if (complete || IsToRedraw) { +// PowX = TacPixelX + TacWidth*ICON_PIXEL_W; // X position of upper left corner of power bar. + + if (LogicPage->Lock()){ + + if (Map.IsSidebarActive) { + IsToRedraw = false; + + /* + ** 1st get the height of the filled section of the power bar + */ + int bottom = PowY + PowHeight - 1; + int power_height = (PowerHeight == DesiredPowerHeight) ? PowerHeight + (_modtable[PowerBounce] * PowerDir) : PowerHeight; + int drain_height = (DrainHeight == DesiredDrainHeight) ? DrainHeight + (_modtable[DrainBounce] * DrainDir) : DrainHeight; + power_height = Bound(power_height, 0, PowHeight - 2); + drain_height = Bound(drain_height, 0, PowHeight - 2); + + /* + ** Create a clip region to draw the unfilled section of the bar + */ + WindowList[WINDOW_CUSTOM][WINDOWX] = 0; + WindowList[WINDOW_CUSTOM][WINDOWY] = 0; + WindowList[WINDOW_CUSTOM][WINDOWWIDTH] = SeenBuff.Get_Width(); + WindowList[WINDOW_CUSTOM][WINDOWHEIGHT] = bottom-power_height; + + /* + ** Draw the unfilled section + */ + CC_Draw_Shape(PowerBarShape, 0, PowX, PowY, WINDOW_CUSTOM, SHAPE_WIN_REL); + CC_Draw_Shape(PowerBarShape, 1 ,PowX, PowY+100, WINDOW_CUSTOM, SHAPE_WIN_REL); + + + /* + ** Set up the clip region for the filled section + */ + WindowList[WINDOW_CUSTOM][WINDOWY] = bottom-power_height; + WindowList[WINDOW_CUSTOM][WINDOWHEIGHT] = SeenBuff.Get_Height() - WindowList[WINDOW_CUSTOM][WINDOWY]; + + /* + ** What color is the filled section? + */ + if (power_height) { + power_color = 0; //green + + if (PlayerPtr->Drain > PlayerPtr->Power) { + power_color = 2; + } + if (PlayerPtr->Drain > (PlayerPtr->Power * 2)) { + power_color = 4; + } + + /* + ** Draw the filled section + */ + CC_Draw_Shape(PowerBarShape, 2+power_color, + PowX, + PowY - WindowList[WINDOW_CUSTOM][WINDOWY], + WINDOW_CUSTOM, + SHAPE_WIN_REL); + + CC_Draw_Shape(PowerBarShape, 3+power_color, + PowX, + PowY - WindowList[WINDOW_CUSTOM][WINDOWY] + 100, + WINDOW_CUSTOM, + SHAPE_WIN_REL); + } + + /* + ** Draw the power drain threshold marker. + */ + CC_Draw_Shape(PowerShape, 0, PowX, bottom - drain_height + 1, WINDOW_MAIN, SHAPE_NORMAL); + + } + LogicPage->Unlock(); + } + } + RadarClass::Draw_It(complete); +} + + +/*********************************************************************************************** + * PowerClass::AI -- Process the power bar logic. * + * * + * Use this routine to process the power bar logic. This consists of animation effects. * + * * + * INPUT: input -- The player input value to be consumed or ignored as appropriate. * + * * + * x,y -- Mouse coordinate parameters to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + *=============================================================================================*/ +void PowerClass::AI(KeyNumType &input, int x, int y) +{ +// if (!IsActive) { +// IsActive = true; +// IsToRedraw = true; +// Flag_To_Redraw(false); +// } + + if (Map.IsSidebarActive /*IsActive*/) { + int olddrain = DrainHeight; + int oldpower = PowerHeight; + + + /* + ** If the recorded power value has changed we need to adjust for + ** it. + */ + if (PlayerPtr->Power != RecordedPower) { + DesiredPowerHeight = Power_Height(PlayerPtr->Power); + RecordedPower = PlayerPtr->Power; + PowerBounce = 12; + if (PowerHeight > DesiredPowerHeight) { + PowerDir = -1; + } else if (PowerHeight < DesiredPowerHeight) { + PowerDir = 1; + } else { + PowerBounce = 0; + } + } + + /* + ** If the recorded drain value has changed we need to adjust for + ** it. + */ + if (PlayerPtr->Drain != RecordedDrain) { + DesiredDrainHeight = Power_Height(PlayerPtr->Drain); + RecordedDrain = PlayerPtr->Drain; + DrainBounce = 12; + if (DrainHeight > DesiredDrainHeight) { + DrainDir = -1; + } else if (DrainHeight < DesiredDrainHeight) { + DrainDir = 1; + } else { + DrainBounce = 0; + } + } + + if (DrainBounce && DrainHeight == DesiredDrainHeight) { + IsToRedraw = true; + Flag_To_Redraw(false); + DrainBounce--; + } else { + /* + ** If we need to move the drain height then do so. + */ + if (DrainHeight != DesiredDrainHeight) { + DrainHeight += DrainDir; + } + } + + if (PowerBounce && PowerHeight == DesiredPowerHeight) { + IsToRedraw = true; + Flag_To_Redraw(false); + PowerBounce--; + } else { + /* + ** If we need to move the power height then do so. + */ + if (PowerHeight != DesiredPowerHeight) { + PowerHeight += PowerDir; + } + } + + if (olddrain != DrainHeight || oldpower != PowerHeight) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + } + RadarClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * PowerClass::Refresh_Cells -- Intercepts the redraw logic to see if sidebar to redraw too. * + * * + * This routine will examine a refresh list request and determine if the sidebar would be * + * affect. If so, it will flag the sidebar to be redrawn. * + * * + * INPUT: cell -- The cell that the offset list is base on. * + * * + * list -- The list of cell offset used to flag for redraw. If the special sidebar * + * affecting cell magic offset number is detected, the sidebar is flagged * + * for redraw and the magic offset is removed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1995 JLB : Created. * + *=============================================================================================*/ +void PowerClass::Refresh_Cells(CELL cell, short const *list) +{ + if (*list == REFRESH_SIDEBAR) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + RadarClass::Refresh_Cells(cell, list); +} + + +/*************************************************************************** + * PowHeight -- Given a value figure where it falls on bar * + * * + * INPUT: int value - the value we are testing * + * * + * OUTPUT: int the height of the point that this value is on graph * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/14/1995 PWG : Created. * + *=========================================================================*/ +int PowerClass::Power_Height(int value) +{ + int num = value/ POWER_STEP_LEVEL; // figure out the initial num of DRAIN_VALUE's + int retval = 0; // currently there is no power + + /* + ** Loop through the diffrent hundreds figuring out the fractional piece + ** of each. + */ + for (int lp = 0; lp < num; lp ++) { + retval = retval + (((PowHeight - 2) - retval) / POWER_STEP_FACTOR); + value -= POWER_STEP_LEVEL; + } + + /* + ** Adjust the retval to factor in the remainder + */ + if (value) { + retval = retval + (((((PowHeight - 2) - retval) / POWER_STEP_FACTOR) * value) / POWER_STEP_LEVEL); + } + + retval = Bound(retval, 0, PowHeight -2); + return(retval); +} + + +/*********************************************************************************************** + * PowerClass::PowerButtonClass::Action -- Handles the mouse over the power bar area. * + * * + * This routine handles input on the power bar area. Since no input is used for the power * + * bar, this routine just pops up appropriate help text for the power bar. * + * * + * INPUT: flags -- The event flags that triggered this action call. * + * * + * key -- The key code (if any) associated with the trigger event. * + * * + * OUTPUT: Should further button processing be stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/07/1995 JLB : Created. * + *=============================================================================================*/ +int PowerClass::PowerButtonClass::Action(unsigned flags, KeyNumType & key) +{ + if (!Map.IsSidebarActive) { + return(false); + } + + /* + ** Force any help label to disappear when the mouse is held over the + ** radar map. + */ + Map.Override_Mouse_Shape(MOUSE_NORMAL); + if (PlayerPtr->Power_Fraction() < 0x0100 && PlayerPtr->Power > 0) { + Map.Help_Text(TXT_POWER_OUTPUT_LOW, -1, -1, CC_GREEN); + } else { + Map.Help_Text(TXT_POWER_OUTPUT, -1, -1, CC_GREEN); + } + GadgetClass::Action(flags, key); + return(true); +} + + diff --git a/POWER.H b/POWER.H new file mode 100644 index 0000000..fcdf49f --- /dev/null +++ b/POWER.H @@ -0,0 +1,126 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\power.h_v 2.16 16 Oct 1995 16:48:06 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : POWER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : December 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef POWER_H +#define POWER_H + +#include "radar.h" + +class PowerClass : public RadarClass +{ + public: + int PowX; + int PowY; + int PowWidth; + int PowHeight; + int PowLineSpace; + int PowLineWidth; + + + PowerClass(); + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time inits + + virtual void Init_Clear(void); // Clears all to known state + virtual void Draw_It(bool complete=false); + virtual void AI(KeyNumType &input, int x, int y); + virtual void Refresh_Cells(CELL cell, short const *list); +// virtual void Must_Redraw_Sidebar(void); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + unsigned IsToRedraw:1; + + protected: + /* + ** This gadget is used to capture mouse input on the power bar. + */ + class PowerButtonClass : public GadgetClass { + public: + PowerButtonClass(void) : GadgetClass(0,0,0,0,LEFTPRESS|LEFTRELEASE|LEFTHELD|LEFTUP|RIGHTPRESS,true) {}; + + protected: + virtual int Action(unsigned flags, KeyNumType & key); + friend class PowerClass; + }; + + /* + ** This is the "button" that tracks all input to the tactical map. + ** It must be available to derived classes, for Save/Load purposes. + */ + static PowerButtonClass PowerButton; + + enum PowerEnums { + POWER_STEP_LEVEL=100, + POWER_STEP_FACTOR=6, + }; + + private: + int Power_Height(int value); + + unsigned IsActive:1; + + int RecordedDrain; + int RecordedPower; + int DesiredDrainHeight; + int DesiredPowerHeight; + int DrainHeight; + int PowerHeight; + int DrainBounce; + int PowerBounce; + short PowerDir; + short DrainDir; + + /* + ** Points to the shape to use for the "desired" power level indicator. + */ + static void const * PowerShape; + + /* + ** Points to the shapes to be used for drawing the power bar + */ + static void const * PowerBarShape; +}; + +#endif diff --git a/PROFILE.CPP b/PROFILE.CPP new file mode 100644 index 0000000..4a4c069 --- /dev/null +++ b/PROFILE.CPP @@ -0,0 +1,656 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\profile.cpv 2.18 16 Oct 1995 16:51:14 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : PROFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : September 10, 1993 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWGetPrivateProfileInt -- Fetches integer value from INI. * + * WWWritePrivateProfileInt -- Write a profile int to the profile data block. * + * WWGetPrivateProfileString -- Fetch string from INI. * + * WWWritePrivateProfileString -- Write a string to the profile data block. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * Read_Private_Config_Struct -- Fetches override integer value. * + * * + * INPUT: * + * OUTPUT: * + * WARNINGS: * + * HISTORY: * + * 08/05/1992 JLB : Created. * + *=========================================================================*/ +bool Read_Private_Config_Struct (char *profile, NewConfigType *config) +{ + config->DigitCard = WWGetPrivateProfileHex ("Sound", "Card", profile); + config->IRQ = WWGetPrivateProfileInt ("Sound", "IRQ", 0,profile); + config->DMA = WWGetPrivateProfileInt ("Sound", "DMA", 0,profile); + config->Port = WWGetPrivateProfileHex ("Sound", "Port", profile); + config->BitsPerSample= WWGetPrivateProfileInt ("Sound", "BitsPerSample",0,profile); + config->Channels = WWGetPrivateProfileInt ("Sound", "Channels", 0,profile); + config->Reverse = WWGetPrivateProfileInt ("Sound", "Reverse", 0,profile); + config->Speed = WWGetPrivateProfileInt ("Sound", "Speed", 0,profile); + WWGetPrivateProfileString ("Language", "Language", NULL, config->Language, 3, profile); + + return((config->DigitCard == 0) && (config->IRQ == 0) && (config->DMA == 0)); +} + + +/*************************************************************************** + * Get_Private_Profile_Hex -- Fetches override integer value. * + * * + * INPUT: * + * OUTPUT: * + * WARNINGS: * + * HISTORY: * + * 08/05/1992 JLB : Created. * + *=========================================================================*/ +unsigned WWGetPrivateProfileHex (char const *section, char const *entry, char *profile) +{ + char buffer[MAX_ENTRY_SIZE]; // Integer staging buffer. + unsigned card; + + memset (buffer, '0', MAX_ENTRY_SIZE); // MAX_ENTRY_SIZE = 15 + buffer[MAX_ENTRY_SIZE] = '\0'; + + WWGetPrivateProfileString(section, entry, "0", buffer, MAX_ENTRY_SIZE, profile); + + if (strlen (buffer) > 0) { + sscanf (buffer, "%x", &card); + } else { + card = 0; + } + + return(card); +} + + +/*********************************************************************************************** + * WWGetPrivateProfileInt -- Fetches integer value. * + * * + * INPUT: * + * section section to read from * + * * + * entry name of entry to read * + * * + * def default value, if entry isn't found * + * * + * profile buffer containing INI data * + * * + * OUTPUT: * + * integer requested * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + *=============================================================================================*/ +int WWGetPrivateProfileInt(char const *section, char const *entry, int def, char *profile) +{ + char buffer[16]; // Integer staging buffer. + + /* + ** Store the default in the buffer. + */ + sprintf(buffer, "%d", def); + + /* + ** Get the buffer; use itself as the default. + */ + WWGetPrivateProfileString(section, entry, buffer, buffer, 15, profile); + + /* + ** Convert to int & return. + */ + return(atoi(buffer)); +} + + +/*********************************************************************************************** + * WWWritePrivateProfileInt -- Write a profile int to the profile data block. * + * * + * INPUT: * + * section section name to write to * + * * + * entry name of entry to write; if NULL, the entire section is deleted * + * * + * value value to write * + * * + * profile INI buffer * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +bool WWWritePrivateProfileInt(char const *section, char const *entry, int value, char *profile) +{ + char buffer[250]; // Working section buffer. + + /* + ** Just return if nothing to do. + */ + if (!profile || !section) { + return(true); + } + + /* + ** Generate string to save. + */ + sprintf(buffer, "%d", value); + + /* + ** Save the string. + */ + return(WWWritePrivateProfileString(section, entry, buffer, profile)); +} + + +/*********************************************************************************************** + * WWGetPrivateProfileString -- Fetch game override string. * + * * + * INPUT: * + * section section name to read from * + * * + * entry name of entry to read; if NULL, all entry names are returned * + * * + * def default string to use if not found; can be NULL * + * * + * retbuffer buffer to store result in * + * * + * retlen max length of return buffer * + * * + * profile INI buffer * + * * + * OUTPUT: * + * ptr to entry found in INI buf; NULL if not found * + * * + * WARNINGS: * + * On the PC, the "\n" (10) is translated to "\r\n" (13,10) when it's written * + * to disk. This routine must take this into consideration, by searching * + * for \n when scanning backward, and for \r when scanning forward. * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + *=============================================================================================*/ +char * WWGetPrivateProfileString(char const *section, char const *entry, char const *def, char *retbuffer, int retlen, char *profile) +{ + char * workptr, // Working pointer into profile block. + * altworkptr; // Alternate work pointer. + char sec[50]; // Working section buffer. + char *retval; // Start of section or entry pointer. + char * next; // Pointer to start of next section (or EOF). + char c,c2; // Working character values. + int len; // Working substring length value. + int entrylen; // Byte length of specified entry. + char *orig_retbuf; // original retbuffer ptr + + /* + ** Fill in the default value just in case the entry could not be found. + */ + if (retbuffer) { + if (def) { + strncpy(retbuffer, def, retlen); + } + retbuffer[retlen-1] = '\0'; + orig_retbuf = retbuffer; + } + + /* + ** Make sure a profile string was passed in + */ + if (!profile || !section) { + return(retbuffer); + } + + /* + ** Build section string to match file image. + */ + sprintf(sec, "[%s]", section); // sec = section name including []'s + strupr(sec); + len = strlen(sec); // len = section name length, incl []'s + + /* + ** Scan for a matching section + */ + retval = profile; + workptr = profile; + for (;;) { + + /* + ** 'workptr' = start of next section + */ + workptr = strchr(workptr, '['); + + /* + ** If the end has been reached without finding the desired section + ** then abort with a failure flag. + */ + if (!workptr) { + return(NULL); + } + + /* + ** 'c' = character just before the '[' + */ + if (workptr==profile) { + c = '\n'; + } else { + c = *(workptr-1); + } + + /* + ** If this is the section name & the character before is a newline, + ** process this section + */ + if (memicmp(workptr, sec, len) == 0 && (c == '\n')) { + + /* + ** Skip work pointer to start of first valid entry. + */ + workptr += len; + while (isspace(*workptr)) { + workptr++; + } + + /* + ** If the section name is empty, we will have stepped onto the start + ** of the next section name; inserting new entries here will leave + ** a blank line between this section's name & 1st entry. So, check + ** for 2 newlines in a row & step backward. + */ + if (workptr - profile > 4) { + if ( *(workptr-1)=='\n' && *(workptr-3)=='\n') + workptr -= 2; + } + + /* + ** 'next = end of section or end of file. + */ + next = strchr(workptr, '['); + for (;;) { + if (next) { + + c = *(next-1); + + /* + ** If character before '[' is newline, this is the start of the + ** next section + */ + if (c == '\n') { + if (*(next-1)=='\n' && *(next-3)=='\n') { + next -= 2; + } + break; + } + + /* + ** This bracket was in the section; keep looking + */ + next = strchr(next+1, '['); + } else { + + /* + ** No bracket found; set 'next' to the end of the file + */ + next = workptr + strlen(workptr)-1; + break; + } + } + + /* + ** If a specific entry was specified then return with the associated + ** string. + */ + if (entry) { + retval = workptr; + entrylen = strlen(entry); + + for (;;) { + /* + ** Search for the 1st character of the entry + */ + workptr = strchr(workptr, *entry); + + /* + ** If the end of the file has been reached or we have spilled + ** into the next section, then abort + */ + if (!workptr || workptr >= next) { + return(NULL); + } + + /* + ** 'c' = character before possible entry; must be a newline + ** 'c2' = character after possible entry; must be '=' or space + */ + c = *(workptr-1); + c2 = *(workptr+entrylen); + + /* + ** Entry found; extract it + */ + if (memicmp(workptr, entry, entrylen) == 0 && (c == '\n') && + (c2 == '=' || isspace(c2))) { + retval = workptr; + workptr += entrylen; // skip entry name + workptr = strchr(workptr, '='); // find '=' + + /* + ** 'altworkptr' = next newline; \r is used here since we're + ** scanning forward! + */ + if (workptr) { + altworkptr = strchr(workptr, '\r'); // find next newline + } + + /* + ** Return if there was no '=', or if the newline is before + ** the next '=' + */ + if (workptr == NULL || altworkptr < workptr) { + return(retval); + } + + /* + ** Skip any white space after the '=' and before the first + ** valid character of the parameter. + */ + workptr++; // Skip the '='. + while (isspace(*workptr)) { + + /* + ** Just return if there's no entry past the '='. + */ + if (workptr >= altworkptr) + return(retval); + + workptr++; // Skip the whitespace + } + + /* + ** Copy the entry into the return buffer. + */ + len = (int)(altworkptr - workptr); + if (len > retlen-1) { + len = retlen-1; + } + + if (retbuffer) { + memcpy(retbuffer, workptr, len); + *(retbuffer + len) = '\0'; // Insert trailing null. + strtrim(retbuffer); + } + return(retval); + } + + /* + ** Entry was not found; go to the next one + */ + workptr++; + } + } else { + + /* + ** No entry was specified, so build a list of all entries. + ** 'workptr' is at 1st entry after section name + ** 'next' is next bracket, or end of file + */ + retval = workptr; + + if (retbuffer) { + + /* + ** Keep accumulating the identifier strings in the retbuffer. + */ + while (workptr && workptr < next) { + altworkptr = strchr(workptr, '='); // find '=' + + if (altworkptr && altworkptr < next) { + int length; // Length of ID string. + + length = (int)(altworkptr - workptr); + + /* + ** Make sure we don't write past the end of the retbuffer; + ** add '3' for the 3 NULL's at the end + */ + if (retbuffer - orig_retbuf + length + 3 < retlen) { + memcpy(retbuffer, workptr, length); // copy entry name + *(retbuffer+length) = '\0'; // NULL-terminate it + strtrim(retbuffer); // trim spaces + retbuffer += strlen(retbuffer)+1; // next pos in dest buf + } else { + break; + } + + /* + ** Advance the work pointer to the start of the next line + ** by skipping the end of line character. + */ + workptr = strchr(altworkptr, '\n'); + if (!workptr) { + break; + } + workptr++; + } else { + + /* + ** If no '=', break out of loop + */ + break; + } + } + + /* + ** Final trailing terminator. Make double sure the double + ** trailing null is added. + */ + *retbuffer++ = '\0'; + *retbuffer++ = '\0'; + } + break; + } + } else { + + /* + ** Section name not found; go to the next bracket & try again + ** Advance past '[' and keep scanning. + */ + workptr++; + } + } + + return(retval); +} + + +/*********************************************************************************************** + * WWWritePrivateProfileString -- Write a string to the profile data block. * + * * + * INPUT: * + * section section name to write to * + * entry name of entry to write; if NULL, the section is deleted * + * string string to write; if NULL, the entry is deleted * + * profile INI buffer * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * This function has to translate newlines into \r\n sequences. * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +bool WWWritePrivateProfileString(char const *section, char const *entry, char const *string, char *profile) +{ + char buffer[250]; // Working section buffer + char *offset; + char *next; // ptr to next section + char c; // Working character value + + /* + ** Just return if nothing to do. + */ + if (!profile || !section) { + return(true); + } + + /* + ** Try to find the section. WWGetPrivateProfileString with NULL entry name + ** will return all entry names in the given buffer, truncated to the given + ** buffer length. 'offset' will point to 1st entry in the section, NULL if + ** section not found. + */ + offset = WWGetPrivateProfileString(section, NULL, NULL, NULL, 0, profile); + + /* + ** If the section could not be found, then add it to the end. Don't add + ** anything if a removal of an entry is requested (it is obviously already + ** non-existent). Make sure two newlines precede the section name. + */ + if (!offset && entry) { + sprintf(buffer, "\r\n[%s]\r\n", section); + strcat(profile, buffer); + } + + /* + ** If the section is there and 'entry' is NULL, remove the entire section + */ + if (offset && !entry) { + + /* + ** 'next = end of section or end of file. + */ + next = strchr(offset, '['); + for (;;) { + if (next) { + c = *(next-1); + + /* + ** If character before '[' is newline, this is the start of the + ** next section + */ + if (c == '\n') { + if ( *(next-1)=='\n' && *(next-3)=='\n') { + next -= 2; + } + break; + } + + /* + ** This bracket was in the section; keep looking + */ + next = strchr(next+1, '['); + } else { + + /* + ** No bracket found; set 'next' to the end of the file + */ + next = offset + strlen(offset); + break; + } + } + + /* + ** Remove the section + */ + strcpy(offset,next); + + return(true); + } + + /* + ** Find the matching entry within the desired section. A NULL return buffer + ** with 0 length will just return the offset of the found entry, NULL if + ** entry not found. + */ + offset = WWGetPrivateProfileString(section, entry, NULL, NULL, 0, profile); + + /* + ** Remove any existing entry + */ + if (offset) { + int eol; // Working EOL offset. + + /* + ** Get # characters up to newline; \n is used since we're after the end + ** of this line + */ + eol = strcspn(offset, "\n"); + + /* + ** Erase the entry by strcpy'ing the entire INI file over this entry + */ + if (eol) { + strcpy(offset, offset + eol + 1); + } + } else { + + /* + ** Entry doesn't exist, so point 'offset' to the 1st entry position in + ** the section. + */ + offset = WWGetPrivateProfileString(section, NULL, NULL, NULL, 0, profile); + } + + /* + ** Add the desired entry. + */ + if (entry && string) { + + /* + ** Generate entry string. + */ + sprintf(buffer, "%s=%s\r\n", entry, string); + + /* + ** Make room for new entry. + */ + memmove(offset+strlen(buffer), offset, strlen(offset)+1); + + /* + ** Copy the entry into the INI buffer. + */ + memcpy(offset, buffer, strlen(buffer)); + } + + return(true); +} diff --git a/QUEUE.CPP b/QUEUE.CPP new file mode 100644 index 0000000..f3d318d --- /dev/null +++ b/QUEUE.CPP @@ -0,0 +1,4204 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c0\vcs\code\queue.cpv 2.24 11 Oct 1995 13:47:40 JOE_BOSTIC $ */ +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : QUEUE.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 11/28/95 * + * * + * Last Update : November 28, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions for Queueing Events: * + * Queue_Mission -- Queue a mega mission event. * + * Queue_Options -- Queue the options event. * + * Queue_Exit -- Add the exit game event to the queue. * + * * + * Functions for processing Queued Events: * + * Queue_AI -- Process all queued events. * + * Queue_AI_Normal -- Process all queued events. * + * Queue_AI_Multiplayer -- Process all queued events. * + * * + * Main Multiplayer Queue Logic: * + * Wait_For_Players -- Waits for other systems to come on-line * + * Generate_Timing_Event -- computes & queues a RESPONSE_TIME event * + * Process_Send_Period -- timing for sending packets every 'n' frames * + * Send_Packets -- sends out events from the OutList * + * Send_FrameSync -- Sends a FRAMESYNC packet * + * Process_Receive_Packet -- processes an incoming packet * + * Process_Serial_Packet -- Handles an incoming serial packet * + * Can_Advance -- determines if it's OK to advance to the next frame * + * Process_Reconnect_Dialog -- processes the reconnection dialog * + * Handle_Timeout -- attempts to reconnect; if fails, bails. * + * Stop_Game -- stops the game * + * * + * Packet Compression / Decompression: * + * Build_Send_Packet -- Builds a big packet from a bunch of little ones. * + * Add_Uncompressed_Events -- adds uncompressed events to a packet * + * Add_Compressed_Events -- adds compressed events to a packet * + * Breakup_Receive_Packet -- Splits a big packet into little ones. * + * Extract_Uncompressed_Events -- extracts events from a packet * + * Extract_Compressed_Events -- extracts events from a packet * + * * + * DoList Management: * + * Execute_DoList -- Executes commands from the DoList * + * Clean_DoList -- Cleans out old events from the DoList * + * Queue_Record -- Records the DoList to disk * + * Queue_Playback -- plays back queue entries from a record file * + * * + * Debugging: * + * Compute_Game_CRC -- Computes a CRC value of the entire game. * + * Add_CRC -- Adds a value to a CRC * + * Print_CRCs -- Prints a data file for finding Sync Bugs * + * Init_Queue_Mono -- inits mono display * + * Update_Queue_Mono -- updates mono display * + * Print_Framesync_Values -- displays frame-sync variables * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "function.h" +#include "tcpip.h" + +/********************************** Defines *********************************/ +#define SHOW_MONO 1 + +int tmp_flag = 0; + +/********************************** Globals *********************************/ +//--------------------------------------------------------------------------- +// GameCRC is the current computed CRC value for this frame. +// CRC[] is a record of our last 32 game CRC's. +// ColorNames is for debug output in Print_CRCs +//--------------------------------------------------------------------------- +#ifndef DEMO +static unsigned long GameCRC; +static unsigned long CRC[32] = + {0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0}; + +static char *ColorNames[6] = { + "Yellow", + "Red", + "BlueGreen", + "Orange", + "Green", + "Blue", +}; +#endif //DEMO + +//........................................................................... +// Mono debugging variables: +// NetMonoMode: 0 = show connection output, 1 = flowcount output +// NewMonoMode: set by anything that toggles NetMonoMode; re-inits screen +// IsMono: used for taking control of Mono screen away from the engine +//........................................................................... +#ifndef DEMO +int NetMonoMode = 1; +int NewMonoMode = 1; +static int IsMono = 0; +#endif //DEMO + +//--------------------------------------------------------------------------- +// Several routines return various codes; here's an enum for all of them. +//--------------------------------------------------------------------------- +typedef enum RetcodeEnum { + RC_NORMAL, // no news is good news + RC_PLAYER_READY, // a new player has been heard from + RC_SCENARIO_MISMATCH, // scenario mismatch + RC_DOLIST_FULL, // DoList is full + RC_SERIAL_PROCESSED, // modem: SERIAL packet was processed + RC_PLAYER_LEFT, // modem: other player left the game + RC_HUNG_UP, // modem has hung up + RC_NOT_RESPONDING, // other player not responding (timeout/hung up) + RC_CANCEL, // user cancelled +} RetcodeType; + + +/********************************* Prototypes *******************************/ +//........................................................................... +// Main multiplayer queue logic +//........................................................................... +static void Queue_AI_Normal(void); +#ifndef DEMO +static void Queue_AI_Multiplayer(void); +static RetcodeType Wait_For_Players(int first_time, ConnManClass *net, + int resend_delta, int dialog_time, int timeout, char *multi_packet_buf, + int my_sent, long *their_frame, unsigned short *their_sent, + unsigned short *their_recv); +static void Generate_Timing_Event(ConnManClass *net, int my_sent); +static void Generate_Real_Timing_Event(ConnManClass *net, int my_sent); +static void Generate_Process_Time_Event(ConnManClass *net); +static int Process_Send_Period(ConnManClass *net); +static int Send_Packets(ConnManClass *net, char *multi_packet_buf, + int multi_packet_max, int max_ahead, int my_sent); +static void Send_FrameSync(ConnManClass *net, int cmd_count); +static RetcodeType Process_Receive_Packet(ConnManClass *net, + char *multi_packet_buf, int id, int packetlen, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv); +static RetcodeType Process_Serial_Packet(char *multi_packet_buf, + int first_time); +static int Can_Advance(ConnManClass *net, int max_ahead, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv); +static int Process_Reconnect_Dialog(CountDownTimerClass *timeout_timer, + long *their_frame, int num_conn, int reconn, int fresh); +static int Handle_Timeout(ConnManClass *net, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv); +static void Stop_Game(void); +#endif //DEMO + +//........................................................................... +// Packet compression/decompression: +//........................................................................... +#ifndef DEMO +static int Build_Send_Packet(void *buf, int bufsize, int frame_delay, + int num_cmds, int cap); +static int Breakup_Receive_Packet(void *buf, int bufsize ); +#endif //DEMO +int Add_Uncompressed_Events(void *buf, int bufsize, int frame_delay, int size, + int cap); +int Add_Compressed_Events(void *buf, int bufsize, int frame_delay, int size, + int cap); +int Extract_Uncompressed_Events(void *buf, int bufsize); +int Extract_Compressed_Events(void *buf, int bufsize); + +//........................................................................... +// DoList management: +//........................................................................... +static int Execute_DoList(int max_houses, HousesType base_house, + ConnManClass *net, TCountDownTimerClass *skip_crc, + long *their_frame, unsigned short *their_sent, unsigned short *their_recv); +static void Clean_DoList(ConnManClass *net); +#ifndef DEMO +static void Queue_Record(void); +static void Queue_Playback(void); +#endif //DEMO + +//........................................................................... +// Debugging: +//........................................................................... +#ifndef DEMO +static void Compute_Game_CRC(void); +static void Init_Queue_Mono(ConnManClass *net); +static void Update_Queue_Mono(ConnManClass *net, int flow_index); +static void Print_Framesync_Values(long curframe, unsigned long max_ahead, + int num_connections, unsigned short *their_recv, + unsigned short *their_sent, unsigned short my_sent); +#endif //DEMO +void Add_CRC(unsigned long *crc, unsigned long val); +void Print_CRCs(EventClass *); + +extern void Keyboard_Process(KeyNumType &input); +void Dump_Packet_Too_Late_Stuff(EventClass *event); + +extern void Register_Game_End_Time(void); +extern void Send_Statistics_Packet(void); + +/*************************************************************************** + * Queue_Mission -- Queue a mega mission event. * + * * + * This routine is called when the player causes a change to a game unit. * + * The event that initiates the change is queued to as a result of a call * + * to this routine. * + * * + * INPUT: * + * whom Whom this mission request applies to (a friendly unit). * + * mission The mission to assign to this object. * + * target The target of this mission (if any). * + * dest The movement destination for this mission (if any). * + * * + * OUTPUT: * + * Was the mission request queued successfully? * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=========================================================================*/ +bool Queue_Mission(TARGET whom, MissionType mission, TARGET target, + TARGET destination) +{ + if (! OutList.Add(EventClass(whom, mission, target, destination))) { + return(false); + } + else { + return(true); + } + +} /* end of Queue_Mission */ + + +/*************************************************************************** + * Queue_Options -- Queue the options event. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * Was the options screen event queued successfully? * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=========================================================================*/ +bool Queue_Options(void) +{ + if (! OutList.Add(EventClass(EventClass::OPTIONS))) { + return(false); + } + else { + return(true); + } + +} /* end of Queue_Options */ + + +/*************************************************************************** + * Queue_Exit -- Add the exit game event to the queue. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * Was the exit event queued successfully? * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=========================================================================*/ +bool Queue_Exit(void) +{ + if (! OutList.Add(EventClass(EventClass::EXIT))) { + return(false); + } + else { + return(true); + } + +} /* end of Queue_Exit */ + + +/*************************************************************************** + * Queue_AI -- Process all queued events. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=========================================================================*/ +void Queue_AI(void) +{ +#ifdef DEMO + Queue_AI_Normal(); +#else //DEMO + + if (PlaybackGame) { + Queue_Playback(); + } + + else { + + switch (GameToPlay) { + + case GAME_NORMAL: + Queue_AI_Normal(); + break; + + case GAME_MODEM: + case GAME_NULL_MODEM: + case GAME_IPX: + case GAME_INTERNET: + Queue_AI_Multiplayer(); + break; + } + } +#endif //DEMO + +} /* end of Queue_AI */ + + +/*************************************************************************** + * Queue_AI_Normal -- Process all queued events. * + * * + * This is the "normal" version of the queue management routine. It does * + * the following: * + * - Transfers items in the OutList to the DoList * + * - Executes any commands in the DoList that are supposed to be done on * + * this frame # * + * - Cleans out the DoList * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=========================================================================*/ +static void Queue_AI_Normal(void) +{ + //------------------------------------------------------------------------ + // Move events from the OutList (events generated by this player) into the + // DoList (the list of events to execute). + //------------------------------------------------------------------------ + while (OutList.Count) { + OutList.First().IsExecuted = false; + if (!DoList.Add(OutList.First())) { + ; + } + OutList.Next(); + } + + //------------------------------------------------------------------------ + // Save the DoList to disk, if we're in "Record" mode + //------------------------------------------------------------------------ +#ifndef DEMO + if (RecordGame) { + Queue_Record(); + } +#endif //DEMO + + //------------------------------------------------------------------------ + // Execute the DoList + //------------------------------------------------------------------------ + if (!Execute_DoList(1,PlayerPtr->Class->House, NULL, NULL, NULL, + NULL, NULL)) { + GameActive = 0; + return; + } + //------------------------------------------------------------------------ + // Clean out the DoList + //------------------------------------------------------------------------ + Clean_DoList(NULL); + +} /* end of Queue_AI_Normal */ + +#ifndef DEMO + +/*************************************************************************** + * Queue_AI_Multiplayer -- Process all queued events. * + * * + * This is the network version of the queue management routine. It does * + * the following: * + * - If this is the 1st frame, waits for other systems to signal ready * + * - Generates a timing event, to allow the connection time to be dynamic * + * - Handles timing related to sending packets every 'n' frames * + * - Sends outgoing events * + * - Frame-syncs to the other systems (see below) * + * - Executes & cleans out the DoList * + * * + * The Frame-Sync'ing logic is the heart & soul of network play. It works * + * by ensuring that any system won't out-run the other system by more than * + * 'MaxAhead' frames; this in turn ensures that a packet's * + * execution frame # won't have been passed by the time that packet is * + * received by all systems. * + * * + * To achieve this, the system must keep track of all other system's * + * current frame #'s; these are stored in an array called 'their_frame[]'. * + * However, because current frame #'s are sent in FRAMEINFO packets, which * + * don't require an ACK, and command packets are sent in packets requiring * + * an ACK, it's possible for a command packet to get lost, and the next * + * frame's FRAMEINFO packet to not get lost; the other system may then * + * advance past the frame # the command is to execute on! So, to prevent * + * this, all FRAMEINFO packets include a CommandCount field. This value * + * tells the other system how many events it should have received by this * + * time. This system can therefore keep track of how many commands it's * + * actually received, and compare it to the CommandCount field, to see if * + * it's missed an event packet. The # of events we've received from each * + * system is stored in 'their_recv[]', and the # events they say they've * + * sent is stored in 'their_sent[]'. * + * * + * Thus, two conditions must be met in order to advance to the next frame: * + * - Our current frame # must be <= their_frame + MaxAhead * + * - their_recv[i] must be >= their_sent[i] * + * * + * 'their_frame[] is updated by Process_Receive_Packet() * + * 'their_recv[] is updated by Process_Receive_Packet() * + * 'their_sent[] is updated by Process_Receive_Packet() * + * 'my_sent' is updated by this routine. * + * * + * The only routines allowed to pop up dialogs are: * + * Wait_For_Players() (only pops up the reconnect dialog) * + * Execute_DoList() (tells if out of sync, or packet recv'd too late) * + * * + * Sign-off's are detected by: * + * - Timing out while waiting for a packet * + * - Detecting that the other player is now at the score screen or * + * connection dialog (serial) * + * - If we see an EventClass::EXIT event on the private channel * + * * + * The current communications protocol, COMM_PROTOCOL_MULTI_E_COMP, has * + * the following properites: * + * - It compresses packets, so that the minimum number of bytes are * + * transmitted. Packets are compressed by extracting all info common to * + * the events into the packet header, and then sending only the bytes * + * relevant to each type of event. For instance, if 100 infantry guys * + * are told to move to the same location, the command itself & the * + * location will be included in the 1st movement command only; after * + * that, there will be a rep count then 99 infantry TARGET numbers, * + * identifying all the infantry told to move. * + * - The protocol also only sends packets out every 'n' frames. This cuts * + * the data rate dramatically. It means that 'MaxAhead' must be * + * divisible by 'n'; also, the minimum value for 'MaxAhead' is * + * 'n * 2', to give both sides some "breathing" room in case a FRAMEINFO * + * packet gets missed. * + * * + * Note: For synchronization-waiting loops (like waiting to hear from all * + * other players, waiting to advance to the next frame, etc), use * + * Net.Num_Connections() rather than NumPlayers; this reflects the * + * actual # of connections, and can be "faked" into playing even when * + * there aren't any other players actually there. A typical example of * + * this is playing back a recorded game. For command-execution loops, use * + * NumPlayers. This ensures all commands get executed, even if * + * there isn't a human generating those commands. * + * * + * The modem works a little differently from the network in this respect: * + * - The connection has to stay "alive" even if the other player exits to * + * the join dialog. This prevents each system from timing out & hanging * + * the modem up. Thus, packets are sent back & forth & just thrown away,* + * but each system knows the other is still there. Messages may be sent * + * between systems, though. * + * - Destroy_Null_Connection doesn't hang up the modem, so * + * Num_Connections() still reports a value of 1 even though the other * + * player has left. * + * - Any waits on Num_Connections() must also check for * + * NumPlayers > 1, to keep from waiting forever if the other * + * guy has left * + * - Packets sent to a player who's left require no ACK * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Queue_AI_Multiplayer(void) +{ + //........................................................................ + // Enums: + //........................................................................ + enum { + MIXFILE_RESEND_DELTA = 120, // ticks b/w resends + MIXFILE_TIMEOUT = 3600, // timeout waiting for mixfiles + FRAMESYNC_DLG_TIME = (3*60), // time until displaying reconnect dialog + FRAMESYNC_TIMEOUT = (25*60), // timeout waiting for frame sync packet + }; + + int timeout_factor = (GameToPlay == GAME_INTERNET) ? 6 : 1; + + //........................................................................ + // Variables for sending, receiving & parsing packets: + //........................................................................ + ConnManClass *net; // ptr to access all multiplayer functions + EventClass packet; // for sending single frame-sync's + char *multi_packet_buf; // buffer for sending/receiving + int multi_packet_max; // max length of multi_packet_buf + + //........................................................................ + // Frame-sync'ing variables. Values in these arrays are stored in the + // order in which the connections are created. + // (ie net->Connection_Index(id)) + //........................................................................ + static long + their_frame[MAX_PLAYERS - 1]; // other players' frame #'s + static unsigned short + their_sent[MAX_PLAYERS - 1]; // # cmds other player claims to have sent + static unsigned short + their_recv[MAX_PLAYERS - 1]; // # cmds actually received from others + static unsigned short + my_sent; // # cmds I've sent out + + //........................................................................ + // Other misc variables + //........................................................................ + int i; + RetcodeType rc; + int reconnect_dlg = 0; // 1 = the reconnect dialog is displayed + + //------------------------------------------------------------------------ + // Initialize the packet buffer pointer & its max size + //------------------------------------------------------------------------ + if (GameToPlay == GAME_MODEM + || GameToPlay == GAME_NULL_MODEM){ + multi_packet_buf = NullModem.BuildBuf; + multi_packet_max = NullModem.MaxLen - sizeof (CommHeaderType); + net = &NullModem; + } + else if (GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) { + multi_packet_buf = MetaPacket; + multi_packet_max = MetaSize; + net = &Ipx; + } + + //------------------------------------------------------------------------ + // Debug stuff + //------------------------------------------------------------------------ + Init_Queue_Mono(net); + Update_Queue_Mono (net, 0); + + //------------------------------------------------------------------------ + // If we've just started a game, or loaded a multiplayer game, we must + // wait for all other systems to signal ready. + //------------------------------------------------------------------------ + if (Frame==0) { + //..................................................................... + // Initialize static locals + //..................................................................... + for (i = 0; i < MAX_PLAYERS - 1; i++) { + their_frame[i] = -1; + their_sent[i] = 0; + their_recv[i] = 0; + TheirProcessTime[i] = -1; + } + my_sent = 0; + for (i = 0; i < 32; i++) { + CRC[i] = 0; + } + + //..................................................................... + // Send our initial FRAMESYNC packet + //..................................................................... + Send_FrameSync(net, my_sent); + + //..................................................................... + // Wait for the other guys + //..................................................................... + rc = Wait_For_Players (1, net, MIXFILE_RESEND_DELTA, FRAMESYNC_DLG_TIME*timeout_factor, + MIXFILE_TIMEOUT, multi_packet_buf, my_sent, their_frame, + their_sent, their_recv); + + if (rc != RC_NORMAL) { + if (rc == RC_NOT_RESPONDING) { + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + } + else if (rc == RC_SCENARIO_MISMATCH) { + CCMessageBox().Process (TXT_SCENARIOS_DO_NOT_MATCH); + } + else if (rc == RC_DOLIST_FULL) { + CCMessageBox().Process(TXT_QUEUE_FULL); + } + Stop_Game(); + return; + } + + //..................................................................... + // Re-initialize frame numbers (in case somebody signed off while I was + // waiting for MIX files to load; we would have fallen through, but + // their frame # would still be -1). + //..................................................................... + for (i = 0; i < MAX_PLAYERS - 1; i++) + their_frame[i] = 0; + + //..................................................................... + // Reset the network response time computation, now that we're both + // sending data again (loading MIX files will have introduced + // deceptively large values). + //..................................................................... + net->Reset_Response_Time(); + + //..................................................................... + // Initialize the frame timers + //..................................................................... + if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + Process_Send_Period(net); + } + + } // end of Frame 0 wait + + //------------------------------------------------------------------------ + // Adjust connection timing parameters every 128 frames. + //------------------------------------------------------------------------ + else if ( (Frame & 0x007f) == 0) { + // + // If we're using the new spiffy protocol, do proper timing handling. + // If we're the net "master", compute our desired frame rate & new + // 'MaxAhead' value. + // + if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + + // + // All systems will transmit their required process time. + // + Generate_Process_Time_Event(net); + + // + // The game "host" will transmit timing adjustment events. + // + if (MPlayerLocalID == MPlayerID[0]) { + Generate_Real_Timing_Event(net, my_sent); + } + } else { + // + // For the older protocols, do the old broken timing handling. + // + Generate_Timing_Event(net, my_sent); + } + } + + //------------------------------------------------------------------------ + // Compute the Game's CRC + //------------------------------------------------------------------------ + Compute_Game_CRC(); + CRC[Frame & 0x001f] = GameCRC; + //unsigned long save_crc = GameCRC; + //Print_CRCs((EventClass *)NULL); + //GameCRC = save_crc; + + //------------------------------------------------------------------------ + // Only process every 'FrameSendRate' frames + //------------------------------------------------------------------------ + if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + if (!Process_Send_Period(net)) { + if (IsMono) { + MonoClass::Disable(); + } + return; + } + } + + //------------------------------------------------------------------------ + // Send our data packet(s); update my command-sent counter + //------------------------------------------------------------------------ + my_sent += Send_Packets(net, multi_packet_buf, multi_packet_max, + MPlayerMaxAhead, my_sent); + + //------------------------------------------------------------------------ + // If this is our first time through, we're done. + //------------------------------------------------------------------------ + if (Frame==0) { + if (IsMono) { + MonoClass::Disable(); + } + return; + } + + //------------------------------------------------------------------------ + // Frame-sync'ing: wait until it's OK to advance to the next frame. + //------------------------------------------------------------------------ + rc = Wait_For_Players (0, net, + (MPlayerMaxAhead << 3), + MAX ( net->Response_Time() * 3, FRAMESYNC_DLG_TIME*timeout_factor ), + FRAMESYNC_TIMEOUT * (timeout_factor*2), + multi_packet_buf, my_sent, their_frame, + their_sent, their_recv); + + if (rc != RC_NORMAL) { + if (rc == RC_NOT_RESPONDING) { + CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + } + else if (rc == RC_SCENARIO_MISMATCH) { + CCMessageBox().Process (TXT_SCENARIOS_DO_NOT_MATCH); + } + else if (rc == RC_DOLIST_FULL) { + CCMessageBox().Process(TXT_QUEUE_FULL); + } + Stop_Game(); + return; + } + + //------------------------------------------------------------------------ + // Save the DoList to disk, if we're in "Record" mode + //------------------------------------------------------------------------ + if (RecordGame) { + Queue_Record(); + } + + //------------------------------------------------------------------------ + // Execute the DoList; if an error occurs, bail out. + //------------------------------------------------------------------------ + if (!Execute_DoList(MPlayerMax, HOUSE_MULTI1, net, NULL, + their_frame, their_sent, their_recv)) { + Stop_Game(); + return; + } + + //------------------------------------------------------------------------ + // Clean out the DoList + //------------------------------------------------------------------------ + Clean_DoList(net); + + if (IsMono) { + MonoClass::Disable(); + } + +} // end of Queue_AI_Multiplayer + + +/*************************************************************************** + * Wait_For_Players -- Waits for other systems to come on-line * + * * + * This routine performs the most critical logic in multiplayer; that of * + * synchronizing my frame number with those of the other systems. * + * * + * INPUT: * + * first_time 1 = 1st time this routine is called * + * net ptr to connection manager * + * resend_delta time (ticks) between FRAMESYNC resends * + * dialog_time time (ticks) until pop up a reconnect dialog * + * timeout time (ticks) until we give up the ghost * + * multi_packet_buf buffer to store packets in * + * my_sent # commands I've sent so far * + * their_frame array of their frame #'s * + * their_sent array of their CommandCount values * + * their_recv array of # cmds I've received from them * + * * + * OUTPUT: * + * RC_NORMAL OK to advance to the next frame * + * RC_CANCEL user hit 'Cancel' at the timeout countdown dlg * + * RC_NOT_RESPONDING other player(s) not responding * + * RC_SCENARIO_MISMATCH scenario's don't match (first_time only) * + * RC_DOLIST_FULL DoList was full * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static RetcodeType Wait_For_Players(int first_time, ConnManClass *net, + int resend_delta, int dialog_time, int timeout, char *multi_packet_buf, + int my_sent, long *their_frame, unsigned short *their_sent, + unsigned short *their_recv) +{ + //........................................................................ + // Variables for sending, receiving & parsing packets: + //........................................................................ + EventClass *event; // event ptr for parsing incoming packets + int packetlen; // size of meta-packet sent, & received + int id; // id of other player + int messages_this_loop; // to limit # messages processed each loop + + //........................................................................ + // Variables used only if 'first_time': + //........................................................................ + int num_ready; // # players signalling ready + + //........................................................................ + // Timing variables + //........................................................................ + CountDownTimerClass retry_timer; // time between FRAMESYNC packet resends + CountDownTimerClass dialog_timer; // time to pop up a dialog + CountDownTimerClass timeout_timer; // general-purpose timeout + + //........................................................................ + // Dialog variables + //........................................................................ + int reconnect_dlg = 0; // 1 = the reconnect dialog is displayed + + //........................................................................ + // Other misc variables + //........................................................................ + KeyNumType input; // for user input + int x,y; // for map input + RetcodeType rc; + + //------------------------------------------------------------------------ + // Wait to hear from all other players + //------------------------------------------------------------------------ + num_ready = 0; + retry_timer.Set (resend_delta, true); // time to retry + dialog_timer.Set (dialog_time, true); // time to show dlg + timeout_timer.Set (timeout, true); // time to bail out + + while (1) { + + Update_Queue_Mono (net, 2); + + //--------------------------------------------------------------------- + // Resend a frame-sync packet if longer than one propogation delay goes + // by; this prevents a "deadlock". If he's waiting for me to advance, + // but has missed my last few FRAMEINFO packets, I may be waiting for + // him to advance. Resending a FRAMESYNC ensures he knows what frame + // number I'm on. + //--------------------------------------------------------------------- + if (!retry_timer.Time()) { + retry_timer.Set (resend_delta, true); // time to retry + Update_Queue_Mono (net, 3); + Send_FrameSync(net, my_sent); + } + + //--------------------------------------------------------------------- + // Service the connections + //--------------------------------------------------------------------- + net->Service(); + + //--------------------------------------------------------------------- + // Pop up a reconnect dialog if enough time goes by + //--------------------------------------------------------------------- + if (!dialog_timer.Time() && SpecialDialog==SDLG_NONE) { + if (Process_Reconnect_Dialog(&timeout_timer, their_frame, + net->Num_Connections(), (first_time==0), (reconnect_dlg==0))) { + return (RC_CANCEL); + } + reconnect_dlg = 1; + } + + //--------------------------------------------------------------------- + // Exit if too much time goes by (the other system has crashed or + // bailed) + //--------------------------------------------------------------------- + if (!timeout_timer.Time()) { + //.................................................................. + // For the first-time run, just give up; something's wrong. + //.................................................................. + if (first_time) { + return (RC_NOT_RESPONDING); + } + //.................................................................. + // Otherwise, we're in the middle of a game; so, the modem & + // network must deal with a timeout differently. + //.................................................................. + else { + Update_Queue_Mono (net, 4); + + if (Handle_Timeout(net, their_frame, their_sent, their_recv)) { + Map.Flag_To_Redraw(true); // erase modem reconnect dialog + Map.Render(); + retry_timer.Set (resend_delta, true); + dialog_timer.Set (dialog_time, true); + timeout_timer.Set (timeout, true); + } + else { + return (RC_NOT_RESPONDING); + } + } + } + + //--------------------------------------------------------------------- + // Check for an incoming message. We must still process commands + // even if 'first_time' is set, in case the other system got my 1st + // FRAMESYNC, but I didn't get his; he'll be at the next frame, and + // may be sending commands. + // We have to limit the number of incoming messages we handle; it's + // possible to go into an infinite loop processing modem messages. + //--------------------------------------------------------------------- + messages_this_loop = 0; + while ( (messages_this_loop++ < 5) && + net->Get_Private_Message (multi_packet_buf, &packetlen, &id) ) { + Update_Queue_Mono (net, 5); + + /*.................................................................. + Get an event ptr to the incoming message + ..................................................................*/ + event = (EventClass *)multi_packet_buf; + + //------------------------------------------------------------------ + // Special processing for a modem game: process SERIAL packets + //------------------------------------------------------------------ + if (GameToPlay == GAME_MODEM + || GameToPlay == GAME_NULL_MODEM){ + //|| GameToPlay == GAME_INTERNET) { + rc = Process_Serial_Packet(multi_packet_buf, first_time); + //............................................................... + // SERIAL packet received & processed + //............................................................... + if (rc == RC_SERIAL_PROCESSED) { + net->Service(); + retry_timer.Set (resend_delta, true); + dialog_timer.Set (dialog_time, true); + timeout_timer.Set (timeout, true); + continue; + } + //............................................................... + // other player has left the game + //............................................................... + else if (rc == RC_PLAYER_LEFT) { + if (first_time) { + num_ready++; + } + break; + } + //............................................................... + // Connection was lost + //............................................................... + else if (rc == RC_HUNG_UP) { + return (RC_NOT_RESPONDING); + } + //............................................................... + // If it was any other type of serial packet, break + //............................................................... + else if (rc != RC_NORMAL) { + break; + } + } + + //------------------------------------------------------------------ + // Process the incoming packet + //------------------------------------------------------------------ + rc = Process_Receive_Packet(net, multi_packet_buf, id, packetlen, + their_frame, their_sent, their_recv); + //.................................................................. + // New player heard from + //.................................................................. + if (rc == RC_PLAYER_READY) { + num_ready++; + } + //.................................................................. + // Scenario's don't match + //.................................................................. + else if (rc == RC_SCENARIO_MISMATCH) { + return (RC_SCENARIO_MISMATCH); + } + //.................................................................. + // DoList was full + //.................................................................. + else if (rc == RC_DOLIST_FULL) { + return (RC_DOLIST_FULL); + } + + //.................................................................. + // Service the connection, to clean out the receive queues + //.................................................................. + net->Service(); + } + + //--------------------------------------------------------------------- + // Debug output + //--------------------------------------------------------------------- + Print_Framesync_Values(Frame, MPlayerMaxAhead, net->Num_Connections(), + their_recv, their_sent, my_sent); + + //--------------------------------------------------------------------- + // Attempt to advance to the next frame. + //--------------------------------------------------------------------- + //..................................................................... + // For the first-time run, just check to see if we've heard from + // everyone. + //..................................................................... + if (first_time) { + if (num_ready >= net->Num_Connections()) break; + } + //..................................................................... + // For in-game processing, we have to check their_sent, their_recv, + // their_frame, etc. + //..................................................................... + else { + if (Can_Advance(net, MPlayerMaxAhead, their_frame, their_sent, + their_recv)) { + break; + } + } + + //--------------------------------------------------------------------- + // Service game stuff. Servicing the map's input, and rendering the + // map, allows the map to scroll even though we're hung up waiting for + // packets. Don't do this if 'first_time' is set, since users could be + // waiting a very long time for all systems to load the scenario, and + // it gets frustrating being able to scroll around without doing + // anything. + //--------------------------------------------------------------------- + Call_Back(); + if (!first_time && SpecialDialog == SDLG_NONE && reconnect_dlg==0) { + WWMouse->Erase_Mouse(&HidPage, TRUE); + Map.Input(input, x, y); + if (input) + Keyboard_Process(input); + Map.Render(); + } + + } /* end of while */ + + //------------------------------------------------------------------------ + // If the reconnect dialog was shown, force the map to redraw. + //------------------------------------------------------------------------ + if (reconnect_dlg) { + Map.Flag_To_Redraw(true); + Map.Render(); + } + + return (RC_NORMAL); + +} // end of Wait_For_Players + + +/*************************************************************************** + * Generate_Timing_Event -- computes & queues a RESPONSE_TIME event * + * * + * This routine adjusts the connection timing on the local system; it also * + * optionally generates a RESPONSE_TIME event, to tell all systems to * + * dynamically adjust the current MaxAhead value. This allows both the * + * MaxAhead & the connection retry logic to have dynamic timing, to adjust * + * to varying line conditions. * + * * + * INPUT: * + * net ptr to connection manager * + * my_sent # commands I've sent out so far * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Generate_Timing_Event(ConnManClass *net, int my_sent) +{ + unsigned long resp_time; // connection response time, in ticks + EventClass ev; + + //------------------------------------------------------------------------ + // Measure the current connection response time. This time will be in + // 60ths of a second, and represents full round-trip time of a packet. + // To convert to one-way packet time, divide by 2; to convert to game + // frames, divide again by 4, assuming a game rate of 15 fps. + //------------------------------------------------------------------------ + resp_time = net->Response_Time(); + + //------------------------------------------------------------------------ + // Adjust my connection retry timing; only do this if I've sent out more + // than 5 commands, so I know I have a measure of the response time. + //------------------------------------------------------------------------ + if (my_sent > 5) { + + net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15); + + //..................................................................... + // If I'm the network "master", I'm also responsible for updating the + // MaxAhead value on all systems, so do that here too. + //..................................................................... + if (MPlayerLocalID == MPlayerID[0]) { + ev.Type = EventClass::RESPONSE_TIME; + //.................................................................. + // For multi-frame compressed events, the MaxAhead must be an even + // multiple of the FrameSendRate. + //.................................................................. + if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + ev.Data.FrameInfo.Delay = MAX( ((((resp_time / 8) + + (FrameSendRate - 1)) / FrameSendRate) * + FrameSendRate), (FrameSendRate * 2) ); +char flip[128]; +sprintf (flip, "C&C95 - Generating timing packet - MaxAhead = %d frames\n", ev.Data.FrameInfo.Delay); +CCDebugString (flip); + + } + //.................................................................. + // For sending packets every frame, just use the 1-way connection + // response time. + //.................................................................. + else { + if (GameToPlay == GAME_MODEM + || GameToPlay == GAME_NULL_MODEM){ + //|| GameToPlay == GAME_INTERNET) { + ev.Data.FrameInfo.Delay = MAX( (resp_time / 8), + MODEM_MIN_MAX_AHEAD ); + } + else if (GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) { + ev.Data.FrameInfo.Delay = MAX( (resp_time / 8), + NETWORK_MIN_MAX_AHEAD ); + } + } + OutList.Add(ev); + } + } + +} // end of Generate_Timing_Event + + +/*************************************************************************** + * Generate_Real_Timing_Event -- Generates a TIMING event * + * * + * INPUT: * + * net ptr to connection manager * + * my_sent # commands I've sent out so far * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/02/1996 BRR : Created. * + *=========================================================================*/ +static void Generate_Real_Timing_Event(ConnManClass *net, int my_sent) +{ + unsigned long resp_time; // connection response time, in ticks + EventClass ev; + int highest_ticks; + int i; + int specified_frame_rate; + int maxahead; + + // + // If we haven't sent out at least 5 guaranteed-delivery packets, don't + // bother trying to measure our connection response time; just return. + // + if (my_sent < 5) { + return; + } + + // + // Find the highest processing time we have stored + // + highest_ticks = 0; + for (i = 0; i < MPlayerCount; i++) { + // + // If we haven't heard from all systems yet, bail out. + // + if (TheirProcessTime[i] == -1) { + return; + } + if (TheirProcessTime[i] > highest_ticks) { + highest_ticks = TheirProcessTime[i]; + } + } + + // + // Compute our "desired" frame rate as the lower of: + // - What the user has dialed into the options screen + // - What we're really able to run at + // + if (highest_ticks == 0) { + DesiredFrameRate = 60; + } else { + DesiredFrameRate = 60 / highest_ticks; + } + + if (Options.GameSpeed == 0) { + specified_frame_rate = 60; + } else { + specified_frame_rate = 60 / Options.GameSpeed; + } + + DesiredFrameRate = MIN (DesiredFrameRate, specified_frame_rate); + + // + // Measure the current connection response time. This time will be in + // 60ths of a second, and represents full round-trip time of a packet. + // To convert to one-way packet time, divide by 2; to convert to game + // frames, ....uh.... + // + resp_time = net->Response_Time(); + + // + // Compute our new 'MaxAhead' value, based upon the response time of our + // connection and our desired frame rate. + // 'MaxAhead' in frames is: + // + // (resp_time / 2 ticks) * (1 sec/60 ticks) * (n Frames / sec) + // + // resp_time is divided by 2 because, as reported, it represents a round- + // trip, and we only want to use a one-way trip. + // + maxahead = (resp_time * DesiredFrameRate) / (2 * 60); + + // + // Now, we have to round 'maxahead' so it's an even multiple of our + // send rate. It also must be at least thrice the FrameSendRate. + // (Isn't "thrice" a cool word?) + // + maxahead = ((maxahead + FrameSendRate - 1) / FrameSendRate) * FrameSendRate; + maxahead = MAX (maxahead, FrameSendRate * 3); + + ev.Type = EventClass::TIMING; + ev.Data.Timing.DesiredFrameRate = DesiredFrameRate; + ev.Data.Timing.MaxAhead = maxahead; + + OutList.Add(ev); + + // + // Adjust my connection retry timing. These values set the retry timeout + // to just over one round-trip time, the 'maxretries' to -1, and the + // connection timeout to allow for about 4 retries. + // + net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15); + +} + + +/*************************************************************************** + * Generate_Process_Time_Event -- Generates a PROCESS_TIME event * + * * + * INPUT: * + * net ptr to connection manager * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/02/1996 BRR : Created. * + *=========================================================================*/ +static void Generate_Process_Time_Event(ConnManClass *net) +{ + EventClass ev; + int avgticks; + unsigned long resp_time; // connection response time, in ticks + + // + // Measure the current connection response time. This time will be in + // 60ths of a second, and represents full round-trip time of a packet. + // To convert to one-way packet time, divide by 2; to convert to game + // frames, ....uh.... + // + resp_time = net->Response_Time(); + + // + // Adjust my connection retry timing. These values set the retry timeout + // to just over one round-trip time, the 'maxretries' to -1, and the + // connection timeout to allow for about 4 retries. + // + net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15); + + if (IsMono) { + MonoClass::Enable(); + Mono_Set_Cursor(0,23); + Mono_Printf("Processing Ticks:%03d Frames:%03d\n", ProcessTicks,ProcessFrames); + MonoClass::Disable(); + } + + avgticks = ProcessTicks / ProcessFrames; + + ev.Type = EventClass::PROCESS_TIME; + ev.Data.ProcessTime.AverageTicks = avgticks; +char flip[128]; +sprintf (flip, "C&C95 - Sending PROCESS_TIME packet of %04x ticks\n", ev.Data.ProcessTime.AverageTicks); +CCDebugString (flip); + + OutList.Add(ev); + + ProcessTicks = 0; + ProcessFrames = 0; +} + + +/*************************************************************************** + * Process_Send_Period -- timing for sending packets every 'n' frames * + * * + * This function is for a CommProtocol of COMM_PROTOCOL_MULTI_E_COMP only. * + * It determines if it's time to send a packet or not. * + * * + * INPUT: * + * net ptr to connection manager * + * * + * OUTPUT: * + * 1 = it's time to send a packet; 0 = don't send a packet this frame. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Process_Send_Period(ConnManClass *net) +{ + //------------------------------------------------------------------------ + // If the current frame # is not an even multiple of 'FrameSendRate', then + // it's not time to send a packet; just return. + //------------------------------------------------------------------------ + if (Frame != (((Frame + (FrameSendRate - 1)) / + FrameSendRate) * FrameSendRate) ) { + + net->Service(); + + if (IsMono) { + MonoClass::Disable(); + } + + return (0); + } + + return (1); + +} // end of Process_Send_Period + + +/*************************************************************************** + * Send_Packets -- sends out events from the OutList * + * * + * This routine computes how many events can be sent this frame, and then * + * builds the "meta-packet" & sends it. * + * * + * The 'cap' value is the max # of events we can send. Ideally, it should * + * be based upon the bandwidth of our connection. Currently, it's just * + * hardcoded to prevent the modem from having to resend "too much" data, * + * which is about 200 bytes per frame. * + * * + * INPUT: * + * net ptr to connection manager * + * multi_packet_buf buffer to store packets in * + * multi_packet_max max size of multi_packet_buf * + * max_ahead current game MaxAhead value * + * my_sent # commands I've sent this game * + * * + * OUTPUT: * + * # events sent, NOT including the FRAMEINFO event * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Send_Packets(ConnManClass *net, char *multi_packet_buf, + int multi_packet_max, int max_ahead, int my_sent) +{ + int cap; // max # events to send, NOT including FRAMEINFO event + int do_once; // true: only go through packet loop once + int ack_req; // 0 = no ack required on outgoing packet + int packetlen; // size of meta-packet sent + + //------------------------------------------------------------------------ + // Determine how many events it's OK to send this frame. + //------------------------------------------------------------------------ + //........................................................................ + // If we have 4 or more packets queue'd for sending, don't add any more + // this frame. + //........................................................................ + if (net->Private_Num_Send() >= 4) { + cap = 0; + do_once = 1; + } + //........................................................................ + // If there are 2 or more packets queued, the entire packet we send must + // fit within a single ComQueue buffer, so limit # events to 5. + // (The Modem connection manager has a max buffer size of 200 bytes, which + // is large enough for 6 uncompressed events, which leaves room for 5 + // events plus a FRAMEINFO.) + //........................................................................ + else if (net->Private_Num_Send() >= 2) { + cap = 5; + do_once = 1; + + } + //........................................................................ + // Otherwise, just send all events in the OutList + //........................................................................ + else { + cap = OutList.Count; + do_once = 0; + } + //........................................................................ + // Make sure we aren't sending more events than are in the OutList + //........................................................................ + if (cap > OutList.Count) { + cap = OutList.Count; + } + + //........................................................................ + // Make sure we don't send so many events that our DoList fills up + //........................................................................ + if (cap > (MAX_EVENTS * 8) - DoList.Count) { + cap = (MAX_EVENTS * 8) - DoList.Count; + } + + /* + ** No cap for internet game + ** + ** Or for serial games for that matter ST - 5/31/96 4:00PM + */ + if (GameToPlay == GAME_INTERNET || GameToPlay == GAME_MODEM || GameToPlay == GAME_NULL_MODEM){ + cap = OutList.Count; + do_once = 0; + } + + //------------------------------------------------------------------------ + // Build our meta-packet & transmit it. + //------------------------------------------------------------------------ + while (1) { + + Update_Queue_Mono (net, 1); + + //..................................................................... + // If there are no commands this frame, we'll just be sending a FRAMEINFO + // packet; no ack is required. For the modem's sake, check + // MPlayerCount; no ACK is needed if we're just sending to someone + // who's left the game. + //..................................................................... + if (cap == 0 || OutList.Count == 0 || MPlayerCount == 1) { + ack_req = 0; + } + else { + ack_req = 1; + } + + //..................................................................... + // Build & send out our message + //..................................................................... + packetlen = Build_Send_Packet (multi_packet_buf, multi_packet_max, + max_ahead, my_sent, cap); + net->Send_Private_Message (multi_packet_buf, packetlen, ack_req); + + //..................................................................... + // Call Service() to actually send the packet + //..................................................................... + net->Service(); + + //..................................................................... + // Stop if there's no more data to send, or if our send queue is + // filling up. + //..................................................................... + if (OutList.Count == 0 || do_once) { + break; + } + } + + return (cap); + +} // end of Send_Packets + + +/*************************************************************************** + * Send_FrameSync -- Sends a FRAMESYNC packet * + * * + * This routine is used to periodically remind the other systems that * + * we're still here, and to tell them what frame # we're on, in case * + * they've missed my FRAMEINFO packets. * + * * + * INPUT: * + * net ptr to connection manager * + * cmd_count # commands I've sent so far * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Send_FrameSync(ConnManClass *net, int cmd_count) +{ + EventClass packet; + + //------------------------------------------------------------------------ + // Build a frame-sync event to send. FRAMESYNC packets contain a + // scenario-based CRC rather than a game-state-based CRC, to let the + // games compare scenario CRC's on startup. + //------------------------------------------------------------------------ + memset (&packet, 0, sizeof(EventClass)); + packet.Type = EventClass::FRAMESYNC; + if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + packet.Frame = ((Frame + MPlayerMaxAhead + (FrameSendRate - 1)) / + FrameSendRate) * FrameSendRate; + } + else { + packet.Frame = Frame + MPlayerMaxAhead; + } + packet.ID = Houses.ID(PlayerPtr); + packet.MPlayerID = MPlayerLocalID; + packet.Data.FrameInfo.CRC = ScenarioCRC; + packet.Data.FrameInfo.CommandCount = cmd_count; + packet.Data.FrameInfo.Delay = MPlayerMaxAhead; + + //------------------------------------------------------------------------ + // Send the event. For modem, this just sends to the other player; + // for network, it sends to everyone we're connected to. + //------------------------------------------------------------------------ + net->Send_Private_Message (&packet, (offsetof(EventClass, Data) + + size_of(EventClass, Data.FrameInfo)), 0 ); + + return; + +} // end of Send_FrameSync + + +/*************************************************************************** + * Process_Receive_Packet -- processes an incoming packet * + * * + * This routine receives a packet from another system, adds it to our * + * execution queue (the DoList), and updates my arrays of their frame #, * + * their commands-sent, and their commands-received. * + * * + * INPUT: * + * net ptr to connection manager * + * multi_packet_buf buffer containing packet(s) to parse * + * id id of sender * + * their_frame array containing frame #'s of other players * + * their_sent array containing command count of other players * + * their_recv array containing # recv'd cmds from other players * + * * + * OUTPUT: * + * RC_NORMAL: nothing unusual happened, although * + * their_sent or their_recv may have been * + * altered * + * RC_PLAYER_READY: player has been heard from for the 1st time; * + * this presumes that his original * + * 'their_frame[]' value was -1 when this * + * routine was called * + * RC_SCENARIO_MISMATCH: FRAMEINFO scenario CRC doesn't match; * + * normally only applies after loading a new * + * scenario or save-game * + * RC_DOLIST_FULL: fatal error; unable to add events to DoList * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static RetcodeType Process_Receive_Packet(ConnManClass *net, + char *multi_packet_buf, int id, int packetlen, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv) +{ + EventClass *event; + int index; + RetcodeType retcode = RC_NORMAL; + int i; + + //------------------------------------------------------------------------ + // Get an event ptr to the incoming message + //------------------------------------------------------------------------ + event = (EventClass *)multi_packet_buf; + + //------------------------------------------------------------------------ + // Get the index of the sender + //------------------------------------------------------------------------ + index = net->Connection_Index(id); + + //------------------------------------------------------------------------ + // Compute the other player's frame # (at the time this packet was sent) + //------------------------------------------------------------------------ + if (their_frame[index] < + (int)(event->Frame - event->Data.FrameInfo.Delay)) { + + //..................................................................... + // If the original frame # for this player is -1, it means we've heard + // from this player for the 1st time; return the appropriate value. + //..................................................................... + if (their_frame[index]==-1) { + retcode = RC_PLAYER_READY; + } + + their_frame[index] = event->Frame - event->Data.FrameInfo.Delay; + } + + //------------------------------------------------------------------------ + // Extract the other player's CommandCount. This count will include + // the commands in this packet, if there are any. + //------------------------------------------------------------------------ + if (event->Data.FrameInfo.CommandCount > their_sent[index]) { + their_sent[index] = event->Data.FrameInfo.CommandCount; + } + + //------------------------------------------------------------------------ + // If this packet was not a FRAMESYNC packet: + // - Add the events in it to our DoList + // - Increment our commands-received counter by the number of non- + // FRAMEINFO packets received + //------------------------------------------------------------------------ + if (event->Type != EventClass::FRAMESYNC) { + //..................................................................... + // Break up the packet into its component events. A returned packet + // count of -1 indicates a fatal queue-full error. + //..................................................................... + i = Breakup_Receive_Packet( multi_packet_buf, packetlen); + if (i==-1) { + return (RC_DOLIST_FULL); + } + //..................................................................... + // Compute the actual # commands in the packet by subtracting off the + // FRAMEINFO event + //..................................................................... + if ( (event->Type==EventClass::FRAMEINFO) && (i > 0)) { + i--; + } + + their_recv[index] += i; + } + + //------------------------------------------------------------------------ + // If the event was a FRAMESYNC packet, there will be no commands to add, + // but we must check the ScenarioCRC value. + //------------------------------------------------------------------------ + else if (event->Data.FrameInfo.CRC != ScenarioCRC) { + return (RC_SCENARIO_MISMATCH); + } + + return (retcode); + +} // end of Process_Receive_Packet + + +/*************************************************************************** + * Process_Serial_Packet -- Handles an incoming serial packet * + * * + * This routine is needed because the modem classes don't support a * + * "global channel" like the network classes do, but that functionality is * + * still needed for modem communications. Specifically, the modem dialogs * + * transmit their own special packets back & forth, and messages are sent * + * using a special packet type. Thus, we have to call this routine when * + * we receive a modem packet, to allow it to process messages & dialog * + * packets. * + * * + * INPUT: * + * multi_packet_buf packet buffer to process * + * first_time 1 = this is the 1st game frame * + * * + * OUTPUT: * + * RC_NORMAL: this wasn't a SERIAL-type packet * + * RC_SERIAL_PROCESSED: this was a SERIAL-type packet, and was * + * processed; the other player is still connected, * + * even if he's not in the game. * + * RC_PLAYER_LEFT: other player has left the game * + * RC_HUNG_UP: we're getting our own packets back; thus, the * + * modem is mirroring our packets, which means the * + * modem hung up! * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static RetcodeType Process_Serial_Packet(char *multi_packet_buf, + int first_time) +{ + SerialPacketType *serial_packet; // for parsing serial packets + int player_gone; + EventClass *event; + char txt[MAX_MESSAGE_LENGTH+80]; + unsigned short magic_number; + unsigned short crc; + + //------------------------------------------------------------------------ + // Determine if this packet means that the other player has left the game + //------------------------------------------------------------------------ + serial_packet = (SerialPacketType *)multi_packet_buf; + player_gone = 0; + //........................................................................ + // On Frame 0, only a SIGN_OFF means the other player left; the other + // packet types may be left over from a previous session. + //........................................................................ + if (first_time) { + if (serial_packet->Command == SERIAL_SIGN_OFF) { + player_gone = 1; + } + } + //........................................................................ + // On subsequent frames, any of SIGN_OFF, TIMING, or SCORE_SCREEN means + // the other player is gone. + //........................................................................ + else { + if (serial_packet->Command == SERIAL_SIGN_OFF || + serial_packet->Command == SERIAL_TIMING || + serial_packet->Command == SERIAL_SCORE_SCREEN ) { + player_gone = 1; + } + } + if (player_gone) { + Destroy_Null_Connection(serial_packet->Color, 0); + return (RC_PLAYER_LEFT); + } + + //------------------------------------------------------------------------ + // Process an incoming message + //------------------------------------------------------------------------ + if (serial_packet->Command == SERIAL_MESSAGE) { + sprintf(txt, Text_String(TXT_FROM), serial_packet->Name, + serial_packet->Message); + + magic_number = *((unsigned short*)(serial_packet->Message + COMPAT_MESSAGE_LENGTH-4)); + crc = *((unsigned short*)(serial_packet->Message + COMPAT_MESSAGE_LENGTH-2)); + + Messages.Add_Message (txt, + MPlayerTColors[MPlayerID_To_ColorIndex(serial_packet->ID)], + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, magic_number, crc); + + //..................................................................... + // Save this message in our last-message buffer + //..................................................................... + if (strlen (serial_packet->Message)) { + strcpy (LastMessage, serial_packet->Message); + } + + //..................................................................... + // Tell the map to do a partial update (just to force the + // messages to redraw). + //..................................................................... + Map.Flag_To_Redraw(false); + return (RC_SERIAL_PROCESSED); + } + + //------------------------------------------------------------------------ + // Any other SERIAL-type packet means the other player is still there; + // throw them away, but let the caller know the connection is OK. + //------------------------------------------------------------------------ + if ( (serial_packet->Command >= SERIAL_CONNECT && + serial_packet->Command < SERIAL_LAST_COMMAND) || + MPlayerCount == 1) { + return (RC_SERIAL_PROCESSED); + } + + //........................................................................ + // are we getting our own packets back?? + //........................................................................ + event = (EventClass *)multi_packet_buf; + if (event->ID == Houses.ID(PlayerPtr)) { + return (RC_HUNG_UP); + } + + return (RC_NORMAL); + +} // end of Process_Serial_Packet + + +/*************************************************************************** + * Can_Advance -- determines if it's OK to advance to the next frame * + * * + * This routine uses the current values stored in their_frame[], * + * their_send[], and their_recv[] to see if it's OK to advance to the next * + * game frame. We must not advance if: * + * - If our frame # would be too far ahead of the slowest player (the * + * lowest their_frame[] value). "Too far" means * + * (Frame > their_frame + MaxAhead). * + * - our current command count doesn't match the sent command count of one * + * other player (meaning that we've missed a command packet from that * + * player, and thus the frame # we're receiving from him may be due to a * + * FRAMEINFO packet sent later than the command, so we shouldn't use * + * this frame # to see if we should advance; we should wait until we * + * have all the commands before we advance. * + * * + * Of course, this routine assumes the values in their_frame[] etc are * + * kept current by the caller. * + * * + * INPUT: * + * net ptr to connection manager * + * max_ahead max frames ahead * + * their_frame array of their frame #'s * + * their_sent array of their sent command count * + * their_recv array of their # received commands * + * * + * OUTPUT: * + * 1 = OK to advance; 0 = not OK * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Can_Advance(ConnManClass *net, int max_ahead, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv) +{ + long their_oldest_frame; // other players' oldest frame # + int count_ok; // true = my cmd count matches theirs + int i; + + //------------------------------------------------------------------------ + // Special case for modem: if the other player has left, go ahead and + // advance to the next frame; don't wait on him. + //------------------------------------------------------------------------ + if (MPlayerCount == 1) { + return (1); + } + //------------------------------------------------------------------------ + // Find the oldest frame # in 'their_frame' + //------------------------------------------------------------------------ + their_oldest_frame = Frame + 1000; + for (i = 0; i < net->Num_Connections(); i++) { + if (their_frame[i] < their_oldest_frame) + their_oldest_frame = their_frame[i]; + } + + //------------------------------------------------------------------------ + // I can advance to the next frame IF: + // 1) I'm less than a one-way propogation delay ahead of the other + // players' frame numbers, AND + // 2) their_recv[i] >= their_sent[i] (ie I've received all the commands + // the other players have sent so far). + //------------------------------------------------------------------------ + count_ok = 1; + for (i = 0; i < net->Num_Connections(); i++) { + if (their_recv[i] < their_sent[i]) { + count_ok = 0; + break; + } + } + if (count_ok && (Frame < (their_oldest_frame + max_ahead))) { + return (1); + } + + return (0); + +} // end of Can_Advance + + +/*************************************************************************** + * Process_Reconnect_Dialog -- processes the reconnection dialog * + * * + * This routine [re]draws the reconnection dialog; if 'reconn' is set, * + * it tells the user who we're trying to reconnect to; otherwise, is just * + * says something generic like "Waiting for connections". * + * * + * INPUT: * + * timeout_timer ptr to count down timer, showing time remaining * + * their_frame array of other players' frame #'s * + * num_conn # connections in 'their_frame' * + * reconn 1 = reconnect, 0 = waiting for first-time connection * + * fresh 1 = draw from scratch, 0 = only update time counter * + * * + * OUTPUT: * + * 1 = user wants to cancel, 0 = not * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Process_Reconnect_Dialog(CountDownTimerClass *timeout_timer, + long *their_frame, int num_conn, int reconn, int fresh) +{ + static int displayed_time = 0; // time value currently displayed + int new_time; + int oldest_index; // index of person requiring a reconnect + int i,j; + + //------------------------------------------------------------------------ + // Convert the timer to seconds + //------------------------------------------------------------------------ + new_time = timeout_timer->Time() / 60; + + //-------------------------------------------------------------------------------- + // If we have just received input focus again after running in the background then + // we need to redraw the whole dialog. + //-------------------------------------------------------------------------------- + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + fresh = true; + } + + //------------------------------------------------------------------------ + // If the timer has changed, or 'fresh' is set, redraw the dialog + //------------------------------------------------------------------------ + if (fresh || (new_time != displayed_time)) { + //..................................................................... + // Find the index of the person we're trying to reconnect to + //..................................................................... + if (reconn) { + j = 0x7fffffff; + oldest_index = 0; + for (i = 0; i < num_conn; i++) { + if (their_frame[i] < j) { + j = their_frame[i]; + oldest_index = i; + } + } + } + Net_Reconnect_Dialog(reconn, fresh, oldest_index, new_time); + } + displayed_time = new_time; + + //........................................................................ + // If user hits ESC, bail out + //........................................................................ + if (Check_Key()) { + if (Get_Key_Num()==KN_ESC) { + return (1); + } + } + + return (0); + +} // end of Process_Reconnect_Dialog + + +/*************************************************************************** + * Handle_Timeout -- handles a timeout in the wait-for-players loop * + * * + * This routine "gracefully" handles a timeout in the frame-sync loop. * + * The timeout must be handled differently by a modem game or network * + * game. * + * * + * The modem game must detect if the other player is still connected * + * physically, even if he's not playing the game any more; if so, this * + * routine returns an OK status. If the other player isn't even * + * physically connected, an error is returned. * + * * + * The network game must find the connection that's causing the timeout, * + * and destroy it. The game continues, even if there are no more human * + * players left. * + * * + * INPUT: * + * net ptr to connection manager * + * their_frame array containing frame #'s of other players * + * their_sent array containing command count of other players * + * their_recv array containing # recv'd cmds from other players * + * * + * OUTPUT: * + * 1 = it's OK; reset timeout timers & keep processing * + * 0 = game over, man * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Handle_Timeout(ConnManClass *net, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv) +{ + int oldest_index; // index of person requiring a reconnect + int i,j; + int id; + + //------------------------------------------------------------------------ + // For modem, attempt to reconnect; if that fails, save the game & bail. + //------------------------------------------------------------------------ + if (GameToPlay == GAME_MODEM || GameToPlay == GAME_NULL_MODEM) { + if ( net->Num_Connections() ) { + if (!Reconnect_Modem()) { + return (0); + } + else { + return (1); + } + } + } + + //------------------------------------------------------------------------ + // For network, destroy the oldest connection + //------------------------------------------------------------------------ + else if (GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) { + j = 0x7fffffff; + oldest_index = 0; + for (i = 0; i < net->Num_Connections(); i++) { + if (their_frame[i] < j) { + j = their_frame[i]; + oldest_index = i; + } + } + + id = net->Connection_ID(oldest_index); + + /* + ** Send the game statistics packet now if the game is effectivly over + */ + if (MPlayerCount == 2 && + GameToPlay == GAME_INTERNET && + !GameStatisticsPacketSent){ + Register_Game_End_Time(); + ConnectionLost = true; + Send_Statistics_Packet(); + } + + if (id != ConnManClass::CONNECTION_NONE) { + for (i = oldest_index; i < net->Num_Connections() - 1; i++) { + their_frame[i] = their_frame[i+1]; + their_sent[i] = their_sent[i+1]; + their_recv[i] = their_recv[i+1]; + } + CCDebugString ("C&C95 = Destroying connection due to time out\n"); + Destroy_Connection(id,1); + } + } + + return (1); + +} // end of Handle_Timeout + + +/*************************************************************************** + * Stop_Game -- stops the game * + * * + * This routine clears any global flags that need it, in preparation for * + * halting the game prematurely. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/22/1995 BRR : Created. * + *=========================================================================*/ +static void Stop_Game(void) +{ + CCDebugString ("C&C95 - In Stop_Game.\n"); + GameActive = 0; + if (IsMono) { + MonoClass::Disable(); + } + + if (GameToPlay == GAME_INTERNET){ + ConnectionLost = true; + CCDebugString ("C&C95 - About to send statistics packet.\n"); + Register_Game_End_Time(); + Send_Statistics_Packet(); + CCDebugString ("C&C95 - Returned from sending stats packet.\n"); + } + + return; + +} // end of Stop_Game + + +/*************************************************************************** + * Build_Send_Packet -- Builds a big packet from a bunch of little ones. * + * * + * This routine takes events from the OutList, and puts them into a * + * "meta-packet", which is transmitted to all systems we're connected to. * + * Also, these events are added to our own DoList. * + * * + * Every Meta-Packet we send uses a FRAMEINFO packet as a header; this * + * tells the other systems what frame we're on, as well as serving as a * + * standard packet header. * + * * + * INPUT: * + * buf buffer to store packet in * + * bufsize max size of buffer * + * frame_delay desired frame delay to attach to all outgoing packets * + * num_cmds value to use for the CommandCount field * + * cap max # events to send * + * * + * OUTPUT: * + * new size of packet * + * * + * WARNINGS: * + * 'num_cmds' should be the total of of commands, including all those sent * + * this frame! * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Build_Send_Packet(void *buf, int bufsize, int frame_delay, + int num_cmds, int cap) +{ + int size = 0; + EventClass *finfo; + + //------------------------------------------------------------------------ + // All events start with a FRAMEINFO event; fill this part in. + //------------------------------------------------------------------------ + //........................................................................ + // Set the event type + //........................................................................ + finfo = (EventClass *)buf; + finfo->Type = EventClass::FRAMEINFO; + //........................................................................ + // Set the frame to execute this event on; this is protocol-specific + //........................................................................ + if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + finfo->Frame = ((Frame + frame_delay + (FrameSendRate - 1)) / + FrameSendRate) * FrameSendRate; + } + else { + finfo->Frame = Frame + frame_delay; + } + //........................................................................ + // Fill in the rest of the event + //........................................................................ + finfo->ID = Houses.ID(PlayerPtr); + finfo->MPlayerID = MPlayerLocalID; + finfo->Data.FrameInfo.CRC = GameCRC; + finfo->Data.FrameInfo.CommandCount = num_cmds; + finfo->Data.FrameInfo.Delay = frame_delay; + + //------------------------------------------------------------------------ + // Initialize the # of bytes processed; this is protocol-specific + //------------------------------------------------------------------------ + if (CommProtocol==COMM_PROTOCOL_SINGLE_NO_COMP) { + size += sizeof(EventClass); + } + else { + size += (offsetof(EventClass, Data) + + size_of(EventClass, Data.FrameInfo)); + } + + //------------------------------------------------------------------------ + // Transfer all events from the OutList into the DoList, building our + // packet while we go. + //------------------------------------------------------------------------ + switch (CommProtocol) { + //..................................................................... + // COMM_PROTOCOL_SINGLE_NO_COMP: + // We'll send at least a FRAMEINFO every single frame, no compression + //..................................................................... + case (COMM_PROTOCOL_SINGLE_NO_COMP): + size = Add_Uncompressed_Events(buf, bufsize, frame_delay, size, cap); + break; + + //..................................................................... + // COMM_PROTOCOL_SINGLE_E_COMP: + // Compress a group of packets into our send buffer; send out + // compressed packets every frame. + // COMM_PROTOCOL_MULTI_E_COMP: + // Compress a group of packets into our send buffer; send out + // compressed packets every 'n' frames. + //..................................................................... + case (COMM_PROTOCOL_SINGLE_E_COMP): + case (COMM_PROTOCOL_MULTI_E_COMP): + size = Add_Compressed_Events(buf, bufsize, frame_delay, size, cap); + break; + + //..................................................................... + // Default: We have no idea what to do, so do nothing. + //..................................................................... + default: + size = 0; + break; + } + + return( size ); + +} /* end of Build_Send_Packet */ + + +/*************************************************************************** + * Add_Uncompressed_Events -- adds uncompressed events to a packet * + * * + * INPUT: * + * buf buffer to store packet in * + * bufsize max size of buffer * + * frame_delay desired frame delay to attach to all outgoing packets * + * size current packet size * + * cap max # events to process * + * * + * OUTPUT: * + * new size value * + * * + * WARNINGS: * + * This routine MUST check to be sure it doesn't overflow the buffer. * + * * + * HISTORY: * + * 11/21/1995 DRD : Created. * + *=========================================================================*/ +static int Add_Uncompressed_Events(void *buf, int bufsize, int frame_delay, + int size, int cap) +{ + int num = 0; // # of events processed + int ev_size; // size of event we're adding + + //------------------------------------------------------------------------ + // Loop until there are no more events, or we've processed our max # of + // events, or the buffer is full. + //------------------------------------------------------------------------ + while (OutList.Count && (num < cap)) { + ev_size = sizeof(EventClass); + //..................................................................... + // Will the next event exceed the size of the buffer? If so, break. + //..................................................................... + if ( (size + ev_size) > bufsize ) { + return (size); + } + + //..................................................................... + // Set the event's frame delay + //..................................................................... + OutList.First().Frame = Frame + frame_delay; + + //..................................................................... + // Set the event's ID + //..................................................................... + OutList.First().ID = Houses.ID(PlayerPtr); + OutList.First().MPlayerID = MPlayerLocalID; + + //..................................................................... + // Transfer the event in OutList to DoList, un-queue the OutList + // event. If the DoList is full, stop transferring immediately. + //..................................................................... + OutList.First().IsExecuted = 0; + if (!DoList.Add(OutList.First())) { + return (size); + } + + //..................................................................... + // Add event to the send packet + //..................................................................... + memcpy ( ((char *)buf) + size, &OutList.First(), sizeof(EventClass) ); + size += sizeof(EventClass); + + //..................................................................... + // Increment our event counter; delete the last event from the queue + //..................................................................... + num++; + OutList.Next(); + } + + return (size); + +} // end of Add_Uncompressed_Events + + +/*************************************************************************** + * Add_Compressed_Events -- adds an compressed events to a packet * + * * + * INPUT: * + * buf buffer to store packet in * + * bufsize max size of buffer * + * frame_delay desired frame delay to attach to all outgoing packets * + * size reference to current packet size * + * cap max # events to process * + * * + * OUTPUT: * + * new size value * + * * + * WARNINGS: * + * This routine MUST check to be sure it doesn't overflow the buffer. * + * * + * HISTORY: * + * 11/21/1995 DRD : Created. * + *=========================================================================*/ +static int Add_Compressed_Events(void *buf, int bufsize, int frame_delay, + int size, int cap) +{ + int num = 0; // # of events processed + EventClass::EventType eventtype; // type of event being compressed + EventClass prevevent; // last event processed + int datasize; // size of element plucked from event union + int storedsize; // actual # bytes stored from event + unsigned char *unitsptr = NULL; // ptr to buffer pos to store mega. rep count + unsigned char numunits = 0; // megamission rep count value + bool missiondup = false; // flag: is this event a megamission repeat? + + //------------------------------------------------------------------------ + // clear previous event + //------------------------------------------------------------------------ + memset (&prevevent, 0, sizeof(EventClass)); + + //------------------------------------------------------------------------ + // Loop until there are no more events, we've processed our max # of + // events, or the buffer is full. + //------------------------------------------------------------------------ + while (OutList.Count && (num < cap)) { + eventtype = OutList.First().Type; + datasize = EventClass::EventLength[ eventtype ]; + //..................................................................... + // For a variable-sized event, pull the size from the event; otherwise, + // the size will be the data element size plus the event type value. + //..................................................................... + storedsize = datasize + sizeof (EventClass::EventType); + + //..................................................................... + // MegaMission compression: MegaMissions are stored as: + // EventType + // Rep Count + // MegaMission structure (event # 1 only) + // Whom #2 + // Whom #3 + // Whom #4 + // ... + // Whom #n + //..................................................................... + if (prevevent.Type == EventClass::MEGAMISSION) { + //.................................................................. + // If previous & current events are both MegaMissions: + //.................................................................. + if (eventtype == EventClass::MEGAMISSION) { + //............................................................... + // If the Mission, Target, & Destination are the same, compress + // the events into one: + // - Change datasize to the size of the 'Whom' field only + // - set total # bytes to store to the size of the 'Whom' only + // - increment the MegaMission rep count + // - set the MegaMission rep flag + //............................................................... + if (OutList.First().Data.MegaMission.Mission == + prevevent.Data.MegaMission.Mission && + OutList.First().Data.MegaMission.Target == + prevevent.Data.MegaMission.Target && + OutList.First().Data.MegaMission.Destination == + prevevent.Data.MegaMission.Destination) { + + datasize = sizeof(prevevent.Data.MegaMission.Whom); + storedsize = datasize; + numunits++; + missiondup = true; + } + //............................................................... + // Data doesn't match; start a new run of MegaMissions: + // - Store previous MegaMission rep count + // - Init 'unitsptr' to buffer pos after next EventType + // - set total # bytes to store to 'datasize' + sizeof(EventType) + + // sizeof (numunits) + // - init the MegaMission rep count to 1 + // - clear the MegaMission rep flag + //............................................................... + else { + *unitsptr = numunits; + unitsptr = ((unsigned char *)buf) + size + + sizeof(EventClass::EventType); + storedsize += sizeof(numunits); + numunits = 1; + missiondup = false; + } + } + //.................................................................. + // Previous event was a MegaMission, but this one isn't: end the + // run of MegaMissions: + // - Store previous MegaMission rep count + // - Clear variables + //.................................................................. + else { + *unitsptr = numunits; // save # events in our run + unitsptr = NULL; // init other values + numunits = 0; + missiondup = false; + } + } + + //..................................................................... + // The previous event is not a MEGAMISSION but the current event is: + // Set up a new run of MegaMissions: + // - Init 'unitsptr' to buffer pos after next EventType + // - set total # bytes to store to 'datasize' + sizeof(EventType) + + // sizeof (numunits) + // - init the MegaMission rep count to 1 + // - clear the MegaMission rep flag + //..................................................................... + else if (eventtype == EventClass::MEGAMISSION) { + unitsptr = ((unsigned char *)buf) + size + + sizeof(EventClass::EventType); + storedsize += sizeof(numunits); + numunits = 1; + missiondup = false; + } + + //..................................................................... + // Will the next event exceed the size of the buffer? If so, + // stop compressing. + //..................................................................... + if ( (size + storedsize) > bufsize ) + break; + + //..................................................................... + // Set the event's frame delay (this is protocol-dependent) + //..................................................................... + if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + OutList.First().Frame = ((Frame + frame_delay + + (FrameSendRate - 1)) / FrameSendRate) * + FrameSendRate; + } + else { + OutList.First().Frame = Frame + frame_delay; + } + + //..................................................................... + // Set the event's ID + //..................................................................... + OutList.First().ID = Houses.ID(PlayerPtr); + OutList.First().MPlayerID = MPlayerLocalID; + + //..................................................................... + // Transfer the event in OutList to DoList, un-queue the OutList event. + // If the DoList is full, stop transferring immediately. + //..................................................................... + OutList.First().IsExecuted = 0; + if ( !DoList.Add( OutList.First() ) ) { + break; + } + + //--------------------------------------------------------------------- + // Compress the event into the send packet buffer + //--------------------------------------------------------------------- + switch ( eventtype ) { + //.................................................................. + // RESPONSE_TIME: just use the Delay field of the FrameInfo union + //.................................................................. + case (EventClass::RESPONSE_TIME): + + *(EventClass::EventType *)( ((char *)buf) + size) = eventtype; + + memcpy ( ((char *)buf) + size + sizeof(EventClass::EventType), + &OutList.First().Data.FrameInfo.Delay, datasize ); + + size += (datasize + sizeof(EventClass::EventType)); + break; + + //.................................................................. + // MEGAMISSION: + //.................................................................. + case (EventClass::MEGAMISSION): + //............................................................... + // Repeated mission in a run: + // - Update the rep count (in case we break out) + // - Copy the Whom field only + //............................................................... + if (missiondup) { + *unitsptr = numunits; + + memcpy ( ((char *)buf) + size, + &OutList.First().Data.MegaMission.Whom, datasize ); + + size += datasize; + } + //............................................................... + // 1st mission in a run: + // - Init the rep count (in case we break out) + // - Set the EventType + // - Copy the MegaMission structure, leaving room for 'numunits' + //............................................................... + else { + *unitsptr = numunits; + + *(EventClass::EventType *)( ((char *)buf) + size) = eventtype; + + memcpy ( ((char *)buf) + size + + sizeof(EventClass::EventType) + sizeof(numunits), + &OutList.First().Data.MegaMission, datasize ); + + size += (datasize + sizeof(EventClass::EventType) + sizeof(numunits)); + } + break; + + //.................................................................. + // Default case: Just copy over the data field from the union + //.................................................................. + default: + *(EventClass::EventType *)( ((char *)buf) + size) = eventtype; + + memcpy ( ((char *)buf) + size + sizeof(EventClass::EventType), + &OutList.First().Data, datasize ); + + size += (datasize + sizeof(EventClass::EventType)); + + break; + } + + //--------------------------------------------------------------------- + // update # events processed + //--------------------------------------------------------------------- + num++; + + //--------------------------------------------------------------------- + // Update 'prevevent' + //--------------------------------------------------------------------- + memcpy ( &prevevent, &OutList.First(), sizeof(EventClass) ); + + //--------------------------------------------------------------------- + // Go to the next event to process + //--------------------------------------------------------------------- + OutList.Next(); + } + + return (size); + +} // end of Add_Compressed_Events + + +/*************************************************************************** + * Breakup_Receive_Packet -- Splits a big packet into little ones. * + * * + * INPUT: * + * buf buffer to break up * + * bufsize length of buffer * + * * + * OUTPUT: * + * # events added to queue, -1 if fatal error (queue is full) * + * (return value includes any FRAMEINFO packets encountered; * + * FRAMESYNC's are ignored) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Breakup_Receive_Packet(void *buf, int bufsize ) +{ + int count = 0; + + /* + ** is there enough leftover for another record + */ + switch (CommProtocol) { + case (COMM_PROTOCOL_SINGLE_NO_COMP): + count = Extract_Uncompressed_Events(buf, bufsize); + break; + + default: + count = Extract_Compressed_Events(buf, bufsize); + break; + } + + return (count); + +} /* end of Breakup_Receive_Packet */ + + +/*************************************************************************** + * Extract_Uncompressed_Events -- extracts events from a packet * + * * + * INPUT: * + * buf buffer containing events to extract * + * bufsize length of 'buf' * + * * + * OUTPUT: * + * # events extracted * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 DRD : Created. * + *=========================================================================*/ +static int Extract_Uncompressed_Events(void *buf, int bufsize) +{ + int count = 0; + int pos = 0; + int leftover = bufsize; + EventClass *event; + + //------------------------------------------------------------------------ + // Loop until there are no more events in the packet + //------------------------------------------------------------------------ + while (leftover >= sizeof(EventClass) ) { + event = (EventClass *)(((char *)buf) + pos); + + //..................................................................... + // add event to the DoList, only if it's not a FRAMESYNC + // (but FRAMEINFO's do get added.) + //..................................................................... + if (event->Type != EventClass::FRAMESYNC) { + event->IsExecuted = 0; + + if (!DoList.Add( *event )) { + return (-1); + } + + //.................................................................. + // Keep count of how many events we add to the queue + //.................................................................. + count++; + } + + //..................................................................... + // Point to the next position in the buffer; decrement our 'leftover' + //..................................................................... + pos += sizeof(EventClass); + leftover -= sizeof(EventClass); + } + + return (count); + +} // end of Extract_Uncompressed_Events + + +/*************************************************************************** + * Extract_Compressed_Events -- extracts events from a packet * + * * + * INPUT: * + * buf buffer containing events to extract * + * bufsize length of 'buf' * + * * + * OUTPUT: * + * # events extracted * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 DRD : Created. * + *=========================================================================*/ +static int Extract_Compressed_Events(void *buf, int bufsize) +{ + int pos = 0; // current buffer parsing position + int leftover = bufsize; // # bytes left to process + EventClass *event; // event ptr for parsing buffer + int count = 0; // # events processed + int datasize = 0; // size of data to copy + EventClass eventdata; // stores Frame, ID, etc + unsigned char numunits = 0; // # units stored in compressed MegaMissions +//int lasteventtype=0; + //------------------------------------------------------------------------ + // Clear work event structure + //------------------------------------------------------------------------ + memset (&eventdata, 0, sizeof(EventClass)); + + //------------------------------------------------------------------------ + // Assume the first event is a FRAMEINFO event + // Init 'datasize' to the amount of data to copy, minus the EventType value + // For the 1st packet only, this will include all info before the Data + // union, plus the size of the FrameInfo structure, minus the EventType size. + //------------------------------------------------------------------------ + datasize = (offsetof(EventClass, Data) + + size_of(EventClass, Data.FrameInfo)) - sizeof(EventClass::EventType); + event = (EventClass *)(((char *)buf) + pos); + + while (leftover >= (datasize + sizeof(EventClass::EventType)) ) { + //..................................................................... + // add event to the DoList, only if it's not a FRAMESYNC + // (but FRAMEINFO's do get added.) + //..................................................................... + if (event->Type != EventClass::FRAMESYNC) { + //.................................................................. + // initialize the common data from the FRAMEINFO event + // keeping IsExecuted 0 + //.................................................................. + if (event->Type == EventClass::FRAMEINFO) { + eventdata.Frame = event->Frame; + eventdata.ID = event->ID; + eventdata.MPlayerID = event->MPlayerID; + + //............................................................... + // Adjust position past the common data + //............................................................... + pos += (offsetof(EventClass, Data) - + sizeof(EventClass::EventType)); + leftover -= (offsetof(EventClass, Data) - + sizeof(EventClass::EventType)); + } + //.................................................................. + // if MEGAMISSION event get the number of units (events to generate) + //.................................................................. + else if (event->Type == EventClass::MEGAMISSION) { + numunits = *(((unsigned char *)buf) + pos + sizeof(eventdata.Type)); + pos += sizeof(numunits); + leftover -= sizeof(numunits); + } + + //.................................................................. + // clear the union data portion of the event + //.................................................................. + memset (&eventdata.Data, 0, sizeof(eventdata.Data)); + eventdata.Type = event->Type; + datasize = EventClass::EventLength[ eventdata.Type ]; + + switch (eventdata.Type) { + case (EventClass::RESPONSE_TIME): + memcpy ( &eventdata.Data.FrameInfo.Delay, + ((char *)buf) + pos + sizeof(EventClass::EventType), + datasize ); + break; + + case (EventClass::MEGAMISSION): + memcpy ( &eventdata.Data.MegaMission, + ((char *)buf) + pos + sizeof(EventClass::EventType), + datasize ); + + if (numunits > 1) { + pos += (datasize + sizeof(EventClass::EventType)); + leftover -= (datasize + sizeof(EventClass::EventType)); + datasize = sizeof(eventdata.Data.MegaMission.Whom); + + while (numunits) { + if ( !DoList.Add( eventdata ) ) { + return (-1); + } + + //...................................................... + // Keep count of how many events we add to the queue + //...................................................... + count++; + numunits--; + memcpy ( &eventdata.Data.MegaMission.Whom, + ((char *)buf) + pos, datasize ); + + //...................................................... + // if one unit left fall thru to normal code + //...................................................... + if (numunits == 1) { + datasize -= sizeof(EventClass::EventType); + break; + } + else { + pos += datasize; + leftover -= datasize; + } + } + } + break; + + default: + memcpy ( &eventdata.Data, + ((char *)buf) + pos + sizeof(EventClass::EventType), + datasize ); + break; + } + +char flip[128]; +sprintf (flip, "C&C95 - Adding event type %d to queue\n", eventdata.Type); +CCDebugString (flip); + +//if (lasteventtype == 11){ +// break; +//} + +//lasteventtype = (int) eventdata.Type; + + + if ( !DoList.Add( eventdata ) ) { + return (-1); + } + + //.................................................................. + // Keep count of how many events we add to the queue + //.................................................................. + count++; + + pos += (datasize + sizeof(EventClass::EventType)); + leftover -= (datasize + sizeof(EventClass::EventType)); + + if (leftover) { + event = (EventClass *)(((char *)buf) + pos); + datasize = EventClass::EventLength[ event->Type ]; + if (event->Type == EventClass::MEGAMISSION) { + datasize += sizeof(numunits); + } + } + } + //..................................................................... + // FRAMESYNC event: This >should< be the only event in the buffer, + // and it will be uncompressed. + //..................................................................... + else { + pos += (datasize + sizeof(EventClass::EventType)); + leftover -= (datasize + sizeof(EventClass::EventType)); + event = (EventClass *)(((char *)buf) + pos); + + //.................................................................. + // size of FRAMESYNC event - EventType size + //.................................................................. + datasize = (offsetof(EventClass, Data) + + size_of(EventClass, Data.FrameInfo)) - + sizeof(EventClass::EventType); + } + } + + return (count); + +} // end of Extract_Compressed_Events + +#endif //DEMO + +/*************************************************************************** + * Execute_DoList -- Executes commands from the DoList * + * * + * This routine executes any events in the DoList that need to be executed * + * on the current game frame. The events must be executed in a special * + * order, so that all systems execute all events in exactly the same * + * order. * + * * + * This routine also handles checking the Game CRC sent by other systems * + * against my own, to be sure we're still in sync. * + * * + * INPUT: * + * max_houses # houses to execute commands for * + * base_house HousesType to start with * + * net ptr to connection manager; NULL if none * + * skip_crc a frame-based countdown timer; if it's non-zero, the * + * CRC check will be skipped. Ignored if NULL. * + * their_frame array of their frame #'s * + * their_sent array of # commands they've sent * + * their_recv array of # commands I've received from them * + * * + * (their_xxx are ignored if 'net' is NULL.) * + * * + * OUTPUT: * + * 1 = OK, 0 = some error occurred (CRC error, packet rcv'd too late.) * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Execute_DoList(int , HousesType , + ConnManClass *net, TCountDownTimerClass *, + long *their_frame, unsigned short *their_sent, unsigned short *their_recv) +{ + int i,j,k,wibble; + int index; + + //------------------------------------------------------------------------ + // For a single-player game, just execute all events in the queue. + //------------------------------------------------------------------------ + if (GameToPlay == GAME_NORMAL) { + for (i = 0; i < DoList.Count; i++) { + if (Frame >= DoList[i].Frame && !DoList[i].IsExecuted) { + DoList[i].Execute(); // execute it + DoList[i].IsExecuted = true; // mark as having been executed + } + } + return (1); + } + +//#if(TIMING_FIX) + // + // If MPlayerMaxAhead is recomputed such that it increases, the systems + // may try to free-run to the new MaxAhead value. If so, they may miss + // an event that was generated after the TIMING event was created, but + // before it executed; this event will be scheduled with the older, + // shorter MaxAhead value. If a system doesn't receive this event, it + // may execute past the frame it's scheduled to execute on, creating + // a Packet-Recieved-Too-Late error. To prevent this, find any events + // that are scheduled to execute during this "period of vulnerability", + // and re-schedule for the end of that period. + // + if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + for (j = 0; j < DoList.Count; j++) { + if (DoList[j].Type != EventClass::FRAMEINFO && + DoList[j].Frame > NewMaxAheadFrame1 && + DoList[j].Frame < NewMaxAheadFrame2) { + DoList[j].Frame = NewMaxAheadFrame2; + } + } + } +//#endif + + //------------------------------------------------------------------------ + // Execute the DoList. Events must be executed in the same order on all + // systems; so, execute them in the order of the MPlayerID array. This + // array is stored in the same order on all systems. + //------------------------------------------------------------------------ + for (i = 0; i < MPlayerCount; i++) { + + HousesType house; + HouseClass *housep; + + house = MPlayerHouses [i]; + housep= HouseClass::As_Pointer (house); + + //..................................................................... + // If for some reason this house doesn't exist, skip it. + // Also, if this house has exited the game, skip it. (The user can + // generate events after he exits, because the exit event is scheduled + // at least FrameSendRate*3 frames ahead. If one system gets these + // packets & another system doesn't, they'll go out of sync because + // they aren't checking the CommandCount for that house, since that + // house isn't connected any more.) + //..................................................................... + if (!housep){ + continue; + } + + if (!housep->IsHuman){ + continue; + } + + //..................................................................... + // Loop through all events + //..................................................................... + for (j = 0; j < DoList.Count; j++) { + +#ifndef DEMO + if (net) + Update_Queue_Mono (net, 6); +#endif //DEMO + + //.................................................................. + // If this event was from the currently-executing player ID, and it's + // time to execute it, execute it. + //.................................................................. + if (DoList[j].MPlayerID == MPlayerID[i] && Frame >= DoList[j].Frame && + !DoList[j].IsExecuted) { + + //............................................................... + // Error if it's too late to execute this packet! + //............................................................... + if (Frame > DoList[j].Frame && DoList[j].Type != + EventClass::FRAMEINFO) { +#ifndef DEMO + Dump_Packet_Too_Late_Stuff(&DoList[j]); +#endif //DEMO + CCMessageBox().Process (TXT_PACKET_TOO_LATE); + return (0); + } + + //............................................................... + // Only execute EXIT & OPTIONS commands if they're from myself. + //............................................................... + if (DoList[j].Type==EventClass::EXIT || + DoList[j].Type==EventClass::OPTIONS) { + + + if (DoList[j].Type==EventClass::EXIT){ +CCDebugString ("C&C95 - Received EXIT packet\n"); + + /* + ** Flag that this house lost because it quit. ST - 6/5/96 0:29AM + */ + for (wibble = 0; wibble < MPlayerCount; wibble++) { + if (MPlayerID[wibble] == DoList[j].MPlayerID) { + house = MPlayerHouses[wibble]; + housep = HouseClass::As_Pointer (house); + housep->IGaveUp = true; + break; + } + } + + /* + ** Send the game statistics packet now since the game is effectivly over + */ + if (MPlayerCount == 2 && + GameToPlay == GAME_INTERNET && + !GameStatisticsPacketSent){ + Register_Game_End_Time(); + Send_Statistics_Packet(); + } + } + + + + if (DoList[j].ID == Houses.ID(PlayerPtr)) { + DoList[j].Execute(); + } + + //............................................................ + // If this EXIT event isn't from myself, destroy the connection + // for that player. The HousesType for this event is the + // connection ID. + //............................................................ + else if (DoList[j].Type==EventClass::EXIT) { + if (GameToPlay == GAME_MODEM + || GameToPlay == GAME_NULL_MODEM){ + //|| GameToPlay == GAME_INTERNET) { + Destroy_Null_Connection( DoList[j].MPlayerID, 0 ); + } + + else if ((GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) && net) { + index = net->Connection_Index (DoList[j].MPlayerID); + if (index != -1) { + for (k = index; k < net->Num_Connections() - 1; k++) { + their_frame[k] = their_frame[k+1]; + their_sent[k] = their_sent[k+1]; + their_recv[k] = their_recv[k+1]; + } + CCDebugString ("C&C95 = Destroying connection due to exit event\n"); +#ifndef DEMO + Destroy_Connection(DoList[j].MPlayerID,0); +#endif //DEMO + } + } + } + } + + //............................................................... + // For a FRAMEINFO event, check the CRC value. + // This could be an old FRAMEINFO packet that was floating around + // for awhile and just arrived; if so, its Frame value will be + // old. Ignore these packets. (This created bogus sync bugs on + // the Internet, when packets that were 35 frames old arrived.) + //............................................................... +#ifndef DEMO + else if (DoList[j].Type == EventClass::FRAMEINFO) { + if (DoList[j].Frame == Frame && + DoList[j].Data.FrameInfo.Delay < 32) { + index = ((DoList[j].Frame - DoList[j].Data.FrameInfo.Delay) & + 0x001f); + if (CRC[index] != DoList[j].Data.FrameInfo.CRC) { + Print_CRCs(&DoList[j]); + if (CCMessageBox().Process (TXT_OUT_OF_SYNC, + TXT_CONTINUE, TXT_STOP) == 0) { + if (GameToPlay == GAME_MODEM || + GameToPlay == GAME_NULL_MODEM){ + Destroy_Null_Connection( DoList[j].MPlayerID, -1 ); + Shutdown_Modem(); + GameToPlay = GAME_NORMAL; + } + else if ((GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) && net) { + CCDebugString ("C&C95 = Destroying connections due to bad frame info packet\n"); + while (net->Num_Connections()) { + Destroy_Connection (net->Connection_ID(0), -1); + } + } + Map.Flag_To_Redraw(true); + } + else { + return (0); + } + return (1); + } + } + } +#endif //DEMO + //............................................................... + // Execute other commands + //............................................................... + else { + DoList[j].Execute(); + } + + //............................................................... + // Mark this event as executed. + //............................................................... + DoList[j].IsExecuted = 1; + } + } + } + + return (1); + +} // end of Execute_DoList + + +/*************************************************************************** + * Clean_DoList -- Cleans out old events from the DoList * + * * + * Currently, an event can only be removed from the DoList if it's at the * + * head of the list; and event can't be removed from the middle. So, * + * this routine loops as long as the next event in the DoList has been * + * executed, it's removed. * + * * + * INPUT: * + * net ptr to connection manager; ignored if NULL * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Clean_DoList(ConnManClass *net) +{ + while (DoList.Count) { + +#ifndef DEMO + if (net) + Update_Queue_Mono (net, 7); +#else + net = net; +#endif //DEMO + + //..................................................................... + // Discard events that have been executed, OR it's too late to execute. + // (This happens if another player exits the game; he'll leave FRAMEINFO + // events lying around in my queue. They won't have been "executed", + // because his IPX connection was destroyed.) + //..................................................................... + if ( (DoList.First().IsExecuted) || (Frame > DoList.First().Frame) ) { + DoList.Next(); + } + else { + break; + } + } + +} // end of Clean_DoList + +#ifndef DEMO + +/*************************************************************************** + * Queue_Record -- Records the DoList to disk * + * * + * This routine just saves any events in the DoList to disk; we can later * + * "play back" the recording just be pulling events from disk rather than * + * from the network! * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/14/1995 BRR : Created. * + *=========================================================================*/ +static void Queue_Record(void) +{ + int i,j; + + //------------------------------------------------------------------------ + // Compute # of events to save this frame + //------------------------------------------------------------------------ + j = 0; + for (i = 0; i < DoList.Count; i++) { + if (Frame == DoList[i].Frame && !DoList[i].IsExecuted) { + j++; + } + } + + //------------------------------------------------------------------------ + // Save the # of events, then all events. + //------------------------------------------------------------------------ + RecordFile.Write (&j,sizeof(j)); + for (i = 0; i < DoList.Count; i++) { + if (Frame == DoList[i].Frame && !DoList[i].IsExecuted) { + RecordFile.Write (&DoList[i],sizeof (EventClass)); + j--; + } + } + +} /* end of Queue_Record */ + + +/*************************************************************************** + * Queue_Playback -- plays back queue entries from a record file * + * * + * This routine reads events from disk, putting them into the DoList; * + * it then executes the DoList just like the network version does. The * + * result is that the game "plays back" like a recording. * + * * + * This routine detects mouse motion and stops playback, so it can work * + * like an "attract" mode, showing a demo of the game itself. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/15/1995 BRR : Created. * + *=========================================================================*/ +static void Queue_Playback(void) +{ + int numevents; + EventClass event; + int i; + int ok; + static int mx,my; + int max_houses; + HousesType base_house; + int key; + int testframe; + + //------------------------------------------------------------------------ + // If the user hits ESC, stop the playback + //------------------------------------------------------------------------ + if (Check_Key_Num()) { + key = Get_Key(); + // + // If the user hit ESC, end the recording. If this is an Attract-mode + // recording, end it no matter what the user does (any key or mouse). + // + if (key == KA_ESC || AllowAttract) { + GameActive = 0; + return; + } + } + + //------------------------------------------------------------------------ + // If we're in "Attact" mode, and the user moves the mouse, stop the + // playback. + //------------------------------------------------------------------------ + if (AllowAttract && Frame > 0 && + (mx != Get_Mouse_X() || my != Get_Mouse_Y())) { + GameActive = 0; + return; + } + mx = Get_Mouse_X(); + my = Get_Mouse_Y(); + + //------------------------------------------------------------------------ + // Compute the Game's CRC + //------------------------------------------------------------------------ + Compute_Game_CRC(); + CRC[Frame & 0x001f] = GameCRC; + + //------------------------------------------------------------------------ + // Don't read anything the first time through (since the Queue_AI_Network + // routine didn't write anything the first time through); do this after the + // CRC is computed, since we'll still need a CRC for Frame 0. + //------------------------------------------------------------------------ + if (Frame==0 && GameToPlay!=GAME_NORMAL) { + return; + } + + //------------------------------------------------------------------------ + // Only process every 'FrameSendRate' frames + //------------------------------------------------------------------------ + testframe = ((Frame + (FrameSendRate - 1)) / FrameSendRate) * FrameSendRate; + + if (GameToPlay != GAME_NORMAL && + CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + if (Frame != testframe) { + return; + } + } + + //------------------------------------------------------------------------ + // Read the DoList from disk + //------------------------------------------------------------------------ + ok = 1; + if (RecordFile.Read (&numevents, sizeof(numevents)) == + sizeof(numevents)) { + for (i = 0; i < numevents; i++) { + if (RecordFile.Read (&event, sizeof(EventClass)) == + sizeof(EventClass)) { + event.IsExecuted = 0; + DoList.Add (event); + } + else { + ok = 0; + break; + } + } + } + else { + ok = 0; + } + + if (!ok) { + GameActive = 0; + return; + } + + + //------------------------------------------------------------------------ + // Execute the DoList; if an error occurs, bail out. + //------------------------------------------------------------------------ + if (GameToPlay == GAME_NORMAL) { + max_houses = 1; + base_house = PlayerPtr->Class->House; + } + else { + max_houses = MPlayerMax; + base_house = HOUSE_MULTI1; + } + if (!Execute_DoList(max_houses, base_house, NULL, NULL, NULL, NULL, NULL)) { + GameActive = 0; + return; + } + + //------------------------------------------------------------------------ + // Clean out the DoList + //------------------------------------------------------------------------ + Clean_DoList(NULL); + +} /* end of Queue_Playback */ + + +/*************************************************************************** + * Compute_Game_CRC -- Computes a CRC value of the entire game. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/09/1995 BRR : Created. * + *=========================================================================*/ +static void Compute_Game_CRC(void) +{ + int i; + InfantryClass *infp; + UnitClass *unitp; + BuildingClass *bldgp; + //ObjectClass *objp; + + GameCRC = 0; + + //------------------------------------------------------------------------ + // Infantry + //------------------------------------------------------------------------ + for (i = 0; i < Infantry.Count(); i++) { + infp = (InfantryClass *)Infantry.Active_Ptr(i); + Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing); + } + + //------------------------------------------------------------------------ + // Units + //------------------------------------------------------------------------ + for (i = 0; i < Units.Count(); i++) { + unitp = (UnitClass *)Units.Active_Ptr(i); + Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing + + (int)unitp->SecondaryFacing); + } + + //------------------------------------------------------------------------ + // Buildings + //------------------------------------------------------------------------ + for (i = 0; i < Buildings.Count(); i++) { + bldgp = (BuildingClass *)Buildings.Active_Ptr(i); + Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing); + } + +#if(0) + //------------------------------------------------------------------------ + // Map Layers + //------------------------------------------------------------------------ + for (i = 0; i < LAYER_COUNT; i++) { + for (j = 0; j < Map.Layer[i].Count(); j++) { + objp = Map.Layer[i][j]; + Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I()); + } + } + + //------------------------------------------------------------------------ + // Logic Layers + //------------------------------------------------------------------------ + for (i = 0; i < Logic.Count(); i++) { + objp = Logic[i]; + Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I()); + } +#endif + + //------------------------------------------------------------------------ + // A random # + //------------------------------------------------------------------------ + Add_CRC(&GameCRC, rand()); + +} /* end of Compute_Game_CRC */ + + +/*************************************************************************** + * Add_CRC -- Adds a value to a CRC * + * * + * INPUT: * + * crc ptr to crc * + * val value to add * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 05/09/1995 BRR : Created. * + *=========================================================================*/ +void Add_CRC(unsigned long *crc, unsigned long val) +{ + int hibit; + + if ( (*crc) & 0x80000000) { + hibit = 1; + } + else { + hibit = 0; + } + + (*crc) <<= 1; + (*crc) += val; + (*crc) += hibit; + +} /* end of Add_CRC */ + + +/*************************************************************************** + * Print_CRCs -- Prints a data file for finding Sync Bugs * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 05/09/1995 BRR : Created. * + *=========================================================================*/ +void Print_CRCs(EventClass *ev) +{ + ev=ev; + + int i; //,j; + InfantryClass *infp; + UnitClass *unitp; + BuildingClass *bldgp; + //ObjectClass *objp; + FILE *fp; + int rnd; + HouseClass *housep; + //HousesType house; + int color; + + Mono_Clear_Screen(); + Mono_Set_Cursor (0,0); + + char filename[80]; + sprintf (filename, "CRC%02d.TXT", Frame & 0x1f); + + fp = fopen(filename, "wt"); //"OUT.TXT","wt"); + if (fp==NULL) { + return; + } + + for (i = 0; i < 32; i++) { + fprintf(fp,"CRC[%d]=%x\n",i,CRC[i]); + } + + + housep = HouseClass::As_Pointer (HOUSE_MULTI1); + if (housep) { + color = housep->RemapColor; + fprintf(fp,"Multi1: IsHuman:%d Color:%s\n",housep->IsHuman, + ColorNames[color]); + } + + housep = HouseClass::As_Pointer (HOUSE_MULTI2); + if (housep) { + color = housep->RemapColor; + fprintf(fp,"Multi2: IsHuman:%d Color:%s\n",housep->IsHuman, + ColorNames[color]); + } + + housep = HouseClass::As_Pointer (HOUSE_MULTI3); + if (housep) { + color = housep->RemapColor; + fprintf(fp,"Multi3: IsHuman:%d Color:%s\n",housep->IsHuman, + ColorNames[color]); + } + + housep = HouseClass::As_Pointer (HOUSE_MULTI4); + if (housep) { + color = housep->RemapColor; + fprintf(fp,"Multi4: IsHuman:%d Color:%s\n",housep->IsHuman, + ColorNames[color]); + } + + housep = HouseClass::As_Pointer (HOUSE_MULTI5); + if (housep) { + color = housep->RemapColor; + fprintf(fp,"Multi5: IsHuman:%d Color:%s\n",housep->IsHuman, + ColorNames[color]); + } + + housep = HouseClass::As_Pointer (HOUSE_MULTI6); + if (housep) { + color = housep->RemapColor; + fprintf(fp,"Multi6: IsHuman:%d Color:%s\n",housep->IsHuman, + ColorNames[color]); + } + + + //------------------------------------------------------------------------ + // Multi1 Infantry + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI1)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI1 INFANTRY -------------------\n"); + for (i = 0; i < Infantry.Count(); i++) { + infp = (InfantryClass *)Infantry.Active_Ptr(i); + if (infp->Owner()==HOUSE_MULTI1) { + Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n", + infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(), + infp->Class->Type); + } + } + Mono_Printf("Multi1 Infantry:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi2 Infantry + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI2)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI2 INFANTRY -------------------\n"); + for (i = 0; i < Infantry.Count(); i++) { + infp = (InfantryClass *)Infantry.Active_Ptr(i); + if (infp->Owner()==HOUSE_MULTI2) { + Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n", + infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(), + infp->Class->Type); + } + } + Mono_Printf("Multi2 Infantry:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi3 Infantry + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI3)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI3 INFANTRY -------------------\n"); + for (i = 0; i < Infantry.Count(); i++) { + infp = (InfantryClass *)Infantry.Active_Ptr(i); + if (infp->Owner()==HOUSE_MULTI3) { + Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n", + infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(), + infp->Class->Type); + } + } + Mono_Printf("Multi3 Infantry:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi4 Infantry + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI4)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI4 INFANTRY -------------------\n"); + for (i = 0; i < Infantry.Count(); i++) { + infp = (InfantryClass *)Infantry.Active_Ptr(i); + if (infp->Owner()==HOUSE_MULTI4) { + Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n", + infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(), + infp->Class->Type); + } + } + Mono_Printf("Multi4 Infantry:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi5 Infantry + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI5)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI5 INFANTRY -------------------\n"); + for (i = 0; i < Infantry.Count(); i++) { + infp = (InfantryClass *)Infantry.Active_Ptr(i); + if (infp->Owner()==HOUSE_MULTI5) { + Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n", + infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(), + infp->Class->Type); + } + } + Mono_Printf("Multi5 Infantry:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi6 Infantry + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI6)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI6 INFANTRY -------------------\n"); + for (i = 0; i < Infantry.Count(); i++) { + infp = (InfantryClass *)Infantry.Active_Ptr(i); + if (infp->Owner()==HOUSE_MULTI6) { + Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n", + infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(), + infp->Class->Type); + } + } + Mono_Printf("Multi6 Infantry:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi1 Units + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI1)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI1 UNITS -------------------\n"); + for (i = 0; i < Units.Count(); i++) { + unitp = (UnitClass *)Units.Active_Ptr(i); + if (unitp->Owner()==HOUSE_MULTI1) { + Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing); + fprintf(fp, + "COORD:%x Facing:%d Facing2:%d Mission:%d Type:%d\n", + unitp->Coord,(int)unitp->PrimaryFacing, + (int)unitp->SecondaryFacing,unitp->Get_Mission(), + unitp->Class->Type); + } + } + Mono_Printf("Multi1 Units:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi2 Units + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI2)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI2 UNITS -------------------\n"); + for (i = 0; i < Units.Count(); i++) { + unitp = (UnitClass *)Units.Active_Ptr(i); + if (unitp->Owner()==HOUSE_MULTI2) { + Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing); + fprintf(fp, + "COORD:%x Facing:%d Facing2:%d Mission:%d Type:%d\n", + unitp->Coord,(int)unitp->PrimaryFacing, + (int)unitp->SecondaryFacing,unitp->Get_Mission(), + unitp->Class->Type); + } + } + Mono_Printf("Multi2 Units:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi3 Units + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI3)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI3 UNITS -------------------\n"); + for (i = 0; i < Units.Count(); i++) { + unitp = (UnitClass *)Units.Active_Ptr(i); + if (unitp->Owner()==HOUSE_MULTI3) { + Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing); + fprintf(fp, + "COORD:%x Facing:%d Facing2:%d Mission:%d Type:%d\n", + unitp->Coord,(int)unitp->PrimaryFacing, + (int)unitp->SecondaryFacing,unitp->Get_Mission(), + unitp->Class->Type); + } + } + Mono_Printf("Multi3 Units:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi4 Units + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI4)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI4 UNITS -------------------\n"); + for (i = 0; i < Units.Count(); i++) { + unitp = (UnitClass *)Units.Active_Ptr(i); + if (unitp->Owner()==HOUSE_MULTI4) { + Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing); + fprintf(fp, + "COORD:%x Facing:%d Facing2:%d Mission:%d Type:%d\n", + unitp->Coord,(int)unitp->PrimaryFacing, + (int)unitp->SecondaryFacing,unitp->Get_Mission(), + unitp->Class->Type); + } + } + Mono_Printf("Multi4 Units:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi5 Units + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI5)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI5 UNITS -------------------\n"); + for (i = 0; i < Units.Count(); i++) { + unitp = (UnitClass *)Units.Active_Ptr(i); + if (unitp->Owner()==HOUSE_MULTI5) { + Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing); + fprintf(fp, + "COORD:%x Facing:%d Facing2:%d Mission:%d Type:%d\n", + unitp->Coord,(int)unitp->PrimaryFacing, + (int)unitp->SecondaryFacing,unitp->Get_Mission(), + unitp->Class->Type); + } + } + Mono_Printf("Multi5 Units:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi6 Units + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI6)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI6 UNITS -------------------\n"); + for (i = 0; i < Units.Count(); i++) { + unitp = (UnitClass *)Units.Active_Ptr(i); + if (unitp->Owner()==HOUSE_MULTI6) { + Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing); + fprintf(fp, + "COORD:%x Facing:%d Facing2:%d Mission:%d Type:%d\n", + unitp->Coord,(int)unitp->PrimaryFacing, + (int)unitp->SecondaryFacing,unitp->Get_Mission(), + unitp->Class->Type); + } + } + Mono_Printf("Multi6 Units:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi1 Buildings + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI1)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI1 BUILDINGS -------------------\n"); + for (i = 0; i < Buildings.Count(); i++) { + bldgp = (BuildingClass *)Buildings.Active_Ptr(i); + if (bldgp->Owner()==HOUSE_MULTI1) { + Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n", + bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(), + bldgp->Class->Type); + } + } + Mono_Printf("Multi1 Buildings:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi2 Buildings + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI2)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI2 BUILDINGS -------------------\n"); + for (i = 0; i < Buildings.Count(); i++) { + bldgp = (BuildingClass *)Buildings.Active_Ptr(i); + if (bldgp->Owner()==HOUSE_MULTI2) { + Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n", + bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(), + bldgp->Class->Type); + } + } + Mono_Printf("Multi2 Buildings:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi3 Buildings + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI3)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI3 BUILDINGS -------------------\n"); + for (i = 0; i < Buildings.Count(); i++) { + bldgp = (BuildingClass *)Buildings.Active_Ptr(i); + if (bldgp->Owner()==HOUSE_MULTI3) { + Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n", + bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(), + bldgp->Class->Type); + } + } + Mono_Printf("Multi3 Buildings:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi4 Buildings + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI4)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI4 BUILDINGS -------------------\n"); + for (i = 0; i < Buildings.Count(); i++) { + bldgp = (BuildingClass *)Buildings.Active_Ptr(i); + if (bldgp->Owner()==HOUSE_MULTI4) { + Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n", + bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(), + bldgp->Class->Type); + } + } + Mono_Printf("Multi4 Buildings:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi5 Buildings + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI5)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI5 BUILDINGS -------------------\n"); + for (i = 0; i < Buildings.Count(); i++) { + bldgp = (BuildingClass *)Buildings.Active_Ptr(i); + if (bldgp->Owner()==HOUSE_MULTI5) { + Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n", + bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(), + bldgp->Class->Type); + } + } + Mono_Printf("Multi5 Buildings:%d\n",GameCRC); + } + + //------------------------------------------------------------------------ + // Multi6 Buildings + //------------------------------------------------------------------------ + if (HouseClass::As_Pointer(HOUSE_MULTI6)) { + GameCRC = 0; + fprintf(fp,"-------------------- MULTI6 BUILDINGS -------------------\n"); + for (i = 0; i < Buildings.Count(); i++) { + bldgp = (BuildingClass *)Buildings.Active_Ptr(i); + if (bldgp->Owner()==HOUSE_MULTI6) { + Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d\n", + bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(), + bldgp->Class->Type); + } + } + Mono_Printf("Multi6 Buildings:%d\n",GameCRC); + } +#if (0) + //------------------------------------------------------------------------ + // Map Layers + //------------------------------------------------------------------------ + GameCRC = 0; + for (i = 0; i < LAYER_COUNT; i++) { + fprintf(fp,">>>> MAP LAYER %d <<<<\n",i); + for (j = 0; j < Map.Layer[i].Count(); j++) { + objp = Map.Layer[i][j]; + Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I()); + fprintf(fp,"Object %d: %x ",j,objp->Coord); + + if (objp->What_Am_I() == RTTI_AIRCRAFT) + fprintf(fp,"Aircraft (Type:%d) ", + (AircraftType)(*((AircraftClass *)objp))); + else if (objp->What_Am_I() == RTTI_ANIM) + fprintf(fp,"Anim (Type:%d) ", + (AnimType)(*((AnimClass *)objp))); + else if (objp->What_Am_I() == RTTI_BUILDING) + fprintf(fp,"Building (Type:%d) ", + (StructType)(*((BuildingClass *)objp))); + else if (objp->What_Am_I() == RTTI_BULLET) + fprintf(fp,"Bullet (Type:%d) ", + (BulletType)(*((BulletClass *)objp))); + else if (objp->What_Am_I() == RTTI_INFANTRY) + fprintf(fp,"Infantry (Type:%d) ", + (InfantryType)(*((InfantryClass *)objp))); + else if (objp->What_Am_I() == RTTI_OVERLAY) + fprintf(fp,"Overlay (Type:%d) ", + (OverlayType)(*((OverlayClass *)objp))); + else if (objp->What_Am_I() == RTTI_SMUDGE) + fprintf(fp,"Smudge (Type:%d) ", + (SmudgeType)(*((SmudgeClass *)objp))); + else if (objp->What_Am_I() == RTTI_TEMPLATE) + fprintf(fp,"Template (Type:%d) ", + (TemplateType)(*((TemplateClass *)objp))); + else if (objp->What_Am_I() == RTTI_TERRAIN) + fprintf(fp,"Terrain (Type:%d) ", + (TerrainType)(*((TerrainClass *)objp))); + else if (objp->What_Am_I() == RTTI_UNIT) + fprintf(fp,"Unit (Type:%d) ", + (UnitType)(*((UnitClass *)objp))); + + house = objp->Owner(); + if (house!=HOUSE_NONE) { + housep = HouseClass::As_Pointer (house); + fprintf(fp,"Owner: %s\n",housep->Class->IniName); + } + else { + fprintf(fp,"Owner: NONE\n"); + } + } + } + Mono_Printf("Map Layers:%d\n",GameCRC); + + //------------------------------------------------------------------------ + // Logic Layers + //------------------------------------------------------------------------ + GameCRC = 0; + fprintf(fp,">>>> LOGIC LAYER <<<<\n"); + for (i = 0; i < Logic.Count(); i++) { + objp = Logic[i]; + Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I()); + fprintf(fp,"Object %d: %x ",i,objp->Coord); + + if (objp->What_Am_I() == RTTI_AIRCRAFT) + fprintf(fp,"Aircraft (Type:%d) ", + (AircraftType)(*((AircraftClass *)objp))); + else if (objp->What_Am_I() == RTTI_ANIM) + fprintf(fp,"Anim (Type:%d) ", + (AnimType)(*((AnimClass *)objp))); + else if (objp->What_Am_I() == RTTI_BUILDING) + fprintf(fp,"Building (Type:%d) ", + (StructType)(*((BuildingClass *)objp))); + else if (objp->What_Am_I() == RTTI_BULLET) + fprintf(fp,"Bullet (Type:%d) ", + (BulletType)(*((BulletClass *)objp))); + else if (objp->What_Am_I() == RTTI_INFANTRY) + fprintf(fp,"Infantry (Type:%d) ", + (InfantryType)(*((InfantryClass *)objp))); + else if (objp->What_Am_I() == RTTI_OVERLAY) + fprintf(fp,"Overlay (Type:%d) ", + (OverlayType)(*((OverlayClass *)objp))); + else if (objp->What_Am_I() == RTTI_SMUDGE) + fprintf(fp,"Smudge (Type:%d) ", + (SmudgeType)(*((SmudgeClass *)objp))); + else if (objp->What_Am_I() == RTTI_TEMPLATE) + fprintf(fp,"Template (Type:%d) ", + (TemplateType)(*((TemplateClass *)objp))); + else if (objp->What_Am_I() == RTTI_TERRAIN) + fprintf(fp,"Terrain (Type:%d) ", + (TerrainType)(*((TerrainClass *)objp))); + else if (objp->What_Am_I() == RTTI_UNIT) + fprintf(fp,"Unit (Type:%d) ", + (UnitType)(*((UnitClass *)objp))); + + house = objp->Owner(); + if (house!=HOUSE_NONE) { + housep = HouseClass::As_Pointer (house); + fprintf(fp,"Owner: %s\n",housep->Class->IniName); + } + else { + fprintf(fp,"Owner: NONE\n"); + } + } + Mono_Printf("Logic:%d\n",GameCRC); +#endif + + //------------------------------------------------------------------------ + // Random # generator, frame # + //------------------------------------------------------------------------ + rnd = rand(); + + Mono_Printf("Random Number:%d\n",rnd); + fprintf(fp,"\nRandom Number:%d\n",rnd); + + Mono_Printf("My Frame:%d\n",Frame); + fprintf(fp,"My Frame:%d\n",Frame); +#if (0) + fprintf(fp,"-------------- Offending event: ----------------\n"); + fprintf(fp,"Type: %d\n",ev->Type); + fprintf(fp,"Frame: %d\n",ev->Frame); + fprintf(fp,"MPlayerID: %04x\n",ev->MPlayerID); + if (ev->Type == EventClass::FRAMEINFO) { + fprintf(fp,"CRC: %x\n",ev->Data.FrameInfo.CRC); + fprintf(fp,"CommandCount: %x\n",ev->Data.FrameInfo.CommandCount); + fprintf(fp,"Delay: %x\n",ev->Data.FrameInfo.Delay); + } +#endif + fclose(fp); + +} /* end of Print_CRCs */ + + +/*************************************************************************** + * Init_Queue_Mono -- inits mono display * + * * + * This routine steals control of the mono screen away from the rest of * + * the engine, by setting the global IsMono; if IsMono is set, the other * + * routines in this module turn off the Mono display when they're done * + * with it, so the rest of the engine won't over-write what we're writing. * + * * + * INPUT: * + * net ptr to connection manager * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Init_Queue_Mono(ConnManClass *net) +{ +#if(SHOW_MONO) + //------------------------------------------------------------------------ + // Set 'IsMono' so we can steal the mono screen from the engine + //------------------------------------------------------------------------ + if (Frame==0 && MonoClass::Is_Enabled()) { + IsMono = true; + } + + //------------------------------------------------------------------------ + // Enable mono output for our stuff; we must Disable it before we return + // control to the engine. + //------------------------------------------------------------------------ + if (IsMono) + MonoClass::Enable(); + + if (net->Num_Connections() > 0) { + //..................................................................... + // Network mono debugging screen + //..................................................................... + if (NetMonoMode==0) { + if (Frame==0 || NewMonoMode) { + net->Configure_Debug (0, sizeof (CommHeaderType), + sizeof(EventClass::EventType), EventClass::EventNames, 0); + net->Mono_Debug_Print (0,1); + NewMonoMode = 0; + } + else { + net->Mono_Debug_Print (0,0); + } + } + //..................................................................... + // Flow control debugging output + //..................................................................... + else { + if (NewMonoMode) { + Mono_Clear_Screen(); + Mono_Printf(" Queue AI:\n"); // flowcount[0] + Mono_Printf(" Build Packet Loop:\n"); // flowcount[1] + Mono_Printf(" Frame Sync:\n"); // flowcount[2] + Mono_Printf(" Frame Sync Resend:\n"); // flowcount[3] + Mono_Printf(" Frame Sync Timeout:\n"); // flowcount[4] + Mono_Printf(" Frame Sync New Message:\n"); // flowcount[5] + Mono_Printf(" DoList Execution:\n"); // flowcount[6] + Mono_Printf(" DoList Cleaning:\n"); // flowcount[7] + Mono_Printf("\n"); + Mono_Printf(" Frame:\n"); + Mono_Printf(" MPlayerMaxAhead:\n"); + Mono_Printf(" their_recv:\n"); + Mono_Printf(" their_sent:\n"); + Mono_Printf(" my_sent:\n"); + Mono_Printf(" DesiredFrameRate:\n"); + NewMonoMode = 0; + } + } + } +#else + net = net; +#endif +} // end of Init_Queue_Mono + + +/*************************************************************************** + * Update_Queue_Mono -- updates mono display * + * * + * INPUT: * + * net ptr to connection manager * + * flow_index index # for flow-count updates * + * -1: display * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Update_Queue_Mono(ConnManClass *net, int flow_index) +{ +#if(SHOW_MONO) + static int flowcount[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + + //------------------------------------------------------------------------ + // If 'NetMonoMode' is 1, display flowcount info + //------------------------------------------------------------------------ + if (NetMonoMode==1) { + if (flow_index >= 0 && flow_index < 20) { + Mono_Set_Cursor(35,flow_index); + flowcount[flow_index]++; + Mono_Printf("%d",flowcount[flow_index]); + } + } + //------------------------------------------------------------------------ + // Otherwise, display the connection debug screen + //------------------------------------------------------------------------ + else { + net->Mono_Debug_Print (0,0); + } + +#else + flow_index = flow_index; + net = net; +#endif + +} // end of Update_Queue_Mono + + +/*************************************************************************** + * Print_Framesync_Values -- displays frame-sync variables * + * * + * INPUT: * + * curframe current game Frame # * + * max_ahead max-ahead value * + * num_connections # connections * + * their_recv # commands I've received from my connections * + * their_sent # commands each connection claims to have sent * + * my_sent # commands I've sent * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Print_Framesync_Values(long curframe, unsigned long max_ahead, + int num_connections, unsigned short *their_recv, + unsigned short *their_sent, unsigned short my_sent) +{ +#if(SHOW_MONO) + int i; + + if (NetMonoMode==1) { + Mono_Set_Cursor(35,9); + Mono_Printf("%d",curframe); + + Mono_Set_Cursor(35,10); + Mono_Printf("%d",max_ahead); + + for (i = 0; i < num_connections; i++) { + Mono_Set_Cursor(35 + i*5,11); + Mono_Printf("%4d",(int)their_recv[i]); + } + + for (i = 0; i < num_connections; i++) { + Mono_Set_Cursor(35 + i*5,12); + Mono_Printf("%4d",(int)their_sent[i]); + } + + Mono_Set_Cursor(35,13); + Mono_Printf("%4d",(int)my_sent); + + Mono_Set_Cursor(35,14); + Mono_Printf("%2d",(int)DesiredFrameRate); + } +#else + curframe = curframe; + max_ahead = max_ahead; + num_connections = num_connections; + their_recv = their_recv; + their_sent = their_sent; + my_sent = my_sent; +#endif +} // end of Print_Framesync_Values + + +/*************************************************************************** + * Dump_Packet_Too_Late_Stuff -- Dumps a debug file to disk * + * * + * INPUT: * + * event ptr to event to print * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/28/1996 BRR : Created. * + *=========================================================================*/ +void Dump_Packet_Too_Late_Stuff(EventClass *event) +{ + FILE *fp; + int i; + + fp = fopen("toolate.txt", "wt"); + if (!fp) { + return; + } + fprintf(fp,"--------- Event data: -------------------\n"); + fprintf(fp,"Type: %s\n",EventClass::EventNames[event->Type]); + fprintf(fp,"Frame: %d\n",event->Frame); + fprintf(fp,"ID: %d\n",event->ID); + fprintf(fp,"MPlayerID: %04x\n",event->MPlayerID); + + for (i = 0; i < MPlayerCount; i++) { + if (event->MPlayerID == MPlayerID[i]) { + fprintf(fp,"Player's Name: %s",MPlayerNames[i]); + } + } + + fprintf(fp,"----------- My data: ------------------\n"); + fprintf(fp,"Frame:%d\n",Frame); + fprintf(fp,"MaxAhead:%d\n",MPlayerMaxAhead); + + fclose(fp); +} + +#endif //DEMO + +/*************************** end of queue.cpp ******************************/ diff --git a/QUEUE.H b/QUEUE.H new file mode 100644 index 0000000..7d6f615 --- /dev/null +++ b/QUEUE.H @@ -0,0 +1,275 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\queue.h_v 2.16 16 Oct 1995 16:45:50 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : QUEUE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/08/94 * + * * + * Last Update : December 9, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * QueueClass::Add -- Add object to queue. * + * QueueClass::First -- Fetches reference to first object in list. * + * QueueClass::Init -- Initializes queue to empty state. * + * QueueClass::Next -- Throws out the head of the line. * + * QueueClass::operator[] -- Fetches reference to sub object in queue. * + * QueueClass::QueueClass -- Default constructor for QueueClass objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef QUEUE_H +#define QUEUE_H + +#include "mission.h" +#include "target.h" +#include "defines.h" + +#pragma warn -inl + +/* +** This class implements a classic FIFO queue (also known as - standing in line). Objects +** are added to the end (tail) of the line. Objects are removed from the start (first) of +** the line. A keyboard buffer is a good example of a common computer use of a queue. There +** is no provision for "taking cuts" or leaving the line once an object has been added. +** +** The implementation uses a circular list of objects. This allows adding and deleting of +** elements without any maintenance moves of remaining objects that would otherwise be +** necessary in a simple array storage method. A side effect of this means that accessing the +** internal array directly is not allowed. Supporting functions are provided for this purpose. +** +** WARNING WARNING WARNING WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +** The size parameter MUST be an exact power of two (2, 4, 8, 16, etc.) otherwise the internal +** indexing algorithm will fail. +*/ +template +class QueueClass +{ + public: + /* + ** This is the count of the number of objects in the queue. If this count is zero, + ** then the operator[], First(), and Next() functions are undefined. Check this + ** value BEFORE calling these functions. + */ + const int Count; + + //-------------- Functions -------------------- + QueueClass(void); // Default constructor. + + /* + ** The bracket subscript operator functions similarly to the way a normal subscript + ** operator works except that entry [0] matches the first-in-line and entry + ** [Count-1] matches the last-in-line. This is ensured regardless of the actual position + ** of the object in the circular internal list. + */ + T & operator[](int); + + /* + ** This function will return a reference to the "head of the line" object. + */ + T & First(void); + + /* + ** This function clears the list of objects. + */ + void Init(void); + + /* + ** This function discards the head-of-the-line object and advances all the remaining + ** objects up by one. Mnemonic: Imagine a broadway audition and the director yells + ** "NEXT!" + */ + int Next(void); + + /* + ** This will add an object to the tail of the line. If there is no more room to add + ** the object, then false will be returned. + */ + int Add(T const &); + + private: + int Head; // Index of element in list the longest. + int Tail; // Index where next new addition will go. + + T Array[size]; // Raw array of objects. +}; + + +/*********************************************************************************************** + * QueueClass::QueueClass -- Default constructor for QueueClass objects. * + * * + * This default constructor for QueueClass objects initializes the queue to an empty * + * state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline QueueClass::QueueClass(void) : Count(0) +{ + Init(); +} + + +/*********************************************************************************************** + * QueueClass::Init -- Initializes queue to empty state. * + * * + * This function resets the queue to an empty state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline void QueueClass::Init(void) +{ + ((int &)Count) = 0; + Head = 0; + Tail = 0; +} + + +/*********************************************************************************************** + * QueueClass::Add -- Add object to queue. * + * * + * This function is used to add an object to the tail of the line. If the queue cannot * + * accept any more entries, then the object won't be added and false will be returned. * + * * + * INPUT: object -- The object that is to be added to the queue. * + * * + * OUTPUT: bool; Was the object added successfully? * + * * + * WARNINGS: If the queue is full, then the object won't be added. Be sure to check for this.* + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline int QueueClass::Add(T const &q) +{ + if (Count < size) { + Array[Tail] = q; + Tail = (Tail + 1) & (size-1); + ((int &)Count) = Count + 1; + return(true); + } + Mono_Printf( "Queue Add failed Count %d size %d tail %d head %d \n", + Count, size, Tail, Head ); + return (false); +} + + +/*********************************************************************************************** + * QueueClass::Next -- Throws out the head of the line. * + * * + * This routine is used to discard the object at the head of the line. All remaining * + * objects "move up" one. No actual movement occurs, merely the index is adjusted, but * + * the affect is that the next oldest object in the queue will now be returned with the * + * next call to the First() function. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of object remaining in the queue. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline int QueueClass::Next(void) +{ + if (Count) { + Head = (Head + 1) & (size-1); + ((int &)Count) = Count - 1; + } + return (Count); +} + + +/*********************************************************************************************** + * QueueClass::operator[] -- Fetches reference to sub object in queue. * + * * + * Use this routine to examine individual objects within the queue. The oldest object in * + * the queue is referenced by an index value of zero. The newest object in the queue is * + * referenced by a value of Count-1. If there are no objects in the queue, then this * + * operator is undefined. Although this operator allows examination of the queue, there is * + * no corresponding ability to insert or delete objects from the middle of the queue. * + * * + * INPUT: index -- The index into the queue of objects. Valid values range from zero to * + * Count-1. All other values return an undefined reference! * + * * + * OUTPUT: Returns with a reference to the object indicated by the index. * + * * + * WARNINGS: Check to make sure that Count is not zero before using this operator. Failure * + * to do so will return a reference to an undefined object. * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline T & QueueClass::operator[](int index) +{ + return Array[(Head + index) & (size-1)]; +} + + +/*********************************************************************************************** + * QueueClass::First -- Fetches reference to first object in list. * + * * + * This routine is used to fetch the first object in the list (head of the line). This * + * object is the oldest in the list. Typical use of this function is to get and process * + * the first object so that it may be discarded with the Next() function in order to bring * + * subsequent objects to the first position. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference to the oldest object in the queue. * + * * + * WARNINGS: If there are no objects in the queue, then this function returns an undefined * + * reference. Be sure to check Count against zero before calling this function. * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline T & QueueClass::First(void) +{ + return Array[Head]; +} + +#endif diff --git a/RADAR.CPP b/RADAR.CPP new file mode 100644 index 0000000..0e20ac7 --- /dev/null +++ b/RADAR.CPP @@ -0,0 +1,1960 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\radar.cpv 2.17 16 Oct 1995 16:49:28 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RADAR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : November 17, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Get_Multi_Color -- Get the multi color offset number * + * RadarClass::AI -- Processes radar input (non-tactical). * + * RadarClass::Cell_On_Radar -- Determines if a cell is currently visible on radar. * + * RadarClass::Click_Cell_Calc -- Determines what cell the pixel coordinate is over. * + * RadarClass::Click_In_Radar -- Check to see if a click is in radar map * + * RadarClass::Click_In_Radar -- Converts a radar click into cell X and Y coordinate. * + * RadarClass::Draw_It -- Displays the radar map of the terrain. * + * RadarClass::Draw_Names -- draws players' names on the radar map * + * RadarClass::Init_Clear -- Sets the radar map to a known state * + * RadarClass::Map_Cell -- Updates radar map when a cell becomes mapped. * + * RadarClass::One_Time -- Handles one time processing for the radar map. * + * RadarClass::Player_Names -- toggles the Player-Names mode of the radar map * + * RadarClass::Plot_Radar_Pixel -- Updates the radar map with a terrain pixel. * + * RadarClass::RadarClass -- Default constructor for RadarClass object. * + * RadarClass::Radar_Activate -- Controls radar activation. * + * RadarClass::Radar_Anim -- Renders current frame of radar animation * + * RadarClass::Radar_Cursor -- Adjust the position of the radar map cursor. * + * RadarClass::Radar_Pixel -- Mark a cell to be rerendered on the radar map. * + * RadarClass::Radar_Position -- Returns with the current position of the radar map. * + * RadarClass::Refresh_Cells -- Intercepts refresh request and updates radar if needed * + * RadarClass::Render_Infantry -- Displays objects on the radar map. * + * RadarClass::Render_Overlay -- Renders an icon for given overlay * + * RadarClass::Render_Terrain -- Render the terrain over the given cell * + * RadarClass::Set_Map_Dimensions -- Sets the tactical map dimensions. * + * RadarClass::Set_Radar_Position -- Sets the radar map coordinates. * + * RadarClass::Set_Radar_Position -- Sets the radar position to center around specified cell.* + * RadarClass::Set_Tactical_Position -- Called when setting the tactical display position. * + * RadarClass::Set_Tactical_Position -- Called when setting the tactical display position. * + * RadarClass::TacticalClass::Action -- I/O function for the radar map. * + * RadarClass::Zoom_Mode(void) -- Handles toggling zoom on the map * + * RadarClass::Set_Tactical_Position -- Sets the map's tactical position and adjusts radar to* + * RadarClass::Coord_To_Radar_Pixel -- Converts a coordinate to a radar pixel position * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include + +//void const * RadarClass::CoverShape; +RadarClass::TacticalClass RadarClass::RadarButton; + +void const * RadarClass::RadarAnim = NULL; + +static bool FullRedraw = false; + +#define _MAX_NAME 13 + +static GraphicBufferClass _IconStage(3,3); +static GraphicBufferClass _TileStage(24,24); + +/*********************************************************************************************** + * RadarClass::RadarClass -- Default constructor for RadarClass object. * + * * + * This default constructor merely sets the radar specific values to default settings. The * + * radar must be deliberately activated in order for it to be displayed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/16/1994 JLB : Created. * + *=============================================================================================*/ +RadarClass::RadarClass(void) +{ + IsZoomed = true; + IsRadarActive = false; + IsToRedraw = false; + RadarCursorRedraw = false; + PixelPtr = 0; + SpecialRadarFrame = 0; + IsPlayerNames = false; +} + + +/*********************************************************************************************** + * RadarClass::One_Time -- Handles one time processing for the radar map. * + * * + * This routine handles any one time processing required in order for the radar map to * + * function. This actually only requires an allocation of the radar staging buffer. This * + * buffer is needed for those cases where the radar area of the page is being destroyed * + * and it needs to be destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure to call this routine only ONCE. * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +void RadarClass::One_Time(void) +{ + int factor = Get_Resolution_Factor(); + RadWidth = 80 << factor; + RadHeight = 70 << factor; + RadX = SeenBuff.Get_Width() - RadWidth; + RadY = Map.Get_Tab_Height() - (1 << factor); + RadPWidth = 64 << factor; + RadPHeight = 64 << factor; + if ( factor ) { + RadOffX = 16; + RadOffY = 7; + RadIWidth = 128; + RadIHeight = 128; + } else { + RadOffX = 4 << factor; + RadOffY = 1 << factor; + RadIWidth = 72 << factor; + RadIHeight = 69 << factor; + } + + DisplayClass::One_Time(); + RadarButton.X = RadX+RadOffX; + RadarButton.Y = RadY+RadOffY; + RadarButton.Width = RadIWidth; + RadarButton.Height = RadIHeight; +} + + +/*********************************************************************************************** + * RadarClass::Init_Clear -- Sets the radar map to a known state. * + * * + * This routine is used to initialize the radar map at the start of the scenario. It * + * sets the radar map position and starts it in the disabled state. * + * * + * INPUT: theater -- The theater that the scenario is starting (unused by this routine). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Init_Clear(void) +{ + DisplayClass::Init_Clear(); + IsRadarActive = false; + IsToRedraw = true; + RadarCursorRedraw = true; + IsRadarActivating = false; + IsRadarDeactivating = false; + DoesRadarExist = false; + PixelPtr = 0; + IsPlayerNames = false; + + /* + ** If we have a valid map lets make sure that we set it correctly + */ + if (MapCellWidth || MapCellHeight) { + IsZoomed = false; + Zoom_Mode(Coord_Cell(Map.TacticalCoord)); + } +} + + +/*********************************************************************************************** + * RadarClass::Radar_Activate -- Controls radar activation. * + * * + * Use this routine to turn the radar map on or off. * + * * + * INPUT: control -- What to do with the radar map: * + * 0 = Turn radar off. * + * 1 = Turn radar on. * + * 2 = Remove Radar Gadgets * + * 3 = Add Radar Gadgets * + * 4 = Remove radar. * + * -1= Toggle radar on or off. * + * * + * OUTPUT: bool; Was the radar map already on? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/11/1994 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Radar_Activate(int control) +{ + bool old = IsRadarActive; + + switch (control) { + + /* + ** Toggle the state of the radar map on or off. + */ + case -1: + { + int temp = (IsRadarActive == false); + if (temp) { + Radar_Activate(1); + } else { + Radar_Activate(0); + } + } + break; + + /* + ** Turn the radar map off properly. + */ + case 0: + if (Map.IsSidebarActive) { + if (IsRadarActive && !IsRadarDeactivating) { + Sound_Effect(VOC_RADAR_OFF); + IsRadarDeactivating = true; + IsRadarActive = false; + if (IsRadarActivating == true) { + IsRadarActivating = false; + } else { + RadarAnimFrame = RADAR_ACTIVATED_FRAME; + } + } + } else { + Radar_Activate(2); + } + return(old); + + case 1: + if (Map.IsSidebarActive) { + if (!IsRadarActivating && !IsRadarActive) { + Sound_Effect(VOC_RADAR_ON); + IsRadarActivating = true; + if (IsRadarDeactivating == true) { + IsRadarDeactivating = false; + } else { + if (DoesRadarExist) { + RadarAnimFrame = MAX_RADAR_FRAMES; + } else { + RadarAnimFrame = 0; + } + } + } + } else { + Radar_Activate(3); + } + return(old); + + case 2: + if (GameToPlay==GAME_NORMAL) { + Map.Zoom.Disable(); + } + IsRadarActive = false; + IsRadarActivating = false; + IsRadarDeactivating = false; + break; + + case 3: + if (GameToPlay==GAME_NORMAL) { + Map.Zoom.Enable(); + } + IsRadarActive = true; + IsRadarActivating = false; + IsRadarDeactivating = false; + break; + + case 4: + IsRadarActive = false; + IsRadarActivating = false; + IsRadarDeactivating = false; + DoesRadarExist = false; + Flag_To_Redraw(false); + IsToRedraw = true; + break; + } + + if (IsRadarActive != old) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + FullRedraw = IsRadarActive; + return(old); +} + + +/*********************************************************************************************** + * RadarClass::Draw_It -- Displays the radar map of the terrain. * + * * + * This is used to display the radar map that appears in the lower * + * right corner. The main changes to this map are the vehicles and * + * structure pixels. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/24/1991 JLB : Created. * + * 05/08/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void RadarClass::Draw_It(bool forced) +{ + DisplayClass::Draw_It(forced); +// if (!In_Debugger) while (!HidPage.Lock()) {} + + /* + ** Don't perform any rendering if none is requested. + */ + if (!forced && !IsToRedraw && !FullRedraw) return; + + static HousesType _house = HOUSE_NONE; + if (PlayerPtr->ActLike != _house) { + char name[_MAX_NAME + _MAX_EXT]; + + if (Special.IsJurassic && AreThingiesEnabled) { + strcpy(name, "RADAR.JP"); + } else { + _makepath(name, NULL, NULL, "RADAR", HouseTypeClass::As_Reference(PlayerPtr->ActLike).Suffix); + } + RadarAnim = Hires_Retrieve(name); + _house = PlayerPtr->ActLike; + } + + /* + ** If in player name mode, just draw player names + */ + if (IsPlayerNames) { + Draw_Names(); + IsToRedraw = false; + return; + } + + if (IsRadarActivating || IsRadarDeactivating) { + Radar_Anim(); + IsToRedraw = false; + return; + } + + if (Map.IsSidebarActive) { + if (IsRadarActive) { + + //HidPage.Lock(); +// ST 8/13/96 2:24PM +//forced = true; + /* + ** If only a few of the radar pixels need to be redrawn, then find and redraw + ** only these. + */ + if (!forced && IsToRedraw && !FullRedraw) { + IsToRedraw = false; + + if (PixelPtr) { + + /* + ** Render all pixels in the "to redraw" stack. + */ + for (int index = 0; index < PixelPtr; index++) { + CELL cell = PixelStack[index]; + if (Cell_On_Radar(cell)) { + (*this)[cell].IsPlot = false; + Plot_Radar_Pixel(cell); + RadarCursorRedraw |= (*this)[cell].IsRadarCursor; + } + } + + + /* + ** Refill the stack if there is pending pixels yet to be plotted. + ** This should only process in sections for speed reasons + */ + + if (PixelPtr == PIXELSTACK) { + PixelPtr = 0; + + for (int y = 0; y < MapCellHeight; y++) { + for (int x = 0; x < MapCellWidth; x++) { + CELL cell = XY_Cell(MapCellX + x, MapCellY + y); + if (Cell_On_Radar(cell)) { + + if ((*this)[cell].IsPlot) { + PixelStack[PixelPtr++] = cell; + IsToRedraw = true; + if (PixelPtr == PIXELSTACK) break; + } + } + } + if (PixelPtr == PIXELSTACK) break; + } + } else { + PixelPtr = 0; + } + } + + Radar_Cursor(RadarCursorRedraw); + + } else { + GraphicViewPortClass *oldpage = Set_Logic_Page(HidPage); +// if (LogicPage->Lock()) { + CC_Draw_Shape(RadarAnim, RADAR_ACTIVATED_FRAME, RadX, RadY+1, WINDOW_MAIN, SHAPE_NORMAL); + if (BaseX || BaseY) { + LogicPage->Fill_Rect( RadX + RadOffX, + RadY + RadOffY, + RadX + RadOffX + RadIWidth - 1, + RadY + RadOffY + RadIHeight - 1, + DKGREY); + } else { + LogicPage->Fill_Rect( RadX + RadOffX, + RadY + RadOffY, + RadX + RadOffX + RadIWidth - 1, + RadY + RadOffY + RadIHeight - 1, + BLACK); + } + + /* + ** Draw the entire radar map. + */ + for (int index = 0; index < MAP_CELL_TOTAL; index++) { + Plot_Radar_Pixel(index); + } + Radar_Cursor(true); + FullRedraw = false; + IsToRedraw = false; + LogicPage->Unlock(); + if (oldpage == &SeenBuff) { + Hide_Mouse(); + LogicPage->Blit(SeenBuff, RadX, RadY, RadX, RadY, RadWidth, RadHeight); + Show_Mouse(); + } + +// Set_Logic_Page(oldpage); + +// } + + } + + } else { + + /* + ** If the radar is not active, then only draw the cover plate if forced to do so. + */ +// if (forced) { + int val = (DoesRadarExist) ? MAX_RADAR_FRAMES : 0; + CC_Draw_Shape(RadarAnim, val, RadX, RadY + 1, WINDOW_MAIN, SHAPE_NORMAL); + FullRedraw = false; + IsToRedraw = false; +// } + } + + //HidPage.Unlock(); +// Map.Activator.Draw_Me(true); + } +} + +/*************************************************************************** + * RadarClass::Render_Terrain -- Render the terrain over the given cell * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/12/1995 PWG : Created. * + *=========================================================================*/ +void RadarClass::Render_Terrain(CELL cell, int x, int y, int size) +{ + TerrainClass *list[4]; + int listidx = 0; + int lp,lp2; + + + ObjectClass *obj = Map[cell].Cell_Occupier(); + + /* + ** If the cell is occupied by a terrain type, add it to the sortable + ** list. + */ + if (obj && obj->What_Am_I() == RTTI_TERRAIN) + list[listidx++] = (TerrainClass *)obj; + + /* + ** Now loop through all the occupiers and add them to the list if they + ** are terrain type. + */ + for (lp = 0; lp < 3; lp ++) { + obj = Map[cell].Overlapper[lp]; + if (obj && obj->What_Am_I() == RTTI_TERRAIN) + list[listidx++] = (TerrainClass *)obj; + } + + /* + ** If there are no entrys in our list then just get out. + */ + if (!listidx) return; + + /* + ** If there is terrain in this cell then draw a dark pixel to + ** represent it. + */ + if (size == 1) { + LogicPage->Put_Pixel(x, y, 60); + return; + } + + /* + ** Sort the list by its sort Y value so that we can render in the proper + ** order. + */ + for (lp = 0; lp < listidx - 1; lp ++) { + for (lp2 = lp + 1; lp2 < listidx; lp2++) { + if (list[lp]->Sort_Y() > list[lp2]->Sort_Y()) { + TerrainClass *terrain = list[lp]; + list[lp] = list[lp2]; + list[lp2] = terrain; + } + } + } + + /* + ** loop through the list and take care of rendering the correct icon. + */ + for (lp = 0; lp < listidx; lp ++) { + unsigned char *icon = list[lp]->Radar_Icon(cell); + if (!icon) continue; + + Buffer_To_Page(0, 0, 3, 3, icon, _IconStage); + _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, ZoomFactor, ZoomFactor, TRUE, (char *)&FadingBrighten[0]); + } +} + + +/*********************************************************************************************** + * RadarClass::Render_Infantry -- Displays objects on the radar map. * + * * + * This routine will display an object imagery at the location specified according to the * + * condition of the specified cell. * + * * + * INPUT: cell -- The cell to use as reference when drawing the radar pixel. * + * * + * x,y -- The pixel coordinate to render the radar "pixel" at. * + * * + * size -- The size of the "pixel". When zoomed in, this value will be "3". * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/17/1995 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Render_Infantry(CELL cell, int x, int y, int size) +{ + ObjectClass *obj; + int xoff,yoff; + + obj = (ObjectClass *)Map[cell].Cell_Occupier(); + while (obj) { + if (obj->Is_Techno() && (((TechnoClass *)obj)->Cloak != CLOAKED || ((TechnoClass *)obj)->House->Is_Ally(PlayerPtr))) { + switch (obj->What_Am_I()) { + case RTTI_INFANTRY: + { + //int divisor = 255 / ZoomFactor; + int divisor = 86; + if ( ZoomFactor >= 3 ) { + xoff = Coord_XLepton(obj->Coord) / divisor; + yoff = Coord_YLepton(obj->Coord) / divisor; + if ( ZoomFactor >= 6 ) { + xoff<<=1; + yoff<<=1; + } + } else { + xoff = 0; + yoff = 0; + } + LogicPage->Put_Pixel(x+xoff, y+yoff, ((InfantryClass *)obj)->House->Class->BrightColor); + } + break; + + case RTTI_UNIT: + case RTTI_AIRCRAFT: + // PWG: Slowdown? + //if (LogicPage->Lock()){ + Fat_Put_Pixel(x, y, ((UnitClass *)obj)->House->Class->BrightColor, size, *LogicPage); + //LogicPage->Unlock(); + //} + break; + } + } + obj = obj->Next; + } +} + + +/*************************************************************************** + * RadarClass::Render_Overlay -- Renders an icon for given overlay * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/18/1995 PWG : Created. * + *=========================================================================*/ +void RadarClass::Render_Overlay(CELL cell, int x, int y, int size) +{ + OverlayType overlay = (*this)[cell].Overlay; + if (overlay != OVERLAY_NONE) { + OverlayTypeClass const * otype = &OverlayTypeClass::As_Reference(overlay); + + if (otype->IsRadarVisible) { + unsigned char *icon = otype->Radar_Icon((*this)[cell].OverlayData); + if (!icon) return; + Buffer_To_Page(0, 0, 3, 3, icon, _IconStage); + if (otype->IsTiberium) { + _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, size, size, TRUE, (char *)&FadingGreen[0]); + } else { + _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, size, size, TRUE, (char *)&FadingBrighten[0]); + } + } + } +} + + +/*************************************************************************** + * RadarClass::Zoom_Mode -- Handles toggling zoom on the map * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/29/1995 PWG : Created. * + *=========================================================================*/ +void RadarClass::Zoom_Mode(CELL cell) +{ + int map_c_width; + int map_c_height; + /* + ** Set all of the initial zoom mode variables to the correct + ** setting. + */ + IsZoomed = !IsZoomed; + BaseX = 0; + BaseY = 0; + + /* + ** Figure out exactly what size we need to zoom the map to. + */ + if ( !IsZoomed ) { + int xfactor = RadIWidth / MapCellWidth; + int yfactor = RadIHeight / MapCellHeight; + ZoomFactor = MIN(xfactor,yfactor); + map_c_width = MapCellWidth; + map_c_height = MapCellHeight; + } else { + ZoomFactor = 6; + map_c_width = RadIWidth / ZoomFactor; + map_c_height = RadIHeight / ZoomFactor; + } + + /* + ** Make sure we do not show more cell then are on the map. + */ + map_c_width = MIN(map_c_width, 62); + map_c_height = MIN(map_c_height, 62); + + /* + ** Find the amount of remainder because this will let us calculate + ** how to center the thing. + */ + int rem_x = RadIWidth - (map_c_width * ZoomFactor); + int rem_y = RadIHeight - (map_c_height * ZoomFactor); + + /* + ** Finally mark the map so it shows just as much as it is supposed + ** to. + */ + BaseX = rem_x / 2; + BaseY = rem_y / 2; + RadarCellWidth = map_c_width; + RadarCellHeight = map_c_height; + RadarWidth = RadIWidth - rem_x; + RadarHeight = RadIWidth - rem_y; + + /* + ** Set the radar position to the current cell. + */ + Set_Radar_Position(cell); + + /* + ** When zoom mode changes then we need to redraw the radar + ** area. + */ + IsToRedraw = true; + + /* + ** Notify the map that we need to redraw a portion + */ + Flag_To_Redraw(false); + + /* + ** Since we have made a vast change we must redraw everything + */ + FullRedraw = true; +} + + +/*********************************************************************************************** + * RadarClass::Plot_Radar_Pixel -- Updates the radar map with a terrain pixel. * + * * + * This will update the radar map with a pixel. It is used to display * + * vehicle positions on the radar map. * + * * + * INPUT: unit -- Pointer to unit to render at the given position. If * + * NULL is passed in, then the underlying terrain is * + * displayed instead. * + * * + * pos -- Position on the map to update. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine does NOT hide the mouse. It is up to you to * + * do so. * + * * + * HISTORY: * + * 06/04/1991 JLB : Created. * + * 06/21/1991 JLB : Large blips for units & buildings. * + * 02/14/1994 JLB : Revamped. * + * 04/17/1995 PWG : Created. * + * 04/18/1995 PWG : Created. * + *=============================================================================================*/ +void RadarClass::Plot_Radar_Pixel(CELL cell) +{ + if (cell == -1) cell = 1; + + int x,y; // Coordinate of cell location. + + /* + ** Perform any clipping on the cell coordinate. + */ + if (!IsRadarActive || (unsigned)cell > MAP_CELL_TOTAL) return; + + if (!In_Radar(cell) || !Cell_On_Radar(cell)) { + return; + } + + /* + ** If we are zoomed in then calculate the pixel based off of the portion + ** of the map the radar is viewing. + */ + x = Cell_X(cell) - RadarX; + y = Cell_Y(cell) - RadarY; + + if (LogicPage->Lock()) { + CellClass * cellptr = &(*this)[cell]; + x = RadX + RadOffX + BaseX + (x * ZoomFactor); + y = RadY + RadOffY + BaseY + (y * ZoomFactor); + + /* + ** Determine what (if any) vehicle or unit should be rendered in this blip. + */ + int color=TBLACK; // Color of the pixel to plot. + if ((*this)[cell].IsVisible || Debug_Unshroud) { + color = cellptr->Cell_Color(true); + } else { + color = BLACK; + } + +// ST 8/13/96 2:24PM +//if (cellptr->IsRadarCursor){ +// color = WHITE; +//} + + /* + ** If no color override occurs for this cell, then render the underlying + ** terrain. + */ + if (color == TBLACK) { + if (ZoomFactor > 1) { + void const *ptr; + long offset; + int icon; + + if (cellptr->TType != TEMPLATE_NONE) { + ptr = TemplateTypeClass::As_Reference(cellptr->TType).Get_Image_Data(); + icon = cellptr->TIcon; + } else { + ptr = TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1).Get_Image_Data(); + icon = cellptr->Clear_Icon(); + } + + /* + ** Convert the logical icon number into the actual icon number. + */ + Mem_Copy(Add_Long_To_Pointer((void *)ptr, 28), &offset, sizeof(offset)); + Mem_Copy(Add_Long_To_Pointer((void *)ptr, offset+icon), &icon, sizeof(char)); + icon &= 0x00FF; + + Mem_Copy(Add_Long_To_Pointer((void *)ptr, 12), &offset, sizeof(offset)); + ptr = Add_Long_To_Pointer((void *)ptr, offset + icon*(24*24)); + + unsigned char * data = (unsigned char *)ptr; + Buffer_To_Page(0, 0, 24, 24, data, _TileStage); + _TileStage.Scale(*LogicPage, 0, 0, x, y, 24, 24, ZoomFactor, ZoomFactor, TRUE); + + } else { + if (LogicPage->Lock()){ + Fat_Put_Pixel(x, y, cellptr->Cell_Color(false), ZoomFactor, *LogicPage); + LogicPage->Unlock(); + } + } + } else { + if (LogicPage->Lock()){ + Fat_Put_Pixel(x, y, color, ZoomFactor, *LogicPage); + LogicPage->Unlock(); + } + } + if (color != BLACK) { + Render_Overlay(cell, x, y, ZoomFactor); + Render_Terrain(cell, x, y, ZoomFactor); + Render_Infantry(cell, x, y, ZoomFactor); + } + LogicPage->Unlock(); + } +} + + +/*********************************************************************************************** + * RadarClass::Radar_Pixel -- Mark a cell to be rerendered on the radar map. * + * * + * This routine is used to inform the system that a pixel needs to be * + * rerendered on the radar map. The pixel(s) will be rendered the * + * next time the map is refreshed. * + * * + * INPUT: cell -- The map cell to be rerendered. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/12/1992 JLB : Created. * + * 05/08/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void RadarClass::Radar_Pixel(CELL cell) +{ + if (IsRadarActive && Map.IsSidebarActive && Cell_On_Radar(cell)) { + IsToRedraw = true; + (*this)[cell].IsPlot = true; + if (PixelPtr < PIXELSTACK) { + PixelStack[PixelPtr++] = cell; + } + } +} + + +/*********************************************************************************************** + * RadarClass::Click_In_Radar -- Converts a radar click into cell X and Y coordinate. * + * * + * This routine will examine the X and Y coordinate and convert them into the X and Y * + * cell coordinate value that cooresponds to the location. * + * * + * INPUT: x,y -- The X and Y moouse coordinate already normalized to the radar upper left * + * corner. * + * * + * OUTPUT: Returns with success rating in addition, the X and Y values will now hold the * + * cell coordinates of the cell the pixel offsets indicated. * + * Result 1 = click was in radar region * + * Result 0 = click was outside radar region completly * + * Result-1 = click in radar area but not on clickable reagion of radar. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/30/1995 PWG : Created. * + * 07/16/1995 JLB : Recognizes when sidebar is closed now. * + *=============================================================================================*/ +int RadarClass::Click_In_Radar(int &ptr_x, int &ptr_y, bool change) +{ + int x = ptr_x; + int y = ptr_y; + + /* + ** If radar is not active the click could have been on a radar point + */ + if (!IsRadarActive || !Map.IsSidebarActive) return(0); + + x -= (RadX + RadOffX); + y -= (RadY + RadOffY); + if ((unsigned)x < RadIWidth && (unsigned)y < RadIHeight) { + x -= BaseX; + y -= BaseY; + + if ((unsigned)x < RadarWidth && (unsigned)y < RadarHeight) { + x = RadarX + (x / ZoomFactor); + y = RadarY + (y / ZoomFactor); + if (change) { + ptr_x = x; + ptr_y = y; + } + return(1); + } + return(-1); + } + return(0); +} + + +/*********************************************************************************************** + * RadarClass::Click_Cell_Calc -- Determines what cell the pixel coordinate is over. * + * * + * This routine will examine the pixel coordinate provided and determine what cell it * + * represents. If the radar map is not active or the coordinates are not positioned over * + * the radar map, then it will fall into the base class corresponding routine. * + * * + * INPUT: x,y -- The pixel coordinate to convert into a cell number. * + * * + * OUTPUT: Returns with the cell number that the coordinate is over or -1 if not over any * + * cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +CELL RadarClass::Click_Cell_Calc(int x, int y) +{ + int result = Click_In_Radar(x, y, true); + switch (result) { + case 1: + return(XY_Cell(x, y)); + + case -1: + return(-1); + } + return(DisplayClass::Click_Cell_Calc(x, y)); +} + + +/*********************************************************************************************** + * RadarClass::Map_Cell -- Updates radar map when a cell becomes mapped. * + * * + * This routine will update the radar map if a cell becomes mapped. * + * * + * INPUT: cell -- The cell that is being mapped. * + * * + * house -- The house that is doing the mapping. * + * * + * OUTPUT: bool; Was the cell mapped (for the first time) by this routine? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Map_Cell(CELL cell, HouseClass * house) +{ + if (DisplayClass::Map_Cell(cell, house)) { + Radar_Pixel(cell); + return(true); + } + return(false); +} + +void RadarClass::Cursor_Cell(CELL cell, int value) +{ + int temp = (*this)[cell].IsRadarCursor; + + /* + ** If this cell is not on the radar don't botther doing anything. + */ + if (In_Radar(cell) && temp != value) { + /* + ** Record the new state of this cell. + */ + (*this)[cell].IsRadarCursor = value; + + /* + ** If we are erasing then erase the cell. + */ +////// ST 8/13/96 2:23PM + if (value == FALSE) { + Plot_Radar_Pixel(cell); +////// + } + } +} + +void RadarClass::Mark_Radar(int x1, int y1, int x2, int y2, int value, int barlen) +{ + int x, y; + /* + ** First step is to convert pixel coordinates back to a CellX and CellY. + */ + x1 = RadarX + (x1 / ZoomFactor); + y1 = RadarY + (y1 / ZoomFactor); + x2 = RadarX + (x2 / ZoomFactor); + y2 = RadarY + (y2 / ZoomFactor); + + /* + ** Now we need to convert the Pixel length to a cell length. + */ + barlen = (barlen / ZoomFactor)+1; + + /* + ** Now lets loop through and mark the map with the proper value. + */ + for (int lp = 0; lp <= barlen; lp++) { + /* + ** Do Horizontal action to upper and lower left corners. + */ + x = x1 + lp; + Cursor_Cell(XY_Cell(x,y1), value); + Cursor_Cell(XY_Cell(x,y2), value); + /* + ** Do Horizontal Action to upper and lower right corners + */ + x = x2 - lp; + Cursor_Cell(XY_Cell(x,y1), value); + Cursor_Cell(XY_Cell(x,y2), value); + /* + ** Do Vertical Action to left and right upper corners + */ + y = y1 + lp; + Cursor_Cell(XY_Cell(x1,y), value); + Cursor_Cell(XY_Cell(x2,y), value); + + /* + ** Do Vertical action to left and right lower corners. + */ + y = y2 - lp; + Cursor_Cell(XY_Cell(x1,y), value); + Cursor_Cell(XY_Cell(x2,y), value); + } +} + + + +/*********************************************************************************************** + * RadarClass::Cell_XY_To_Radar_Pixel-- Adjust the position of the radar map cursor. * + * * + * This routine will adjust the location (and visibility) of the radar * + * map cursor. It handles all restoration, drawing, and flashing. * + * * + * INPUT: pos - Cell position for the cursor. If the value is -1 then * + * the cursor will be hidden. If the value is equal to * + * the last value passed in then cursor flashing will * + * be maintained. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1991 JLB : Created. * + * 11/17/1995 PWG : Created. * + *=============================================================================================*/ +void RadarClass::Cell_XY_To_Radar_Pixel(int cellx, int celly, int &x, int &y) +{ + x = (cellx - RadarX) * ZoomFactor; + y = (celly - RadarY) * ZoomFactor; +} + +/*********************************************************************************************** + * RadarClass::Radar_Cursor -- Adjust the position of the radar map cursor. * + * * + * This routine will adjust the location (and visibility) of the radar * + * map cursor. It handles all restoration, drawing, and flashing. * + * * + * INPUT: pos - Cell position for the cursor. If the value is -1 then * + * the cursor will be hidden. If the value is equal to * + * the last value passed in then cursor flashing will * + * be maintained. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1991 JLB : Created. * + * 11/17/1995 PWG : Created. * + *=============================================================================================*/ +#pragma argsused +void RadarClass::Radar_Cursor(int forced) +{ + static _last_pos = -1; + static _last_frame = -1; + GraphicViewPortClass *oldpage; + int x1, y1, x2, y2; + /* + ** figure out these function calls as we will need to call them multiple times. + */ + int tac_cell = Coord_Cell(TacticalCoord); + int tac_cell_x = Cell_X(tac_cell); + int tac_cell_y = Cell_Y(tac_cell); + int barlen = 6; + /* + ** If the current tactical cell is invalid or we haven't moved and we are not forced to redraw then + ** just skip the redraw process. + */ + if (tac_cell != -1 && _last_pos == tac_cell && _last_frame == SpecialRadarFrame && !forced) return; + + if ( _last_pos != -1 ) { + /* + ** The first thing we need to do is take care of erasing the last radar cell position. We do this + ** by converting to pixel coordinates, then adjusting for the pixel coords for the current frame and + ** finally taking care of calling the erase procedure which will convert the pixel coordinates back + ** to the cells that need to be redraw. + **/ + int last_cell_x = Cell_X(_last_pos); + int last_cell_y = Cell_Y(_last_pos); + + Cell_XY_To_Radar_Pixel(last_cell_x, last_cell_y, x1, y1); + Cell_XY_To_Radar_Pixel(last_cell_x + Lepton_To_Cell(TacLeptonWidth), last_cell_y + Lepton_To_Cell(TacLeptonHeight), x2, y2); + x2--; + y2--; + + /* + ** Adjust the current coordinates based on the last animation frame. + */ + x1-= _last_frame; + y1-= _last_frame; + x2+= _last_frame; + y2+= _last_frame; + /* + ** Finally mark the map (actually remove the marks that indicate the radar cursor was there + */ + Mark_Radar(x1, y1, x2, y2, FALSE, barlen); + } + + + /* + ** find the upper left and lower right corners of the radar cursor. Remember to adjust x2 and y2 back + ** by one pixel as they will not be pointing to the right value otherwise. They point one cell ahead + ** of where they should. + */ + Cell_XY_To_Radar_Pixel(tac_cell_x, tac_cell_y, x1, y1); + Cell_XY_To_Radar_Pixel(tac_cell_x + Lepton_To_Cell(TacLeptonWidth), tac_cell_y + Lepton_To_Cell(TacLeptonHeight), x2, y2); + x2--; + y2--; + + /* + ** Adjust the coordinates based on the current frame of radar animation. + */ + x1-= SpecialRadarFrame; + y1-= SpecialRadarFrame; + x2+= SpecialRadarFrame; + y2+= SpecialRadarFrame; + + Mark_Radar(x1, y1, x2, y2, TRUE, barlen); + + /* + ** setup a graphic view port class so we can write all the pixels relative + ** to 0,0 rather than relative to full screen coordinates. + */ + oldpage = Set_Logic_Page(HidPage); + GraphicViewPortClass draw_window(LogicPage->Get_Graphic_Buffer(), + RadX + RadOffX + BaseX + LogicPage->Get_XPos(), + RadY + RadOffY + BaseY + LogicPage->Get_YPos(), + RadarWidth, + RadarHeight); + + draw_window.Draw_Line(x1, y1, x1 + barlen, y1, LTGREEN); + draw_window.Draw_Line(x1, y1, x1, y1 + barlen, LTGREEN); + + // Draw upper right hand corner + draw_window.Draw_Line(x2 - barlen, y1, x2, y1, LTGREEN); + draw_window.Draw_Line(x2, y1, x2, y1 + barlen, LTGREEN); + + // Draw lower left hand corner + draw_window.Draw_Line(x1, y2 - barlen, x1, y2, LTGREEN); + draw_window.Draw_Line(x1, y2, x1 + barlen, y2, LTGREEN); + + // Draw lower right hand corner + draw_window.Draw_Line(x2, y2 - barlen, x2, y2, LTGREEN); + draw_window.Draw_Line(x2 - barlen, y2, x2, y2, LTGREEN); +#if(0) + draw_window.Draw_Rect(x1, y1, x2, y2, WHITE); +#endif + +#if(FALSE) + if (oldpage == &SeenBuff) { + Hide_Mouse(); + HidPage.Blit( SeenBuff, + (int)(RadX + RadOffX + BaseX), + (int)(RadY + RadOffY + BaseY), + (int)(RadX + RadOffX + BaseX), + (int)(RadY + RadOffY + BaseY), + (int)draw_window.Get_Width(), + (int)draw_window.Get_Height(), + (BOOL)FALSE); + + Show_Mouse(); + } +#endif + Set_Logic_Page(oldpage); + _last_pos = tac_cell; + _last_frame = SpecialRadarFrame; + RadarCursorRedraw = FALSE; +} + + +/*************************************************************************** + * RadarClass::Radar_Anim -- Renders current frame of radar animation * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1995 PWG : Created. * + *=========================================================================*/ +void RadarClass::Radar_Anim(void) +{ + /* + ** Do nothing if we're in player-name mode + */ + if (IsPlayerNames) + return; + + if (!Map.IsSidebarActive) return; + + GraphicViewPortClass *oldpage= Set_Logic_Page(HidPage); + GraphicViewPortClass draw_window(LogicPage->Get_Graphic_Buffer(), + RadX + RadOffX + LogicPage->Get_XPos(), + RadY + RadOffY + LogicPage->Get_YPos(), + RadIWidth, + RadIHeight); + + Draw_Box(RadX+RadOffX-1, RadY+RadOffY-1, RadIWidth+2, RadIHeight+2, BOXSTYLE_RAISED, true); + draw_window.Clear(); + CC_Draw_Shape(RadarAnim, RadarAnimFrame, RadX, RadY+1, WINDOW_MAIN, SHAPE_NORMAL); + + Flag_To_Redraw(false); + Set_Logic_Page(oldpage); +} + + +/*********************************************************************************************** + * RadarClass::AI -- Processes radar input (non-tactical). * + * * + * This routine intercepts any player input that concerns the radar map, but not those * + * areas that represent the tactical map. These are handled by the tactical map AI * + * processor. Primarily, this routine handles the little buttons that border the radar * + * map. * + * * + * INPUT: input -- The player input code. * + * * + * x,y -- Mouse coordinate parameters to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + * 12/26/1994 JLB : Moves tactical map with click or drag. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + *=============================================================================================*/ +void RadarClass::AI(KeyNumType & input, int x, int y) +{ + /* + ** Check to see if we need to animate the radar cursor + */ + if (IsRadarActive && Map.IsSidebarActive && SpecialRadarFrame) { + SpecialRadarFrame--; + RadarCursorRedraw = TRUE; + IsToRedraw = TRUE; + Flag_To_Redraw(FALSE); + } + + /* + ** Check goes here to see if there is enough power to run the radar + */ + if (IsRadarActivating) { + if (!DoesRadarExist) { + RadarAnimFrame++; + if (RadarAnimFrame < RADAR_ACTIVATED_FRAME) { + IsToRedraw = true; + Flag_To_Redraw(false); + } else { + DoesRadarExist = true; + Radar_Activate(3); + } + } else { + RadarAnimFrame--; + if (RadarAnimFrame > RADAR_ACTIVATED_FRAME) { + IsToRedraw = true; + Flag_To_Redraw(false); + } else { + Radar_Activate(3); + } + } + } + if (IsRadarDeactivating) { + RadarAnimFrame++; + if (RadarAnimFrame == MAX_RADAR_FRAMES) { + IsRadarDeactivating = false; + } else { + IsToRedraw = true; + Flag_To_Redraw(false); + } + } + + DisplayClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * RadarClass::TacticalClass::Action -- I/O function for the radar map. * + * * + * This is the main action function for handling player I/O on the radar map. It processes * + * mouse clicks as well as mouse moves. * + * * + * INPUT: flags -- The event flags that trigger this function call. * + * * + * key -- Reference the keyboard event that applies to the trigger event. * + * * + * OUTPUT: Should further processing of the input list be aborted? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +int RadarClass::TacticalClass::Action(unsigned flags, KeyNumType & key) +{ + CELL cell; // cell num click happened over + int x,y; // Sub cell pixel coordinates. + int cellx,celly; // Sub cell pixel coordinates. + bool shadow; // is the cell in shadow or not + ObjectClass *object = 0; // what object is in the cell + ActionType action = ACTION_NONE; // Action possible with currently selected object. + + /* + ** Force any help label to disappear when the mouse is held over the + ** radar map. + */ + if (Map.IsSidebarActive) { + Map.Help_Text(TXT_NONE); + } + + if (!Map.IsRadarActive) { + if (Map.IsSidebarActive) { + Map.Override_Mouse_Shape(MOUSE_NORMAL, true); + } + return(false); + } + + /* + ** Disable processing if the player names are up + */ + if (Map.Is_Player_Names()) { + GadgetClass::Action(0, key); + return(true); + } + + /* + ** Set some working variables that depend on the mouse position. For the press + ** or release event, special mouse queuing storage variables are used. Other + ** events must use the current mouse position globals. + */ + if (flags & (LEFTPRESS|LEFTRELEASE|RIGHTPRESS|RIGHTRELEASE)) { + x = _Kbd->MouseQX; + y = _Kbd->MouseQY; + } else { + x = Get_Mouse_X(); + y = Get_Mouse_Y(); + } + + int result = Map.RadarClass::Click_In_Radar(x, y, false); + + if (result == 1) { + cell = Map.RadarClass::Click_Cell_Calc(x, y); + if (cell != -1) { + shadow = (!Map[cell].IsVisible && !Debug_Unshroud); + cellx = 12; + celly = 12; + + /* + ** Determine the object that the mouse is currently over. + */ + if (!shadow) { + object = Map.Cell_Object(cell, cellx, celly); + } + + /* + ** If there is a currently selected object, then the action to perform if + ** the left mouse button were clicked must be determined. + */ + if (CurrentObject.Count()) { + if (object) { + action = CurrentObject[0]->What_Action(object); + } else { + action = CurrentObject[0]->What_Action(cell); + } + + /* + ** If this is not a valid radar map action then we are not going to do + ** anything. + */ + switch (action) { + case ACTION_MOVE: + case ACTION_NOMOVE: + case ACTION_ATTACK: + case ACTION_ENTER: + case ACTION_CAPTURE: + case ACTION_SABOTAGE: + break; + + default: + action = ACTION_NONE; + object = NULL; + break; + } + + /* + ** On the radar map the only reason we would want the normal cursor to + ** appear is if we were over one of our own selected units. Otherwise + ** we can't move there. + **/ + if (action == ACTION_NONE) { + if (object && object->IsSelected) { + object = NULL; + } else { + action = ACTION_NOMOVE; + } + } + + /* + ** A right mouse button press toggles the zoom mode. + */ + if (flags & RIGHTPRESS) { + Map.Mouse_Right_Press(); + } + + /* + ** When the mouse buttons aren't pressed, only the mouse cursor shape is processed. + ** The shape changes depending on what object the mouse is currently over and what + ** object is currently selected. + */ + if (flags & LEFTUP) { + Map.Mouse_Left_Up(shadow, object, action, true); + } + + /* + ** Normal actions occur when the mouse button is released. The press event is + ** intercepted and possible rubber-band mode is flagged. + */ + if (flags & LEFTPRESS) { + Map.Mouse_Left_Release(cell, cellx, celly, object, action, true); + } + + } else { + + Map.Set_Default_Mouse(MOUSE_RADAR_CURSOR, !Map.IsZoomed); + + if (flags & LEFTPRESS) { + + cell = Map.RadarClass::Click_Cell_Calc(x, y); + if (cell != -1) { + int cellx = Cell_X(cell); + int celly = Cell_Y(cell); + cellx -= Lepton_To_Cell(Map.TacLeptonWidth) / 2; + cellx = MAX(cellx, Map.MapCellX); + celly -= Lepton_To_Cell(Map.TacLeptonHeight) / 2; + celly = MAX(celly, Map.MapCellY); + cell = XY_Cell(cellx, celly); + shadow = (!Map[cell].IsVisible && !Debug_Unshroud); + Map.Set_Tactical_Position(Cell_Coord(cell)); + cell = Coord_Cell(Map.DesiredTacticalCoord); + Map.DisplayClass::IsToRedraw = true; + //Map.Flag_To_Redraw(false); + Map.Flag_To_Redraw(true); + Map.SpecialRadarFrame = 4; + } + } + + /* + ** A right mouse button press toggles the zoom mode. + */ + if (flags & RIGHTPRESS) { + Map.Zoom_Mode(cell); + } + } + } + } + if (result == -1) { + Map.Override_Mouse_Shape(MOUSE_NORMAL, true); + } + GadgetClass::Action(0, key); + return(true); +} + + +/*********************************************************************************************** + * RadarClass::Refresh_Cells -- Intercepts refresh request and updates radar if needed * + * * + * This routine intercepts the refresh cells request and if it detects that the sidebar * + * should be rerendered, it flags the radar map to redraw during the next draw operation. * + * * + * INPUT: cell -- The origin cell that the refresh cell offset list is based upon. * + * * + * list -- Pointer to the list of offsets from the origin cell that specifies the * + * cells to be flagged for redraw. If the list starts with the special * + * code to refresh the sidebar, then this routine recognizes it and flags * + * the radar map to be redrawn accordingly. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Refresh_Cells(CELL cell, short const *list) +{ + if (*list == REFRESH_SIDEBAR) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + DisplayClass::Refresh_Cells(cell, list); +} + + +/*********************************************************************************************** + * RadarClass::Set_Radar_Position -- Sets the radar position to center around specified cell. * + * * + * This routine will try to center the radar map around the cell position specified. * + * * + * INPUT: cell -- The cell to try and position the radar map around. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Set_Radar_Position(CELL cell) +{ + int oldx, oldy; + int newx, newy; + int newcell; + + if (ZoomFactor != 1) { +#if(FALSE) + oldx = (Cell_X(cell) - MapCellX) - (RadarCellWidth / 2); + oldy = (Cell_Y(cell) - MapCellY) - (RadarCellHeight / 2); +#else + oldx = (Cell_X(cell) - MapCellX); + oldy = (Cell_Y(cell) - MapCellY); +#endif + } else { + oldx = 0; + oldy = 0; + } + + Confine_Rect(&oldx, &oldy, RadarCellWidth, RadarCellHeight, MapCellWidth, MapCellHeight); + + newx = oldx + MapCellX; + newy = oldy + MapCellY; + newcell = XY_Cell(newx, newy); + + if (RadarCell != newcell) { + int forced = FALSE; + int xmod = newx; + int ymod = newy; + + int radx = (Cell_X(RadarCell)) - xmod; + int rady = (Cell_Y(RadarCell)) - ymod; + + RadarX = newx; + RadarY = newy; + RadarCell = newcell; + + if (Map.IsSidebarActive&& Map.IsRadarActive) { + int radw = RadarCellWidth-ABS(radx); // Replicable width. + int radh = RadarCellHeight-ABS(rady); // Replicable height. + + if (radw < 1) forced = true; + if (radh < 1) forced = true; + + if (!forced && (radw != RadarWidth || radh != RadarHeight)) { + /* + ** Blit the section that is actually overlapping. + */ + if (OverlappedVideoBlits || !HidPage.Get_IsDirectDraw()){ + HidPage.Blit(HidPage, + (((radx < 0) ? -radx : 0) * ZoomFactor) + RadX + RadOffX + BaseX, + (((rady < 0) ? -rady : 0) * ZoomFactor) + RadY + RadOffY + BaseY, + (((radx < 0) ? 0 : radx) * ZoomFactor) + RadX+ RadOffX + BaseX, + (((rady < 0) ? 0 : rady) * ZoomFactor) + RadY + RadOffY + BaseY, + radw * ZoomFactor, + radh * ZoomFactor); + }else{ + /* + ** System does not support overlapped blitting of video surfaces. + ** Blit it in 2 stages using an intermediate buffer. + */ + GraphicBufferClass temp_surface; + temp_surface.Init((RadarWidth + 16) & 0xfffffff0, + (RadarHeight + 16) & 0xfffffff0, + NULL, 0, (GBC_Enum) GBC_VIDEOMEM); + + HidPage.Blit(temp_surface, (((radx < 0) ? -radx : 0) * ZoomFactor) + RadX + RadOffX + BaseX, + (((rady < 0) ? -rady : 0) * ZoomFactor) + RadY + RadOffY + BaseY, + 0, + 0, + RadarWidth, + RadarHeight); + + temp_surface.Blit (HidPage,0, + 0, + (((radx < 0) ? 0 : radx) * ZoomFactor) + RadX + RadOffX + BaseX, + (((rady < 0) ? 0 : rady) * ZoomFactor) + RadY + RadOffY + BaseY, + radw * ZoomFactor, + radh * ZoomFactor); + } + + /* + ** Now we need to flag the section of the map that is going to redraw. + */ + if ( radx != 0 ) { + int min; + int max; + if ( radx < 0 ) { // this mean regen the right edge + min = radw; + max = radw+ABS(radx); + } else { // this mean regen the left edge + min = 0; + max = radx; + } + for (int x = min; x < max; x++ ) { + for (int y = 0; y < RadarCellHeight; y++ ) { + Radar_Pixel(XY_Cell(newx + x, newy + y)); + } + } + } + if ( newy != 0 ) { + int min; + int max; + if ( rady < 0 ) { // this mean regen the bottom edge + min = radh; + max = radh+ABS(rady); + } else { // this mean regen the top edge + min = 0; + max = rady; + } + for (int y = min; y < max; y++ ) { + for ( int x = 0; x < RadarCellWidth; x++ ) { + Radar_Pixel(XY_Cell(newx + x, newy + y)); + } + } + } + } + } + RadarCursorRedraw = IsRadarActive; + IsToRedraw = IsRadarActive; + Flag_To_Redraw(false); + if (ZoomFactor > 4) { + FullRedraw = forced; + } + } else { + RadarCursorRedraw = IsRadarActive; + IsToRedraw = IsRadarActive; + Flag_To_Redraw(false); + } +} + + +/*********************************************************************************************** + * RadarClass::Radar_Position -- Returns with the current position of the radar map. * + * * + * This returns the cell number of the upper left corner of the radar map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the radar map upper left corner cell position. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +CELL RadarClass::Radar_Position(void) +{ + return(RadarCell); +} + + +/*********************************************************************************************** + * RadarClass::Set_Map_Dimensions -- Sets the tactical map dimensions. * + * * + * This routine is called when the tactical map changes its dimensions. This occurs when * + * the tactical map moves and when the sidebar pops on or off. * + * * + * INPUT: x,y -- The cell coordinate of the upper left corner of the tactical map. * + * * + * w,y -- The cell width and height of the tactical map. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Set_Map_Dimensions(int x, int y, int w, int h) +{ + Set_Radar_Position(XY_Cell(x, y)); + DisplayClass::Set_Map_Dimensions(x, y, w, h); +} + + +/*********************************************************************************************** + * RadarClass::Set_Tactical_Position -- Sets the map's tactical position and adjusts radar to * + * * + * This routine is called when the tactical map is to change position. The radar map might * + * be adjusted as well by this routine. * + * * + * INPUT: coord -- The new coordinate to use for the upper left corner of the tactical * + * map. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/17/1995 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Set_Tactical_Position(COORDINATE coord) +{ + DisplayClass::Set_Tactical_Position(coord); + Set_Radar_Position(Coord_Cell(DesiredTacticalCoord)); +} + + +/*********************************************************************************************** + * RadarClass::Cell_On_Radar -- Determines if a cell is currently visible on radar. * + * * + * This routine will examine the specified cell number and return whether it is visible * + * on the radar map. This depends on the radar map position. * + * * + * INPUT: cell -- The cell number to check. * + * * + * OUTPUT: Is the specified cell visible on the radar map currently? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/03/1995 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Cell_On_Radar(CELL cell) +{ + if ((unsigned)cell > MAP_CELL_TOTAL) + return(false); + + int x = Cell_X(cell) - RadarX; + int y = Cell_Y(cell) - RadarY; + return (!((unsigned)x >= RadarCellWidth || (unsigned)y >= RadarCellHeight)); + +// if (!IsZoomed) { +// return(true); +// } +// return(!(((Cell_X(cell) - RadarX) > RadarCellWidth) || ((Cell_Y(cell) - RadarY) > RadarCellHeight))); +} + +/*********************************************************************************************** + * RadarClass::Player_Names -- toggles the Player-Names mode of the radar map * + * * + * INPUT: * + * on true = turn on; false = turn off * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/07/1995 BRR : Created. * + *=============================================================================================*/ +void RadarClass::Player_Names(bool on) +{ + IsPlayerNames = on; + IsToRedraw = true; + if (on) { + Flag_To_Redraw(true); +// Flag_To_Redraw(false); + } else { + Flag_To_Redraw(true); // force drawing of the plate + } +} + + +/*********************************************************************************************** + * Draw_Names -- draws players' names on the radar map * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/07/1995 BRR : Created. * + *=============================================================================================*/ +void RadarClass::Draw_Names(void) +{ + int c_idx; + HousesType house; + HouseClass *ptr; + int y; + char txt[40]; + unsigned char id; + int i; + HousesType h; + int kills; + int color; + TextPrintType style; + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + /* + ** Do nothing if the sidebar isn't there + */ + if (!Map.IsSidebarActive) { + return; + } + + CC_Draw_Shape(RadarAnim, RADAR_ACTIVATED_FRAME, RadX, RadY+1, WINDOW_MAIN, SHAPE_NORMAL); + LogicPage->Fill_Rect( RadX + RadOffX, + RadY + RadOffY, + RadX + RadOffX + RadIWidth - 1, + RadY + RadOffY + RadIHeight - 1, + BLACK); + + y = RadY + RadOffY; + + Fancy_Text_Print (TXT_NAME_COLON, RadX + RadOffX, y, LTGREY, TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + + Fancy_Text_Print (TXT_KILLS_COLON, RadX + RadOffX + RadIWidth - 2, y, + LTGREY, TBLACK, TPF_RIGHT | TPF_6PT_GRAD | TPF_NOSHADOW | TPF_USE_GRAD_PAL); + + y += 6*factor+1; + + LogicPage->Draw_Line(RadX + RadOffX, y, + RadX + RadOffX + RadIWidth - 1, y, LTGREY); + + y += 2*factor; + + for (house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + MPlayerMax); house++) { + ptr = HouseClass::As_Pointer(house); + + if (!ptr) + continue; + + /* + ** Decode this house's color + */ + c_idx = ptr->RemapColor; + + if (ptr->IsDefeated) { + color = GREY; + style = TPF_6PT_GRAD | TPF_NOSHADOW | TPF_USE_GRAD_PAL; + } else { + color = MPlayerTColors[c_idx]; + style = TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW; + } + + /* + ** Initialize our message + */ + txt[0] = 0; + + /* + ** If the house is non-human, generate the message + */ + if (!ptr->IsHuman) { + sprintf(txt,"%s", Text_String(TXT_COMPUTER)); + } else { + + /* + ** For a human house: + ** - Compute the multiplayer ID for this house + ** - find the name for this player + */ + id = Build_MPlayerID (c_idx,ptr->ActLike); + for (i = 0; i < MPlayerCount; i++) { + if (id == MPlayerID[i]) { + sprintf(txt,"%s",MPlayerNames[i]); + break; + } + } + } + + /* + ** Print the player name, and the # of kills + */ + if (strlen(txt)) { + if (strlen(txt) > 9) { + txt[9] = '.'; + txt[10] = '\0'; + } + Fancy_Text_Print (txt, RadX + RadOffX, y, color, BLACK, style); + + kills = 0; + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + kills += ptr->UnitsKilled[h]; + kills += ptr->BuildingsKilled[h]; + } + sprintf(txt, "%2d", kills); + Fancy_Text_Print (txt, RadX + RadOffX + RadIWidth - 2, y, + color, BLACK, style | TPF_RIGHT); + + y += 6*factor+1; + + } + } +} diff --git a/RADAR.H b/RADAR.H new file mode 100644 index 0000000..ed0cc02 --- /dev/null +++ b/RADAR.H @@ -0,0 +1,213 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\radar.h_v 2.17 16 Oct 1995 16:48:04 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RADAR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : December 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RADAR_H +#define RADAR_H + +#include "display.h" + +class RadarClass: public DisplayClass +{ + public: + int RadX; + int RadOffX; + int RadY; + int RadOffY; + int RadWidth; + int RadHeight; + int RadIWidth; + int RadIHeight; + int RadPWidth; + int RadPHeight; + + RadarClass(void); + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time inits + virtual void Init_Clear(void); // Clears all to known state + + virtual bool Map_Cell(CELL cell, HouseClass * house); + virtual CELL Click_Cell_Calc(int x, int y); + virtual void AI(KeyNumType &input, int x, int y); + virtual void Draw_It(bool complete=false); + virtual void Refresh_Cells(CELL cell, short const *list); + virtual void Set_Map_Dimensions(int x, int y, int w, int h); +// virtual void Set_Tactical_Position(int x, int y, int leptonx=0, int leptony=0); +// virtual void Set_Tactical_Position(CELL cell); + virtual void Set_Tactical_Position(COORDINATE coord); + void Zoom_Mode(CELL cell); + int Click_In_Radar(int &x, int &y, bool change=false); + void Cell_XY_To_Radar_Pixel(int cellx, int celly, int &x, int &y); + + void Set_Radar_Position(CELL cell); + CELL Radar_Position(void); + bool Radar_Activate(int control); + void Plot_Radar_Pixel(CELL cell); + void Radar_Pixel(CELL cell); + void Coord_To_Radar_Pixel(COORDINATE coord, int &x, int &y); + void Cursor_Cell(CELL cell, int value); + void RadarClass::Mark_Radar(int x1, int y1, int x2, int y2, int value, int barlen); + void Radar_Cursor(int forced = false); + void Render_Terrain(CELL cell, int x, int y, int size); + bool Cell_On_Radar(CELL cell); + void Render_Infantry(CELL cell, int x, int y, int size); + void Render_Overlay(CELL cell, int x, int y, int size); + void Radar_Anim(void); + bool Is_Radar_Active(void) {return IsRadarActive;}; + bool Is_Radar_Existing(void) {return(DoesRadarExist);}; + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Toggles player names on & off + */ + void Player_Names(bool on); + int Is_Player_Names(void) {return IsPlayerNames;} + void Draw_Names(void); + int Is_Zoomed(void) {return IsZoomed;} + + protected: + + /* + ** Radar map constant values. + */ + enum RadarClassEnums { + RADAR_ACTIVATED_FRAME=22, + MAX_RADAR_FRAMES = 41 + }; + + /* + ** If the radar map must be completely redrawn, then this flag will be true. + ** Typical causes of this would be when the radar first appears, or when the + ** screen has been damaged. + */ + unsigned IsToRedraw:1; + unsigned RadarCursorRedraw:1; + + /* + ** If the radar map is visible then this flag is true. + */ + unsigned DoesRadarExist:1; + unsigned IsRadarActive:1; + unsigned IsRadarActivating:1; + unsigned IsRadarDeactivating:1; + + /* + ** Special radar frame is set when a new location is selected on the + ** radar map. It counts down through the special radar cursors until + ** either the radar cursor becomes normal or the radar cursor is moved + ** again. + */ + unsigned SpecialRadarFrame:3; + unsigned RadarAnimFrame:6; + + static void const * RadarAnim; + + /* + ** This gadget class is used for capturing input to the tactical map. All mouse input + ** will be routed through this gadget. + */ + class TacticalClass : public GadgetClass { + public: + TacticalClass(void) : GadgetClass(0,0,0,0,LEFTPRESS|LEFTRELEASE|LEFTHELD|LEFTUP|RIGHTPRESS,true) {}; + + protected: + virtual int Action(unsigned flags, KeyNumType & key); + friend class RadarClass; + }; + friend class TacticalClass; + + /* + ** This is the "button" that tracks all input to the tactical map. + ** It must be available to derived classes, for Save/Load purposes. + */ + static TacticalClass RadarButton; + + private: + + /* + ** The current radar position as the upper left corner cell for the + ** radar map display. The width and height is controlled by the + ** actual dimensions of the radar map display box (in pixels). + */ + unsigned RadarX; + unsigned RadarY; + unsigned RadarCell; + + /* + ** This is the origin (pixel offsets) for the upper left corner + ** of the radar map within the full radar map area of the screen. + ** This is biased so that the radar map, when smaller than full + ** size will appear centered. + */ + unsigned BaseX; + unsigned BaseY; + + unsigned RadarWidth; + unsigned RadarCellWidth; + unsigned RadarHeight; + unsigned RadarCellHeight; + + /* + ** If the radar map is in zoom mode, then this value will be true. + */ + unsigned IsZoomed:1; + + /* + ** This flag is true if the radar map is in its special show-the-player + ** names mode. + */ + unsigned IsPlayerNames:1; + + /* + ** This is the list of radar pixels that need to be updated. Only a partial + ** list is maintained for maximum speed. + */ + unsigned PixelPtr; + int ZoomFactor; + enum PixelStackEnums {PIXELSTACK=200}; + CELL PixelStack[PIXELSTACK]; +}; + + +#endif diff --git a/RADIO.CPP b/RADIO.CPP new file mode 100644 index 0000000..8aa7abc --- /dev/null +++ b/RADIO.CPP @@ -0,0 +1,252 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\radio.cpv 2.19 16 Oct 1995 16:50:12 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RADIO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : June 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RadioClass::Debug_Dump -- Displays the current status of the radio to the mono monitor. * + * RadioClass::Receive_Message -- Handles receipt of a radio message. * + * RadioClass::Transmit_Message -- Transmit message from one object to another. * + * RadioClass::Limbo -- When limboing a unit will always break radio contact. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/* +** These are the text representations of the radio messages that can be transmitted. +*/ +char const * RadioClass::Messages[RADIO_COUNT] = { + "hisssss", + "Roger.", + "Come in.", + "Over and out.", + "Requesting transport.", + "Attach to transport.", + "I've got a delivery for you.", + "I'm performing load/unload maneuver. Be careful.", + "I'm clear.", + "You are clear to unload. Driving away now.", + "Am unable to comply.", + "I'm starting construction now... act busy.", + "I've finished construction. You are free.", + "We bumped, redraw yourself please.", + "I'm trying to load up now.", + "May I become a passenger?", + "Are you ready to receive shipment?", + "Are you trying to become a passenger?", + "Move to location X.", + "Do you need to move?", + "All right already. Now what?", + "I'm a passenger now.", + "Backup into refinery now.", + "Run away!", + "Tether established.", + "Tether broken.", + "Repair one step.", + "Are you prepared to fight?", + "Attack this target please.", + "Reload one step.", + "Take this kick! You... You...", + "Take this punch! You... You...", + "Fancy a little fisticuffs, eh?" +}; + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * RadioClass::Debug_Dump -- Displays the current status of the radio to the mono monitor. * + * * + * This displays the radio connection value to the monochrome monitor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void RadioClass::Debug_Dump(MonoClass *mono) const +{ + mono->Set_Cursor(34, 5);mono->Printf(Messages[LastMessage]); + if (Radio) { + mono->Set_Cursor(50, 1);mono->Printf("%04X", Radio->As_Target()); + } + MissionClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * RadioClass::Receive_Message -- Handles receipt of a radio message. * + * * + * This is the base version of what should happen when a radio message is received. It * + * turns the radio off when the "OVER_OUT" message is received. All other messages are * + * merely acknowledged with a "ROGER". * + * * + * INPUT: from -- The object that is initiating this radio message (always valid). * + * * + * message -- The radio message received. * + * * + * param -- Reference to optional value that might be used to return more * + * information than can be conveyed in the simple radio response * + * messages. * + * * + * OUTPUT: Returns with the response radio message. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + * 09/24/1994 JLB : Streamlined to be only a communications carrier. * + * 05/22/1995 JLB : Recognized who is sending the message * + *=============================================================================================*/ +RadioMessageType RadioClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + /* + ** Keep a record of the last message received by this radio. + */ + LastMessage = message; + + /* + ** When this message is received, it means that the other object + ** has already turned its radio off. Turn this radio off as well. + ** This only applies if the message is coming from the object that + ** has an established conversation with this object. + */ + if (from == Radio && message == RADIO_OVER_OUT) { + MissionClass::Receive_Message(from, message, param); + Radio_Off(); + return(RADIO_ROGER); + } + + /* + ** The "hello" message is an attempt to establish contact. If this radio + ** is already in an established conversation with another object, then + ** return with "negative". If all is well, return with "roger". + */ + if (message == RADIO_HELLO && Strength) { + if (Radio == from || !Radio) { + Radio = from; + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + } + + return(MissionClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * RadioClass::Transmit_Message -- Transmit message from one object to another. * + * * + * This routine is used to transmit a radio message from this object to another. Most * + * inter object coordination is handled through this mechanism. * + * * + * INPUT: to -- Pointer to the object that will receive the radio message. * + * * + * message -- The message itself (see RadioType). * + * * + * param -- Optional reference to parameter that might be used to pass or * + * receive additional information. * + * * + * OUTPUT: Returns with the response radio message from the receiving object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +RadioMessageType RadioClass::Transmit_Message(RadioMessageType message, long & param, RadioClass * to) +{ + if (!to) { + to = Contact_With_Whom(); + } + + /* + ** If there is no target for the radio message, then always return static. + */ + if (!to) return(RADIO_STATIC); + + /* + ** Handle some special case processing that occurs when certain messages + ** are transmitted. + */ + if (to == Radio && message == RADIO_OVER_OUT) { + Radio = 0; + } + + /* + ** If this object is not in radio contact but the message + ** indicates that radio contact should be established, then + ** try to do so. If the other party agrees then contact + ** is established. + */ + if (/*!Radio &&*/ message == RADIO_HELLO) { + Transmit_Message(RADIO_OVER_OUT); + if (to->Receive_Message(this, message, param) == RADIO_ROGER) { + Radio = to; + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + } + + return(to->Receive_Message(this, message, param)); +} + + +/*********************************************************************************************** + * RadioClass::Limbo -- When limboing a unit will always break radio contact. * + * * + * This routine will break radio contact as the object is entering limbo state. * + * * + * INPUT: none * + * * + * OUTPUT: Was the object successfully limboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +bool RadioClass::Limbo(void) +{ + if (!IsInLimbo) { + Transmit_Message(RADIO_OVER_OUT); + } + return(MissionClass::Limbo()); +} + + +RadioMessageType RadioClass::Transmit_Message(RadioMessageType message, RadioClass * to) {return(Transmit_Message(message, LParam, to));}; diff --git a/RADIO.H b/RADIO.H new file mode 100644 index 0000000..cf6023f --- /dev/null +++ b/RADIO.H @@ -0,0 +1,108 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\radio.h_v 2.15 16 Oct 1995 16:45:32 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RADIO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RADIO_H +#define RADIO_H + +//#include "object.h" +#include "mission.h" +//#include "flasher.h" + +class ObjectClass; +class TechnoClass; + + +/**************************************************************************** +** Radio contact is controlled by this class. It handles the mundane chore +** of keeping the radio contact alive as well as broadcasting messages +** to the receiving radio. Radio contact is primarily used when one object +** is in "command" of another. +*/ +class RadioClass : public MissionClass { + private: + + /* + ** This is a record of the last message received by this receiver. + */ + RadioMessageType LastMessage; + + /* + ** This is the object that radio communication has been established + ** with. Although is is only a one-way reference, it is required that + ** the receiving radio is also tuned to the object that contains this + ** radio set. + */ + RadioClass * Radio; + + /* + ** This is a text representation of all the possible radio messages. This + ** text is used for monochrome debug printing. + */ + static char const * Messages[RADIO_COUNT]; + + public: + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + RadioClass(void) {Radio = 0;LastMessage = RADIO_STATIC;}; + virtual ~RadioClass(void) {}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + bool In_Radio_Contact(void) const {return (Radio != 0);}; + void Radio_Off(void) {Radio = 0;}; + TechnoClass * Contact_With_Whom(void) const {return (TechnoClass*)Radio;}; + + // Inherited from base class(es). + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual RadioMessageType Transmit_Message(RadioMessageType message, long & param=LParam, RadioClass * to=NULL); + virtual RadioMessageType Transmit_Message(RadioMessageType message, RadioClass * to); + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + virtual bool Limbo(void); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); +}; + +#endif diff --git a/RAND.CPP b/RAND.CPP new file mode 100644 index 0000000..ffb0e87 --- /dev/null +++ b/RAND.CPP @@ -0,0 +1,173 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\rand.cpv 1.6 16 Oct 1995 16:51:10 JOE_BOSTIC $ */ +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RAND.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 04/25/95 * + * * + * Last Update : June 17, 1995 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Sim_Random -- Returns 0 - 255 * + * Sim_IRandom -- Returns minval to maxval, inclusive * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +int SimRandIndex = 0; + +/*************************************************************************** + * Sim_Random -- Returns 0 - 255 * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 - 255, at random * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/25/1995 BRR : Created. * + * 06/17/1995 JLB : Takes advantage of character math wrap. * + *=========================================================================*/ +int Sim_Random(void) +{ + static unsigned char _randvals[] = { + 0x47, 0xce, 0xc6, 0x6e, 0xd7, 0x9f, 0x98, 0x29, 0x92, 0x0c, 0x74, 0xa2, + 0x65, 0x20, 0x4b, 0x4f, 0x1e, 0xed, 0x3a, 0xdf, 0xa5, 0x7d, 0xb5, 0xc8, + 0x86, 0x01, 0x81, 0xca, 0xf1, 0x17, 0xd6, 0x23, 0xe1, 0xbd, 0x0e, 0xe4, + 0x62, 0xfa, 0xd9, 0x5c, 0x68, 0xf5, 0x7f, 0xdc, 0xe7, 0xb9, 0xc4, 0xb3, + 0x7a, 0xd8, 0x06, 0x3e, 0xeb, 0x09, 0x1a, 0x31, 0x3f, 0x46, 0x28, 0x12, + 0xf0, 0x10, 0x84, 0x76, 0x3b, 0xc5, 0x53, 0x18, 0x14, 0x73, 0x7e, 0x59, + 0x48, 0x93, 0xaa, 0x1d, 0x5d, 0x79, 0x24, 0x61, 0x1b, 0xfd, 0x2b, 0xa8, + 0xc2, 0xdb, 0xe8, 0x2a, 0xb0, 0x25, 0x95, 0xab, 0x96, 0x83, 0xfc, 0x5f, + 0x9c, 0x32, 0x78, 0x9a, 0x9e, 0xe2, 0x8e, 0x35, 0x4c, 0x41, 0xa1, 0x69, + 0x5a, 0xfe, 0xa7, 0xa4, 0xf6, 0x6d, 0xc1, 0x58, 0x0a, 0xcf, 0xea, 0xc3, + 0xba, 0x85, 0x99, 0x8d, 0x36, 0xb6, 0xdd, 0xd3, 0x04, 0xe6, 0x45, 0x0d, + 0x60, 0xae, 0xa3, 0x22, 0x4d, 0xe9, 0xc9, 0x9b, 0xb7, 0x0f, 0x02, 0x42, + 0xf9, 0x0b, 0x8f, 0x43, 0x44, 0x87, 0x70, 0xbe, 0xe3, 0xf8, 0xee, 0xa9, + 0xbc, 0xc0, 0x67, 0x33, 0x16, 0x37, 0x57, 0xad, 0x5e, 0x9d, 0x64, 0x40, + 0x54, 0x05, 0x2c, 0xe0, 0xb2, 0x97, 0x08, 0xaf, 0x75, 0x8a, 0x5b, 0xfb, + 0x4e, 0xbf, 0x91, 0xf3, 0xcb, 0x7c, 0x63, 0xef, 0x89, 0x52, 0x6c, 0x2f, + 0x21, 0x4a, 0xf7, 0xcd, 0x2e, 0xf4, 0xc7, 0x6f, 0x19, 0xb1, 0x66, 0xcc, + 0x90, 0x8c, 0x50, 0x51, 0x26, 0x7b, 0xda, 0x49, 0x80, 0x30, 0x55, 0x1f, + 0xd2, 0xb4, 0xd1, 0xd5, 0x6b, 0xf2, 0x72, 0xbb, 0x13, 0x3d, 0xff, 0x15, + 0x38, 0xe5, 0xd4, 0xde, 0x2d, 0x27, 0x94, 0xa0, 0xd0, 0x39, 0x82, 0x8b, + 0x03, 0xac, 0x3c, 0x34, 0x77, 0xb8, 0xec, 0x00, 0x07, 0x1c, 0x88, 0xa6, + 0x56, 0x11, 0x71, 0x6a, + }; + + ((unsigned char&)SimRandIndex)++; +// SimRandIndex &= 0xff; + return(_randvals[SimRandIndex]); +} + + +/*************************************************************************** + * Sim_IRandom -- returns minval to maxval, inclusive * + * * + * INPUT: * + * minval,maxval range limits * + * * + * OUTPUT: * + * random value * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/25/1995 BRR : Created. * + * 06/17/1995 JLB : Uses fixed point math helper routine. * + *=========================================================================*/ +int Sim_IRandom(int minval, int maxval) +{ + return(Fixed_To_Cardinal((maxval-minval), Sim_Random()) + minval); +} + + +/*--------------------------------------------------------------------------- +This routine can be used to create the _RandVals[] table. It will be +different every time it's run. +---------------------------------------------------------------------------*/ +#if 0 +#include +#include +#include +#include + +void main(void); + +void main(void) +{ + int i; + FILE *fp; + int r; + char is_used[256]; + char rand_val[256]; + + srand ( time ( NULL ) ) ; + + for (i = 0; i < 256; i++) { + is_used[i] = 0; + } + + /* + ** Store the digits 0 to 255 in a random order within the 'rand_val' array + ** This ensures our random number sequence represents every value. + */ + for (i = 0; i < 256; i++) { + if (kbhit()!=0) { + printf("ABORTED!\n"); + break; + } + r = (rand() * 256) / RAND_MAX; // r is the index into array + if (is_used[r]==0) { + is_used[r] = 1; + rand_val[r] = i; + } else { + i--; + } + } + + fp = fopen("rand.cpp","wt"); + if (fp==NULL) { + printf("File error\n"); + exit(0); + } + + fprintf(fp, "unsigned char _RandVals[] = {\n"); + for (i = 0; i < 256; i++) { + fprintf(fp,"0x%02x,\n",rand_val[i]); + } + fprintf(fp,"};\n"); + fclose(fp); + +} + +#endif + diff --git a/RAWFILE.CPP b/RAWFILE.CPP new file mode 100644 index 0000000..61551d1 --- /dev/null +++ b/RAWFILE.CPP @@ -0,0 +1,1089 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\rawfile.cpv 2.18 16 Oct 1995 16:50:54 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : RAWFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : October 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RawFileClass::Close -- Perform a closure of the file. * + * RawFileClass::Create -- Creates an empty file. * + * RawFileClass::Delete -- Deletes the file object from the disk. * + * RawFileClass::Error -- Handles displaying a file error message. * + * RawFileClass::Is_Available -- Checks to see if the specified file is available to open. * + * RawFileClass::Open -- Assigns name and opens file in one operation. * + * RawFileClass::Open -- Opens the file object with the rights specified. * + * RawFileClass::RawFileClass -- Simple constructor for a file object. * + * RawFileClass::Read -- Reads the specified number of bytes into a memory buffer. * + * RawFileClass::Seek -- Reposition the file pointer as indicated. * + * RawFileClass::Set_Name -- Manually sets the name for a file object. * + * RawFileClass::Size -- Determines size of file (in bytes). * + * RawFileClass::Write -- Writes the specified data to the buffer specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wwlib32.h" +#include "compat.h" +#include "rawfile.h" + +extern GraphicBufferClass SeenBuff; +extern GraphicBufferClass HidPage; + +/*********************************************************************************************** + * RawFileClass::Error -- Handles displaying a file error message. * + * * + * Display an error message as indicated. If it is allowed to retry, then pressing a key * + * will return from this function. Otherwise, it will exit the program with "exit()". * + * * + * INPUT: error -- The error number (same as the DOSERR.H error numbers). * + * * + * canretry -- Can this routine exit normally so that retrying can occur? If this is * + * false, then the program WILL exit in this routine. * + * * + * filename -- Optional filename to report with this error. If no filename is * + * supplied, then no filename is listed in the error message. * + * * + * OUTPUT: none, but this routine might not return at all if the "canretry" parameter is * + * false or the player pressed ESC. * + * * + * WARNINGS: This routine may not return at all. It handles being in text mode as well as * + * if in a graphic mode. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void RawFileClass::Error(int error, int canretry, char const * filename) +{ + char message[256]; // Staging buffer for error message string. + + /* + ** Build the complete text of the error message. This text is used in either the graphic + ** mode or the text mode version. + */ +#ifdef GERMAN + strcpy(message, "DATEIFEHLER"); +#else +#ifdef FRENCH + strcpy(message, "ERREUR DE FICHIER"); +#else + strcpy(message, "FILE ERROR"); +#endif +#endif + if (filename) { + strcat(message, "("); + strcat(message, filename); + strcat(message, ")"); + } + strcat(message, ": "); +//BG: Borland only strcat(message, _sys_errlist[error]); + strcat(message, strerror(error) ); + strcat(message, ". "); + + /* + ** If it can't properly handle displaying the error message in the + ** current graphic mode, then this forces the error to become non + ** recoverable. Go into text mode and proceed. + */ + if (!FontPtr && GraphicMode != TXT_MODE) { + Set_Video_Mode(RESET_MODE); + canretry = false; + GraphicMode = TXT_MODE; + } + + /* + ** Add the text explaining the valid actions to take. + */ + if (canretry) { + if (GraphicMode == TXT_MODE) strcat(message, "\n"); +#ifdef GERMAN + strcat(message, " Beliebige Taste drcken fr erneuten Versuch."); + if (GraphicMode == TXT_MODE) strcat(message, "\n"); + strcat(message, " drcken, um das Programm zu verlassen."); +#else +#ifdef FRENCH + strcat(message, " Appuyez sur une touche pour recommencer."); + if (GraphicMode == TXT_MODE) strcat(message, "\n"); + strcat(message, " Appuyez sur Echap pour quitter le programme."); +#else + strcat(message, " Press any key to retry."); + if (GraphicMode == TXT_MODE) strcat(message, "\n"); + strcat(message, " Press to exit program."); +#endif +#endif + if (GraphicMode == TXT_MODE) strcat(message, "\n"); + } else { + if (GraphicMode == TXT_MODE) strcat(message, "\n"); +#ifdef GERMAN + strcat(message, " Beliebige Taste drcken, um das Programm zu verlassen."); +#else +#ifdef FRENCH + strcat(message, " Appuyez sur une touche pour quitter le programme."); +#else + strcat(message, " Press any key to exit program."); +#endif +#endif + if (GraphicMode == TXT_MODE) strcat(message, "\n"); + } + + /* + ** In text mode, the error handler is very simple. It just prints the error message + ** to the screen and waits for a response. + */ + if (GraphicMode == TXT_MODE) { + int input; + + /* + ** Display the error message and wait for a response. + */ + printf(message); + Keyboard::Clear(); + input = Keyboard::Get(); + + /* + ** Check for input. If the ESC key was pressed or if retrying is not allowed, + ** then exit the program. Otherwise, return from this routine for a retry + ** attempt. + */ + if (input == KN_ESC || !canretry) { + Prog_End(); + exit(EXIT_FAILURE); + } + + } else { + + /* + ** The graphic mode version of the error handler will display a simple message + ** box on the screen. If the palette is black at this point, then the error will + ** be invisible. For more thorough and pleasing results, you should replace this + ** virtual function with one of your own, that is more aware of the environment + ** in which is exists. + */ + void *background; // Pointer to background saving buffer. + GraphicBufferClass * oldpage; // Copy of old logic page. + int oldwindow; // Copy of old window number. + void const *oldfont; // Copy of old font pointer. + int oldspacing; // Old font X spacing. + + /* + ** Setup display in preparation for printing the error message. + */ + oldpage = Set_Logic_Page(SeenBuff); + oldwindow = Change_Window(ErrorWindow); + oldfont = Set_Font(FontPtr); + oldspacing = FontXSpacing; FontXSpacing = 0; + Hide_Mouse(); + + /* + ** Try to allocate a storage buffer for the background to the + ** error window. + */ + background = new char [Size_Of_Region(WinW<<3, WinH)]; + + /* + ** If there is memory for the background storage, then save the + ** screen image area to that buffer. + */ + if (background) { + SeenBuff.To_Buffer(WinX<<3, WinY, WinW<<3, WinH, background, Size_Of_Region(WinW<<3, WinH)); + } + + /* + ** Draw a rudimentary box. + */ + New_Window(); + LogicPage->Draw_Rect(WinX<<3, WinY, (WinX+WinW)<<3, WinY+WinH, WinC); + + /* + ** shrinks window down one byte in all directions. + */ + WindowList[Window][WINDOWX] += 1; + WindowList[Window][WINDOWY] += 1<<3; + WindowList[Window][WINDOWWIDTH] -= 1<<1; + WindowList[Window][WINDOWHEIGHT] -= 1<<4; + Change_Window(Window); + WinCx = WinCy = 0; + + Window_Print(message); + Keyboard::Clear(); + + /* + ** Check for input. If the ESC key was pressed or if retrying is not allowed, + ** then exit the program. Otherwise, return from this routine for a retry + ** attempt. + */ + int input = Keyboard::Get(); + if (input == KN_ESC || !canretry) { + Prog_End(); + exit(EXIT_FAILURE); + } + + /* + ** Restore the window back to its original size. + */ + WindowList[Window][WINDOWX] -= 1; + WindowList[Window][WINDOWY] -= 1<<3; + WindowList[Window][WINDOWWIDTH] += 1<<1; + WindowList[Window][WINDOWHEIGHT] += 1<<4; + Change_Window(Window); + WinCx = WinCy = 0; + + /* + ** If the background was saved off, then restore it. + */ + if (background) { + Buffer_To_Page(WinX<<3, WinY, WinW<<3, WinH, background, SeenBuff); + delete [] background; + } + + /* + ** Restore the system global settings to original values. + */ + Show_Mouse(); + Change_Window(oldwindow); + Set_Font(oldfont); + Set_Logic_Page(oldpage); + FontXSpacing = oldspacing; + } +} + + +/*********************************************************************************************** + * RawFileClass::RawFileClass -- Simple constructor for a file object. * + * * + * This constructor is called when a file object is created with a supplied filename, but * + * not opened at the same time. In this case, an assumption is made that the supplied * + * filename is a constant string. A duplicate of the filename string is not created since * + * it would be wasteful in that case. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +RawFileClass::RawFileClass(char const *filename) : + Handle(-1), + Filename(filename), + Allocated(false) +{ +} + + +RawFileClass::~RawFileClass(void) +{ + if (Allocated && Filename) { + free((char *)Filename); + } + Allocated = false; + Filename = 0; +} + + +/*********************************************************************************************** + * RawFileClass::Set_Name -- Manually sets the name for a file object. * + * * + * This routine will set the name for the file object to the name specified. This name is * + * duplicated in free store. This allows the supplied name to be a temporarily constructed * + * text string. Setting the name in this fashion doesn't affect the closed or opened state * + * of the file. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * OUTPUT: Returns with a pointer to the allocated copy of this filename. This pointer is * + * guaranteed to remain valid for the duration of this file object or until the name * + * is changed -- whichever is sooner. * + * * + * WARNINGS: Because of the allocation this routine must perform, memory could become * + * fragmented. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +char const * RawFileClass::Set_Name(char const *filename) +{ + if (Filename && Allocated) { + free((char*)Filename); + Filename = 0; + Allocated = false; + } + + if (!filename) return(NULL); + + Filename = strdup(filename); + if (!Filename) { + Error(ENOMEM, false, filename); + } + Allocated = true; + return(Filename); +} + + +/*********************************************************************************************** + * RawFileClass::Open -- Assigns name and opens file in one operation. * + * * + * This routine will assign the specified filename to the file object and open it at the * + * same time. If the file object was already open, then it will be closed first. If the * + * file object was previously assigned a filename, then it will be replaced with the new * + * name. Typically, this routine is used when an anonymous file object has been crated and * + * now it needs to be assigned a name and opened. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * rights -- The open file access rights to use. * + * * + * OUTPUT: bool; Was the file opened? The return value of this is moot, since the open file * + * is designed to never return unless it succeeded. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Open(char const *filename, int rights) +{ + Set_Name(filename); + return(Open(rights)); +} + + +/*********************************************************************************************** + * RawFileClass::Open -- Opens the file object with the rights specified. * + * * + * This routine is used to open the specified file object with the access rights indicated. * + * This only works if the file has already been assigned a filename. It is guaranteed, by * + * the error handler, that this routine will always return with success. * + * * + * INPUT: rights -- The file access rights to use when opening this file. This is a * + * combination of READ and/or WRITE bit flags. * + * * + * OUTPUT: bool; Was the file opened successfully? This will always return true by reason of * + * the error handler. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Open(int rights) +{ + Close(); + + /* + ** Verify that there is a filename associated with this file object. If not, then this is a + ** big error condition. + */ + if (!Filename) { + Error(ENOENT, false); + } + + /* + ** Record the access rights used for this open call. These rights will be used if the + ** file object is duplicated. + */ + Rights = rights; + + /* + ** Repetatively try to open the file. Abort if a fatal error condition occurs. + */ + for (;;) { + + /* + ** Try to open the file according to the access rights specified. + */ + Hard_Error_Occured = 0; + switch (rights) { + + /* + ** If the access rights are not recognized, then report this as + ** an invalid access code. + */ + default: + errno = EINVAL; + break; + + case READ: + _dos_open(Filename, O_RDONLY|SH_DENYNO, &Handle); + break; + + case WRITE: + _dos_creat(Filename, 0, &Handle); + break; + + case READ|WRITE: + _dos_open(Filename, O_RDWR|O_CREAT|SH_DENYWR, &Handle); + break; + } + + /* + ** If the handle indicates the file is not open, then this is an error condition. + ** For the case of the file cannot be found, then allow a retry. All other cases + ** are fatal. + */ + if (Handle < 0) { + + /* + ** If this flag is set, then some hard error occurred. Just assume that the error + ** is probably a removed CD-ROM and allow a retry. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + } else { + if (errno == ENOENT) { + Error(ENOENT, true, Filename); + } else { + Error(errno, false, Filename); + } + } + continue; + } + break; + } + return(true); +} + + +/*********************************************************************************************** + * RawFileClass::Is_Available -- Checks to see if the specified file is available to open. * + * * + * This routine will examine the disk system to see if the specified file can be opened * + * or not. Use this routine before opening a file in order to make sure that is available * + * or to perform other necessary actions. * + * * + * INPUT: force -- Should this routine keep retrying until the file becomes available? If * + * in this case it doesn't become available, then the program will abort. * + * * + * OUTPUT: bool; Is the file available to be opened? * + * * + * WARNINGS: Depending on the parameter passed in, this routine may never return. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Is_Available(int forced) +{ + int file; // Working file handle. + int open_failed; + + /* + ** If the file is already open, then is must have already passed the availability check. + ** Return true in this case. + */ + if (Is_Open()) { + return(true); + } + + /* + ** If this is a forced check, then go through the normal open channels, since those + ** channels ensure that the file must exist. + */ + if (forced) { + RawFileClass::Open(READ); + RawFileClass::Close(); + return(true); + } + + /* + ** Perform a raw open of the file. If this open fails for ANY REASON, including a missing + ** CD-ROM, this routine will return a failure condition. In all but the missing file + ** condition, go through the normal error recover channels. + */ + for (;;) { + + Hard_Error_Occured = 0; + open_failed = _dos_open(Filename, O_RDONLY|SH_DENYNO, &file); + + /* + ** If DOS reports that everything is just fine except that the file is not present, + ** then return with this fact. Any other case will fall through to the error handler + ** routine. + */ + if (open_failed && errno == ENOENT) { + return(false); + } + + /* + ** If we got an access error it could be because there is no cd in + ** the drive. Call the error handler but allow a continue if it + ** returns. + */ + if (open_failed && errno == EACCES) { +// Error(errno, true, Filename); +// continue; + return(false); + } + + /* + ** If the file could not be found, then return with this information. If a more + ** serious error occurred, then display the error message and abort. + */ + if (Hard_Error_Occured) { + return(false); +// Error(Hard_Error_Occured, true, Filename); +// continue; + } else { + if (open_failed) { + /* + ** An unhandled error condition is fatal. Display the error message and then + ** abort. + */ +// Error(errno, false, Filename); + return(false); + } + } + break; + } + + /* + ** Since the file could be opened, then close it and return that the file exists. + */ + if (_dos_close(file)) { + Error(errno, false, Filename); + } + return(true); +} + + +/*********************************************************************************************** + * RawFileClass::Close -- Perform a closure of the file. * + * * + * Close the file object. In the rare case of an error, handle it as appropriate. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Some rare error conditions may cause this routine to abort the program. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +void RawFileClass::Close(void) +{ + + /* + ** If the file is open, then close it. If the file is already closed, then just return. This + ** isn't considered an error condition. + */ + if (Is_Open()) { + + for (;;) { + /* + ** Close the file. If there was an error in the close operation -- abort. + */ + Hard_Error_Occured = 0; + if (_dos_close(Handle)) { + + /* + ** By definition, this error can only be a bad file handle. This a fatal condition + ** of course, so abort with an error message. + */ + Error(errno, false, Filename); + } + + /* + ** In the condition (if it is even possible) of a hard error occurring, then + ** assume it is the case of missing media. Display an error message and try + ** again if indicated. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; + } + break; + } + + /* + ** At this point the file must have been closed. Mark the file as empty and return. + */ + Handle = -1; + } +} + + +/*********************************************************************************************** + * RawFileClass::Read -- Reads the specified number of bytes into a memory buffer. * + * * + * This routine will read the specified number of bytes and place the data into the buffer * + * indicated. It is legal to call this routine with a request for more bytes than are in * + * the file. This condition can result in fewer bytes being read than requested. Determine * + * this by examining the return value. * + * * + * INPUT: buffer -- Pointer to the buffer to read data into. If NULL is passed, no read * + * is performed. * + * * + * size -- The number of bytes to read. If NULL is passed, then no read is * + * performed. * + * * + * OUTPUT: Returns with the number of bytes read into the buffer. If this number is less * + * than requested, it indicates that the file has been exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Read(void *buffer, long size) +{ + long bytesread = 0; // Running count of the number of bytes read into the buffer. + int opened = false; // Was the file opened by this routine? + int readresult; + + /* + ** If the file isn't opened, open it. This serves as a convenience + ** for the programmer. + */ + if (!Is_Open()) { + + /* + ** The error check here is moot. Open will never return unless it succeeded. + */ + if (!Open(READ)) { + return(0); + } + opened = true; + } + + /* + ** Read the file in convenient chunk sizes. When the actual number + ** of bytes read does not match the desired, then assume that the file + ** is exhausted and bail. This loop was adjusted to take into + ** consideration the fact that "read" returns a SIGNED value whereas + ** it takes an UNSIGNED value as the byte count. + */ + while (size) { + unsigned desired; // Bytes desired to be read this pass. + unsigned actual; // Actual number of bytes read. + + /* + ** Break the read request into chunks no bigger than the low level DOS read + ** can handle. + */ + desired = size; + + Hard_Error_Occured = 0; + readresult = _dos_read(Handle, buffer, desired, &actual); + + /* + ** If a hard error occurred, then assume that it is the case of the CD-ROM or + ** floppy media having been removed. Display the error and retry as directed. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; // Not technically needed, but to be consistent... + } else { + + /* + ** If negative one is returned from the read operation, then this indicates + ** either a bad file number or invalid access. These are fatal conditions, so + ** display the error and then abort. + */ + if (readresult != 0) { + Error(errno, false, Filename); + } else { + + /* + ** No error occurred during the read. Adjust the pointers and size counters and + ** loop again if more data is needed to be read. + */ + buffer = Add_Long_To_Pointer(buffer, actual); + bytesread += actual; + size -= actual; + if (actual != desired) break; // No more data? + } + } + } + + /* + ** Close the file if it was opened by this routine and return + ** the actual number of bytes read into the buffer. + */ + if (opened) Close(); + return(bytesread); +} + + +/*********************************************************************************************** + * RawFileClass::Write -- Writes the specified data to the buffer specified. * + * * + * This routine will write the data specified to the file. * + * * + * INPUT: buffer -- The buffer that holds the data to write. * + * * + * size -- The number of bytes to write to the file. * + * * + * OUTPUT: Returns with the number of bytes written to the file. This routine catches the * + * case of a disk full condition, so this routine will always return with the number * + * matching the size request. * + * * + * WARNINGS: A fatal file condition could cause this routine to never return. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Write(void const *buffer, long size) +{ + long bytesread = 0; + int opened = false; // Was the file manually opened? + int writeresult; + + /* + ** Check to open status of the file. If the file is open, then merely write to + ** it. Otherwise, open the file for writing and then close the file when the + ** output is finished. + */ + if (!Is_Open()) { + if (!Open(WRITE)) { + return(0); + } + opened = true; + } + + /* + ** Write the data to the file in chunks no bigger than what the low level DOS write + ** can handle. + */ + while (size) { + unsigned desired; // Bytes desired to be write this pass. + unsigned actual; // Actual number of bytes written. + + Hard_Error_Occured = 0; +// desired = (unsigned)MIN(size, Transfer_Block_Size()); + desired = size; + writeresult = _dos_write(Handle, buffer, desired, &actual); + + /* + ** If a hard error occurred, then assume it is the case of the media being + ** removed. Print the error message an retry as directed. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; // Not technically needed, but to be consistent... + } else { + + /* + ** If negative one is returned by the DOS read, then this indicates a bad file + ** handle or invalid access. Either condition is fatal -- display error condition + ** and abort. + */ + if (writeresult != 0) { + Error(errno, false, Filename); + } else { + + /* + ** A successful write occurred. Update pointers and byte counter as appropriate. + */ + buffer = Add_Long_To_Pointer((void *)buffer, actual); + bytesread += actual; + size -= actual; + + /* + ** If the actual bytes written is less than requested, assume this is a case of + ** the disk being full. Consider this a fatal error condition. + */ + if (actual != desired) { + Error(ENOSPC, false, Filename); + } + } + } + } + + /* + ** If this routine had to open the file, then close it before returning. + */ + if (opened) { + Close(); + } + + /* + ** Return with the number of bytes written. This will always be the number of bytes + ** requested, since the case of the disk being full is caught by this routine. + */ + return(bytesread); +} + + +/*********************************************************************************************** + * RawFileClass::Seek -- Reposition the file pointer as indicated. * + * * + * Use this routine to move the filepointer to the position indicated. It can move either * + * relative to current position or absolute from the beginning or ending of the file. This * + * routine will only return if it successfully performed the seek. * + * * + * INPUT: pos -- The position to seek to. This is interpreted as relative to the position * + * indicated by the "dir" parameter. * + * * + * dir -- The relative position to relate the seek to. This can be either SEEK_SET * + * for the beginning of the file, SEEK_CUR for the current position, or * + * SEEK_END for the end of the file. * + * * + * OUTPUT: This routine returns the position that the seek ended up at. * + * * + * WARNINGS: If there was a file error, then this routine might never return. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Seek(long pos, int dir) +{ + /* + ** If the file isn't opened, then this is a fatal error condition. + */ + if (!Is_Open()) { + Error(EBADF, false, Filename); + } + + /* + ** Keep trying to seek until a non-retry condition occurs. + */ + for (;;) { + + /* + ** Perform the low level seek on the file. + */ + Hard_Error_Occured = 0; + pos = lseek(Handle, pos, dir); + + /* + ** If a hard error occurred, then assume that it is the case of removed media. Display + ** error message and retry. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; + } else { + + /* + ** A negative one indicates a fatal error with the seek operation. Display error + ** condition and then abort. + */ + if (pos == -1) { + Error(errno, false, Filename); + } + } + break; + } + + /* + ** Return with the new position of the file. This will range between zero and the number of + ** bytes the file contains. + */ + return(pos); +} + + +/*********************************************************************************************** + * RawFileClass::Size -- Determines size of file (in bytes). * + * * + * Use this routine to determine the size of the file. The file must exist or this is an * + * error condition. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes in the file. * + * * + * WARNINGS: This routine handles error conditions and will not return unless the file * + * exists and can successfully be queried for file length. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Size(void) +{ + long size = 0; + + /* + ** If the file is open, then proceed normally. + */ + if (Is_Open()) { + + /* + ** Repetitively try to determine the file size until a fatal error condition or success + ** is achieved. + */ + for (;;) { + Hard_Error_Occured = 0; + size = filelength(Handle); + + /* + ** If a hard error occurred, then assume it is the case of removed media. Display an + ** error condition and allow retry. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; + } else { + if (size == -1) { + Error(errno, false, Filename); + } + } + break; + } + } else { + + /* + ** If the file wasn't open, then open the file and call this routine again. Count on + ** the fact that the open function must succeed. + */ + if (Open()) { + size = Size(); + + /* + ** Since we needed to open the file we must remember to close the file when the + ** size has been determined. + */ + Close(); + } + } + return(size); +} + + +/*********************************************************************************************** + * RawFileClass::Create -- Creates an empty file. * + * * + * This routine will create an empty file from the file object. The file object's filename * + * must already have been assigned before this routine will function. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the file successfully created? This routine will always return true. * + * * + * WARNINGS: A fatal error condition could occur with this routine. Especially if the disk * + * is full or a read-only media was selected. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Create(void) +{ + Close(); + if (Open(WRITE)) { + Close(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RawFileClass::Delete -- Deletes the file object from the disk. * + * * + * This routine will delete the file object from the disk. If the file object doesn't * + * exist, then this routine will return as if it had succeeded (since the effect is the * + * same). * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the file deleted? If the file was already missing, the this value will * + * be false. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Delete(void) +{ + /* + ** If the file was open, then it must be closed first. + */ + Close(); + + /* + ** If there is no filename associated with this object, then this indicates a fatal error + ** condition. Report this and abort. + */ + if (!Filename) { + Error(ENOENT, false); + } + + /* + ** Repetitively try to delete the file if possible. Either return with success, or + ** abort the program with an error. + */ + for (;;) { + + /* + ** If the file is already missing, then return with this fact. No action is necessary. + ** This can occur as this section loops if the file exists on a floppy and the floppy + ** was removed, the file deleted on another machine, and then the floppy was + ** reinserted. Admittedly, this is a rare case, but is handled here. + */ + if (!Is_Available()) { + return(false); + } + + Hard_Error_Occured = 0; + if (remove(Filename) == -1) { + + /* + ** If a hard error occurred, then assume that the media has been removed. Display + ** error message and retry as directed. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; + } + + /* + ** If at this point, DOS says the file doesn't exist, then just exit with this + ** fact. It should have been caught earlier, but in any case, this is a legal + ** condition. + */ + if (errno == ENOENT) break; + + /* + ** The only way it can reach this point is if DOS indicates that access is denied + ** on the file. This occurs when trying to delete a file on a read-only media such + ** as a CD-ROM. Report this as a fatal error and then abort. + */ + Error(errno, false, Filename); + } + break; + } + + /* + ** DOS reports that the file was successfully deleted. Return with this fact. + */ + return(true); +} diff --git a/RAWFILE.H b/RAWFILE.H new file mode 100644 index 0000000..46476e9 --- /dev/null +++ b/RAWFILE.H @@ -0,0 +1,243 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\rawfile.h_v 2.16 16 Oct 1995 16:45:46 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : RAWFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : October 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RawFileClass::File_Name -- Returns with the filename associate with the file object. * + * RawFileClass::RawFileClass -- Default constructor for a file object. * + * RawFileClass::Is_Open -- Checks to see if the file is open or not. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RAWFILE_H +#define RAWFILE_H + +#include +#include +#include +#include + +#ifdef NEVER + /* + ** This is a duplicate of the error numbers. The error handler for the RawFileClass handles + ** these errors. If the error routine is overridden and additional errors are defined, then + ** use numbers starting with 100. Note that these errors here are listed in numerical order. + ** These errors are defined in the standard header file "ERRNO.H". + */ + EZERO, // Non-error. + EINVFNC, // Invalid function number. + ENOFILE, // File not found. + ENOENT=ENOFILE, // No such file or directory. + ENOPATH, // Path not found. + EMFILE, // Too many open files. + EACCES, // Permission denied. + EBADF, // Bad file number. + ECONTR, // Memory blocks destroyed. + ENOMEM, // Not enough core memory. + EINVMEM, // Invalid memory block address. + EINVENV, // Invalid environment. + EINVFMT, // Invalid format. + EINVACC, // Invalid access code. + EINVDAT, // Invalid data. + EFAULT, // Unknown error. + EINVDRV, // Invalid drive specified. + ENODEV=EINVDRV, // No such device. + ECURDIR, // Attempt to remove CurDir. + ENOTSAM, // Not same device. + ENMFILE, // No more files. + EINVAL, // Invalid argument. + E2BIG, // Argument list too long. + ENOEXEC, // exec format error. + EXDEV, // Cross-device link. + ENFILE, // Too many open files. + ECHILD, // No child process. + ENOTTY, // not used + ETXTBSY, // not used + EFBIG, // not used + ENOSPC, // No space left on device. + ESPIPE, // Illegal seek. + EROFS, // Read-only file system. + EMLINK, // not used + EPIPE, // Broken pipe. + EDOM, // Math argument. + ERANGE, // Result too large. + EEXIST, // File already exists. + EDEADLOCK, // Locking violation. + EPERM, // Operation not permitted. + ESRCH, // not used + EINTR, // Interrupted function call. + EIO, // Input/output error. + ENXIO, // No such device or address. + EAGAIN, // Resource temporarily unavailable. + ENOTBLK, // not used + EBUSY, // Resource busy. + ENOTDIR, // not used + EISDIR, // not used + EUCLEAN, // not used +#endif + +/* +** This is the definition of the raw file class. It is derived from the abstract base FileClass +** and handles the interface to the low level DOS routines. This is the first class in the +** chain of derived file classes that actually performs a useful function. With this class, +** I/O is possible. More sophisticated features, such as packed files, CD-ROM support, +** file caching, and XMS/EMS memory support, are handled by derived classes. +** +** Of particular importance is the need to override the error routine if more sophisticated +** error handling is required. This is more than likely if greater functionality is derived +** from this base class. +*/ +class RawFileClass : public FileClass +{ + public: + + /* + ** This is a record of the access rights used to open the file. These rights are + ** used if the file object is duplicated. + */ + int Rights; + + RawFileClass(char const *filename); + RawFileClass(void); + RawFileClass(RawFileClass const & f); + RawFileClass & operator = (RawFileClass const & f); + virtual ~RawFileClass(void); + + virtual char const * File_Name(void) const; + virtual char const * Set_Name(char const *filename); + virtual int Create(void); + virtual int Delete(void); + virtual int Is_Available(int forced=false); + virtual int Is_Open(void) const; + virtual int Open(char const *filename, int rights=READ); + virtual int Open(int rights=READ); + virtual long Read(void *buffer, long size); + virtual long Seek(long pos, int dir=SEEK_CUR); + virtual long Size(void); + virtual long Write(void const *buffer, long size); + virtual void Close(void); + virtual void Error(int error, int canretry = false, char const * filename=NULL); + + protected: + + /* + ** This function returns the largest size a low level DOS read or write may + ** perform. Larger file transfers are performed in chunks of this size or less. + */ + long Transfer_Block_Size(void) {return (long)((unsigned)UINT_MAX)-16L;}; + + private: + + /* + ** This is the low level DOS handle. A -1 indicates an empty condition. + */ + int Handle; + + /* + ** This points to the filename as a NULL terminated string. It may point to either a + ** constant or an allocated string as indicated by the "Allocated" flag. + */ + char const * Filename; + + /* + ** Filenames that were assigned as part of the construction process + ** are not allocated. It is assumed that the filename string is a + ** constant in that case and thus making duplication unnecessary. + ** This value will be non-zero if the filename has be allocated + ** (using strdup()). + */ + unsigned Allocated:1; +}; + + +/*********************************************************************************************** + * RawFileClass::File_Name -- Returns with the filename associate with the file object. * + * * + * Use this routine to determine what filename is associated with this file object. If no * + * filename has yet been assigned, then this routing will return NULL. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the file name associated with this file object or NULL * + * if one doesn't exist. * + * * + * WARNINGS: none * + * * + * HISTORY: * +;* 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline char const * RawFileClass::File_Name(void) const +{ + return(Filename); +} + +/*********************************************************************************************** + * RawFileClass::RawFileClass -- Default constructor for a file object. * + * * + * This constructs a null file object. A null file object has no file handle or filename * + * associated with it. In order to use a file object created in this fashion it must be * + * assigned a name and then opened. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * +;* 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline RawFileClass::RawFileClass(void) : Handle(-1), Filename(0), Allocated(false) +{ +} + +/*********************************************************************************************** + * RawFileClass::Is_Open -- Checks to see if the file is open or not. * + * * + * Use this routine to determine if the file is open. It returns true if it is. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file open? * + * * + * * + * WARNINGS: none * + * * + * HISTORY: * +;* 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline int RawFileClass::Is_Open(void) const +{ + return (Handle >= 0); +} + +#endif diff --git a/REAL.H b/REAL.H new file mode 100644 index 0000000..23411bd --- /dev/null +++ b/REAL.H @@ -0,0 +1,934 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\function.h_v 2.21 16 Oct 1995 16:46:44 JOE_BOSTIC $*/ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FUNCTION.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 27, 1994 * + * * + * Last Update : May 27, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FUNCTION_H +#define FUNCTION_H + +#ifdef NEVER +Map (screen) class heirarchy. + + MapeditClass (most derived class) -- scenario editor + ³ + MouseClass -- handles mouse animation and display control + ³ + ScrollClass -- map scroll handler + ³ + HelpClass -- pop-up help text handler + ³ + TabClass -- file folder tab screen mode control dispatcher + ³ + SidebarClass -- displays and controls construction list sidebar + ³ + PowerClass -- display power production/consumption bargraph + ³ + RadarClass -- displays and controls radar map + ³ + DisplayClass -- general tactical map display handler + ³ + MapClass -- general tactical map data handler + ³ + GScreenClass (pure virtual base class) -- generic screen control + + AbstractClass + ³ + ³ + ³ + ³ + ObjectClass + ³ + ÚÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄ¿ + AnimClass ³ TemplateClass ³ ÃÄ FuseClass ³ TerrainClass + ³ ³ ÃÄ FlyClass ³ + ³ ³ BulletClass ³ + OverlayClass MissionClass SmudgeClass + ³ + RadioClass + ³ + ÃÄ CrewClass + ÃÄ FlasherClass + ÃÄ StageClass + ÃÄ CargoClass + TechnoClass + ³ + ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + FootClass BuildingClass + ³ + ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + DriveClass InfantryClass ÃÄ FlyClass + ³ AircraftClass + TurretClass + ³ + TarComClass + ³ + UnitClass + + + AbstractTypeClass + ³ + ObjectTypeClass + ³ + ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ ³ ³ ³ + TechnoTypeClass ³ ³ ³ + ³ BulletTypeClass ³ ³ + ³ TemplateTypeClass ³ + ÚÄÄÄÄÄÄÄÄÁÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ TerrainTypeClass + ³ ³ ³ ³ +UnitTypeClass ³ BuildingTypeClass ³ + ³ InfantryTypeClass + AircraftTypeClass +#endif + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +enum {false=0,true=1}; +typedef int bool; + + + +#define _WIN32 +#define WIN32 //_LEAN_AND_MEAN +#include + + +/********************************************************************** +** If the following define is enabled, then the memory checking code +** will be disabled. +*/ +#define NOMEMCHECK + +#include "watcom.h" +#define FILE_H +#define WWMEM_H +#include "compat.h" +#include +#include "jshell.h" + +#ifdef +#undef +#endif +#ifdef _pascal +#undef _pascal +#endif +#ifdef pascal +#undef pascal +#endif + +#define pascal +#define _pascal +#define + + + + +// Should be part of WWLIB.H. This is used in JSHELL.CPP. +typedef struct { + unsigned char SourceColor; + unsigned char DestColor; + unsigned char Fading; + unsigned char reserved; +} TLucentType; + + +// Don't complain if these headers aren't referenced. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* +** VQ player specific includes. +*/ +#include +#include + +extern bool GameActive; +extern long LParam; + +#include "vector.h" +#include "heap.h" +#include "ccfile.h" +#include "monoc.h" +#include "conquer.h" +//#include "debug.h" +#include "special.h" +#include "defines.h" + + +/* +** Greenleaf specific includes. +*/ +#include +#include + + +extern long Frame; +inline CELL Coord_XCell(COORDINATE coord) {return (CELL)(*(((unsigned char*)&coord)+1));} +inline CELL Coord_YCell(COORDINATE coord) {return (CELL)(*(((unsigned char*)&coord)+3));} +//inline CELL Coord_Cell(COORD coord){return (CELL)(((*(((unsigned short *)&coord)+1) & 0xFF00) >> 2) | *(((unsigned char *)&coord)+1));} +CELL Coord_Cell(COORDINATE coord); +#pragma aux Coord_Cell parm [eax] \ + modify [ebx] \ + value [ax] = \ + "mov ebx,eax" \ + "shr eax,010h" \ + "xor al,al" \ + "shr eax,2" \ + "or al,bh" + + +#include "facing.h" +#include "ftimer.h" +#include "theme.h" +#include "link.h" +#include "gadget.h" +#include "control.h" +#include "toggle.h" +#include "checkbox.h" +#include "shapebtn.h" +#include "textbtn.h" +#include "slider.h" +#include "list.h" +#include "cheklist.h" +#include "colrlist.h" +#include "edit.h" +#include "gauge.h" +#include "msgbox.h" +#include "dial8.h" +#include "txtlabel.h" +#include "super.h" +#include "house.h" +#include "gscreen.h" +#include "map.h" +#include "display.h" +#include "radar.h" +#include "power.h" +#include "sidebar.h" +#include "tab.h" +#include "help.h" +#include "mouse.h" +//#include "mapedit.h" +#include "help.h" +#include "target.h" +#include "theme.h" +#include "team.h" // Team objects. +#include "teamtype.h" // Team type objects. +#include "trigger.h" // Trigger event objects. +#include "mapedit.h" // ??? +#include "abstract.h" +#include "object.h" +#include "mission.h" +#include "door.h" +#include "bullet.h" // Bullet objects. +#include "terrain.h" // Terrain objects. +#include "anim.h" // Animation objects. +#include "template.h" // Icon template objects. +#include "overlay.h" // Overlay objects. +#include "smudge.h" // Stains on the terrain objects. +#include "aircraft.h" // Aircraft objects. +#include "unit.h" // Ground unit objects. +#include "infantry.h" // Infantry objects. +#include "credits.h" // Credit counter class. +#include "score.h" // Scoring system class. +#include "factory.h" // Production manager class. +#include "intro.h" +#include "ending.h" +#include "logic.h" +#include "queue.h" +#include "event.h" +#include "base.h" // defines the AI's pre-built base +#include "ipxmgr.h" +#include "combuf.h" +#include "connect.h" +#include "connmgr.h" +#include "noseqcon.h" +#include "msglist.h" +#include "nullconn.h" +#include "nullmgr.h" +#include "phone.h" +#include "loaddlg.h" +#include "ipxaddr.h" + +/**************************************************************************** +** This is a "node", used for the lists of available games & players. The +** 'Game' structure is used for games; the 'Player' structure for players. +*/ +typedef struct NodeNameTag { + char Name[MPLAYER_NAME_MAX]; + IPXAddressClass Address; + union { + struct { + int Version; + unsigned char IsOpen; + unsigned long LastTime; + } Game; + struct { + HousesType House; + unsigned char Color; + } Player; + }; +} NodeNameType; + + +#include "externs.h" + + +extern int Get_CD_Drive(void); +extern void Fatal(char const *message, ...); + + +/* +** ANIM.CPP +*/ +void Shorten_Attached_Anims(ObjectClass * obj); + +/* +** AUDIO.CPP +*/ +int Sound_Effect(VocType voc, VolType volume, int variation=1, signed short panvalue=0); +void Speak(VoxType voice); +void Speak_AI(void); +void Stop_Speaking(void); +void Sound_Effect(VocType voc, COORDINATE coord=NULL, int variation=1); +bool Is_Speaking(void); + +/* +** COMBAT.CPP +*/ +int Modify_Damage(int damage, WarheadType warhead, ArmorType armor, int distance); +void Explosion_Damage(COORDINATE coord, unsigned strength, TechnoClass * source, WarheadType warhead); + +/* +** CONQUER.CPP +*/ +void Center_About_Objects(void); +bool Force_CD_Available(int cd); +void Handle_View(int view, int action=0); +void Handle_Team(int team, int action=0); +TechnoTypeClass const * Fetch_Techno_Type(RTTIType type, int id); +char const * Fading_Table_Name(char const * base, TheaterType theater); +void Unselect_All(void); +void Play_Movie(char const * name, ThemeType theme=THEME_NONE, bool clrscrn=true); +bool Main_Loop(); +TheaterType Theater_From_Name(char const *name); +//DirType Rotation_Calc(DirType current, DirType desired, int rate); +void Main_Game(int argc, char *argv[]); +long VQ_Call_Back(unsigned char * buffer=NULL, long frame=0); +void Call_Back(void); +char const *Language_Name(char const *basename); +SourceType Source_From_Name(char const *name); +char const *Name_From_Source(SourceType source); +FacingType KN_To_Facing(int input); +void const *Get_Radar_Icon(void const *shapefile, int shapenum, int frames, int zoomfactor); +void CC_Draw_Shape(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata=0, void const * ghostdata=0); +void Go_Editor(bool flag); +long MixFileHandler(VQAHandle *vqa, long action, void *buffer, long nbytes); + +char *CC_Get_Shape_Filename(void const *shapeptr ); +void CC_Add_Shape_To_Global(void const *shapeptr, char *filename, char code ); + +void Bubba_Print(char *format,...); + +void Heap_Dump_Check( char *string ); +void Dump_Heap_Pointers( void ); +unsigned long Disk_Space_Available(void); + +void Validate_Error(char *name); +void const * Hires_Retrieve(char *name); +int Get_Resolution_Factor(void); + + +/* +** INTERPAL.CPP +*/ +#define SIZE_OF_PALETTE 256 +extern "C" unsigned char *InterpolationPalette; +extern BOOL InterpolationPaletteChanged; +extern void Interpolate_2X_Scale( GraphicBufferClass *source, GraphicBufferClass *dest ,char const *palette_file_name); +void Read_Interpolation_Palette (char const *palette_file_name); +void Write_Interpolation_Palette (char const *palette_file_name); +void Increase_Palette_Luminance(unsigned char *InterpolationPalette , int RedPercentage ,int GreenPercentage ,int BluePercentage ,int cap); +extern "C"{ + extern unsigned char PaletteInterpolationTable[SIZE_OF_PALETTE][SIZE_OF_PALETTE]; + extern unsigned char *InterpolationPalette; + void __cdecl Asm_Create_Palette_Interpolation_Table(void); +} + + +/* +** COORD.CPP +*/ +void Move_Point(short &x, short &y, register DirType dir, unsigned short distance); +COORDINATE Adjacent_Cell(COORDINATE coord, FacingType dir); +COORDINATE Coord_Move(COORDINATE start, DirType facing, unsigned short distance); +COORDINATE Coord_Scatter(COORDINATE coord, unsigned distance, bool lock=false); +DirType Direction(CELL cell1, CELL cell2); +DirType Direction(COORDINATE coord1, COORDINATE coord2); +DirType Direction256(COORDINATE coord1, COORDINATE coord2); +DirType Direction8(COORDINATE coord1, COORDINATE coord2); +int Distance(CELL coord1, CELL coord2); +int Distance(COORDINATE coord1, COORDINATE coord2); +short const * Coord_Spillage_List(COORDINATE coord, int maxsize); +//void Move_Point(unsigned short &x, unsigned short &y, DirType dir, unsigned short distance); + +/* +** COORDA.CPP +*/ +//extern "C" { +//unsigned Cardinal_To_Fixed(unsigned base, unsigned cardinal); +//unsigned Fixed_To_Cardinal(unsigned base, unsigned fixed); +//} + +/* +** DEBUG.CPP +*/ +void Log_Event(char const *text, ...); +void Debug_Key(unsigned input); +void Self_Regulate(void); + +/* +** DIALOG.CPP +*/ +int Format_Window_String(char * string, int maxlinelen, int & width, int & height); +extern void Dialog_Box(int x, int y, int w, int h); +void Conquer_Clip_Text_Print(char const *, unsigned x, unsigned y, unsigned fore, unsigned back=(unsigned)TBLACK, TextPrintType flag=TPF_8POINT|TPF_DROPSHADOW, unsigned width=-1, int const * tabs=0); +void Draw_Box(int x, int y, int w, int h, BoxStyleEnum up, bool filled); +int __cdecl Dialog_Message(char *errormsg, ...); +void Window_Box(WindowNumberType window, BoxStyleEnum style); +void Fancy_Text_Print(char const *text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...); +void Fancy_Text_Print(int text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...); +void Simple_Text_Print(char const *text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag); + +/* +** DISPLAY.CPP +*/ + +/* +** ENDING.CPP +*/ +void GDI_Ending(void); +void Nod_Ending(void); + +/* +** EXPAND.CPP +*/ +bool Expansion_Present(void); +bool Expansion_Dialog(void); + +/* +** FINDPATH.CPP +*/ +//PathType * Find_Path(CELL source, CELL dest, FacingType *final_moves, int maxlen, int (*callback)(CELL, FacingType), int threshhold); +int Optimize_Moves(PathType *path, int (*callback)(CELL, FacingType), int threshhold); + +/* +** GOPTIONS.CPP +*/ +void Draw_Caption(int text, int x, int y, int w); + +/* +** INI.CPP +*/ +void Set_Scenario_Name(char *buf, int scenario, ScenarioPlayerType player, ScenarioDirType dir = SCEN_DIR_NONE, ScenarioVarType var = SCEN_VAR_NONE); +void Write_Scenario_Ini(char *root); +bool Read_Scenario_Ini(char *root, bool fresh=true); +int Scan_Place_Object(ObjectClass *obj, CELL cell); + +/* +** INIT.CPP +*/ +void Uninit_Game(void); +long Obfuscate(char const * string); +void Anim_Init(void); +bool Init_Game(int argc, char *argv[]); +bool Select_Game(bool fade = false); +bool Parse_Command_Line(int argc, char *argv[]); +void Parse_INI_File(void); +int Version_Number(void); +void Save_Recording_Values(void); +void Load_Recording_Values(void); + +/* +** JSHELL.CPP +*/ +void * Small_Icon(void const * iconptr, int iconnum); +void Set_Window(int window, int x, int y, int w, int h); +void * Load_Alloc_Data(FileClass &file); +long Load_Uncompress(FileClass &file, BuffType &uncomp_buff, BuffType &dest_buff, void *reserved_data); +long Translucent_Table_Size(int count); +void *Build_Translucent_Table(void const *palette, TLucentType const *control, int count, void *buffer); +void *Conquer_Build_Translucent_Table(void const *palette, TLucentType const *control, int count, void *buffer); + +/* +** KEYFBUFF.ASM +*/ +#ifdef __cplusplus +extern "C" { +#endif +long __cdecl Buffer_Frame_To_Page(int x, int y, int w, int h, void *Buffer, GraphicViewPortClass &view, int flags, ...); +#ifdef __cplusplus +} +#endif + +/* +** KEYFRAME.CPP +*/ +int Get_Last_Frame_Length(void); +unsigned long Build_Frame(void const *dataptr, unsigned short framenumber, void *buffptr); +unsigned short Get_Build_Frame_Count(void const *dataptr); +unsigned short Get_Build_Frame_X(void const *dataptr); +unsigned short Get_Build_Frame_Y(void const *dataptr); +unsigned short Get_Build_Frame_Width(void const *dataptr); +unsigned short Get_Build_Frame_Height(void const *dataptr); +bool Get_Build_Frame_Palette(void const *dataptr, void *palette); + +/* +** MAP.CPP +*/ +int Terrain_Cost(CELL cell, FacingType facing); +int Coord_Spillage_Number(COORDINATE coord, int maxsize); + +/* +** MENUS.CPP +*/ +void Setup_Menu(int menu, char const *text[], unsigned long field, int index, int skip); +int Check_Menu(int menu, char const *text[], char *selection, long field, int index); +int Do_Menu(char const **strings, bool blue); +extern int UnknownKey; +int Main_Menu(unsigned long timeout); + +/* +** MPLAYER.CPP +*/ +GameType Select_MPlayer_Game (void); +void Read_MultiPlayer_Settings (void); +void Write_MultiPlayer_Settings (void); +void Read_Scenario_Descriptions (void); +void Free_Scenario_Descriptions(void); +void Computer_Message(void); +int Surrender_Dialog(void); + +/* +** NETDLG.CPP +*/ +bool Init_Network (void); +void Shutdown_Network (void); +bool Remote_Connect (void); +void Destroy_Connection(int id, int error); +bool Process_Global_Packet(GlobalPacketType *packet, IPXAddressClass *address); +unsigned long Compute_Name_CRC(char *name); +void Net_Reconnect_Dialog(int reconn, int fresh, int oldest_index, unsigned long timeval); + +/* +** NULLDLG.CPP +*/ +int Init_Null_Modem( SerialSettingsType *settings ); +void Shutdown_Modem( void ); +void Modem_Signoff( void ); +int Test_Null_Modem( void ); +int Reconnect_Modem( void ); +void Destroy_Null_Connection(int id, int error); +GameType Select_Serial_Dialog( void ); +int Com_Scenario_Dialog(void); +int Com_Show_Scenario_Dialog(void); + +void Smart_Printf( char *format, ... ); +void Hex_Dump_Data( char *buffer, int length ); +void itoh( int i, char *s); + +/* +** PROFILE.CPP +*/ +int WWGetPrivateProfileInt(char const *section, char const *entry, int def, char *profile); +bool WWWritePrivateProfileInt(char const *section, char const *entry, int value, char *profile); +bool WWWritePrivateProfileString(char const *section, char const *entry, char const *string, char *profile); +char * WWGetPrivateProfileString(char const *section, char const *entry, char const *def, char *retbuffer, int retlen, char *profile); +unsigned WWGetPrivateProfileHex (char const *section, char const *entry, char *profile); + +/* +** QUEUE.CPP +*/ +bool Queue_Target(TARGET whom, TARGET target); +bool Queue_Destination(TARGET whom, TARGET target); +bool Queue_Mission(TARGET whom, MissionType mission); +bool Queue_Mission(TARGET whom, MissionType mission, TARGET target, TARGET destination); +bool Queue_Options(void); +bool Queue_Exit(void); +void Queue_AI(void); +void Add_CRC(unsigned long *crc, unsigned long val); + +/* +** RAND.CPP +*/ +int Sim_IRandom(int minval, int maxval); +int Sim_Random(void); + +/* +** REINF.CPP +*/ +bool Do_Reinforcements(TeamTypeClass *team); +bool Create_Special_Reinforcement(HouseClass * house, TechnoTypeClass const * type, TechnoTypeClass const * another, TeamMissionType mission = TMISSION_NONE, int argument =0); +int Create_Air_Reinforcement(HouseClass *house, AircraftType air, int number, MissionType mission, TARGET tarcom, TARGET navcom); + +/* +** SAVELOAD.CPP +*/ +bool Load_Misc_Values(FileClass &file); +bool Save_Misc_Values(FileClass &file); +bool Get_Savefile_Info(int id, char *buf, unsigned *scenp, HousesType *housep); +bool Load_Game(int id); +bool Read_Object (void *ptr, int base_size, int class_size, FileClass & file, void * vtable); +bool Save_Game(int id,char *descr); +bool Write_Object (void *ptr, int class_size, FileClass & file); +TARGET TechnoType_To_Target(TechnoTypeClass const * ptr); +TechnoTypeClass const * Target_To_TechnoType(TARGET target); +void * Get_VTable(void *ptr, int base_size); +void Code_All_Pointers(void); +void Decode_All_Pointers(void); +void Dump(void); +void Set_VTable(void *ptr, int base_size, void *vtable); + +/* +** SCENARIO.CPP +*/ +bool End_Game(void); +bool Read_Scenario(char *root); +bool Start_Scenario(char *root, bool briefing=true); +HousesType Select_House(void); +void Clear_Scenario(void); +void Do_Briefing(char const * text); +void Do_Lose(void); +void Do_Win(void); +void Do_Restart(void); +void Fill_In_Data(void); +bool Restate_Mission(char const * name, int button1, int button2); + +/* +** SCORE.CPP +*/ +void Map_Selection(void); +void Bit_It_In_Scale(int x, int y, int w, int h, GraphicBufferClass *src, GraphicBufferClass *dest, GraphicBufferClass *seen , int delay=0, int dagger=0); +void Bit_It_In(int x, int y, int w, int h, GraphicBufferClass *src, GraphicBufferClass *dest, int delay=0, int dagger=0); +void Call_Back_Delay(int time); +int Alloc_Object(ScoreAnimClass *obj); +extern GraphicBufferClass *PseudoSeenBuff; + +void Window_Dialog_Box(HANDLE hinst, LPCTSTR lpszTemplate, HWND hwndOwner, DLGPROC dlgprc); + +/* +** SPECIAL.CPP +*/ +void Special_Dialog(void); + +/* +** SUPPORT.ASM +*/ +#ifdef __cplusplus +extern "C" { +#endif +void __cdecl Remove_From_List(void **list, int *index, void * ptr); +void * __cdecl Conquer_Build_Fading_Table(void const *palette, void *dest, int color, int frac); +void __cdecl Fat_Put_Pixel(int x, int y, int color, int size, GraphicViewPortClass &); +void __cdecl strtrim(char *buffer); +long __cdecl Get_EAX( void ); +#ifdef __cplusplus +} +#endif + +/* +** TARCOM.CPP +*/ + +/* +** TARGET.CPP +*/ +COORDINATE As_Movement_Coord(TARGET target); +AircraftClass * As_Aircraft(TARGET target); +AnimClass * As_Animation(TARGET target); +BuildingClass * As_Building(TARGET target); +BulletClass * As_Bullet(TARGET target); +CELL As_Cell(TARGET target); +COORDINATE As_Coord(TARGET target); +InfantryClass * As_Infantry(TARGET target); +TeamClass * As_Team(TARGET target); +TeamTypeClass * As_TeamType(TARGET target); +TechnoClass * As_Techno(TARGET target); +//TerrainClass * As_Terrain(TARGET target); +TriggerClass * As_Trigger(TARGET target); +UnitClass * As_Unit(TARGET target); +inline bool Target_Legal(TARGET target) {return(target != TARGET_NONE);}; +ObjectClass * As_Object(TARGET target); + +/* +** ULOGIC.CPP +*/ +int Terrain_Cost(CELL cell, FacingType facing); + +/* +** Inline miscellaneous functions. +*/ +#define XYP_COORD(x,y) (((x)*ICON_LEPTON_W)/CELL_PIXEL_W + ((((y)*ICON_LEPTON_H)/CELL_PIXEL_H)<<16)) +inline FacingType Dir_Facing(DirType facing) {return (FacingType)(((unsigned char)(facing+0x10)&0xFF)>>5);} +inline DirType Facing_Dir(FacingType facing) {return (DirType)((int)facing << 5);} +inline int Cell_To_Lepton(int cell) {return cell<<8;} +inline int Lepton_To_Cell(int lepton) {return ((unsigned)(lepton + 0x0080))>>8;} +inline CELL XY_Cell(int x, int y) {return ((CELL)(((y)<<6)|(x)));} +inline COORDINATE XY_Coord(int x, int y) {return ((COORDINATE)MAKE_LONG(y, x));} +inline int Coord_X(COORDINATE coord) {return (short)(LOW_WORD(coord));} +inline int Coord_Y(COORDINATE coord) {return (short)(HIGH_WORD(coord));} +inline int Cell_X(CELL cell) {return (int)(((unsigned)cell) & 0x3F);} +inline int Cell_Y(CELL cell) {return (int)(((unsigned)cell) >> 6);} +inline int Dir_Diff(DirType dir1, DirType dir2) {return (int)(*((signed char*)&dir2) - *((signed char*)&dir1));} +inline CELL Coord_XLepton(COORDINATE coord) {return (CELL)(*((unsigned char*)&coord));} +inline CELL Coord_YLepton(COORDINATE coord) {return (CELL)(*(((unsigned char*)&coord)+2));} +//inline COORD CellXY_Coord(unsigned x, unsigned y) {return (COORD)(MAKE_LONG(y<<8, x<<8));} +inline COORDINATE Coord_Add(COORDINATE coord1, COORDINATE coord2) {return (COORDINATE)MAKE_LONG((*((short*)(&coord1)+1) + *((short*)(&coord2)+1)), (*((short*)(&coord1)) + *((short*)(&coord2))));} +inline COORDINATE Coord_Sub(COORDINATE coord1, COORDINATE coord2) {return (COORDINATE)MAKE_LONG((*((short*)(&coord1)+1) - *((short*)(&coord2)+1)), (*((short*)(&coord1)) - *((short*)(&coord2))));} +inline COORDINATE Coord_Snap(COORDINATE coord) {return (COORDINATE)MAKE_LONG((((*(((unsigned short *)&coord)+1))&0xFF00)|0x80), (((*((unsigned short *)&coord))&0xFF00)|0x80));} +inline COORDINATE Coord_Mid(COORDINATE coord1, COORDINATE coord2) {return (COORDINATE)MAKE_LONG((*((unsigned short *)(&coord1)+1) + *((unsigned short *)(&coord2)+1))>>1, (*((unsigned short *)(&coord1)) + *((unsigned short *)(&coord2)))>>1);} +inline COORDINATE Cell_Coord(CELL cell) {return (COORDINATE) MAKE_LONG( (((cell & 0x0FC0)<<2)|0x80), ((((cell & 0x003F)<<1)+1)<<7) );} +inline COORDINATE XYPixel_Coord(int x, int y) {return ((COORDINATE)MAKE_LONG((int)(((long)y*(long)ICON_LEPTON_H)/(long)ICON_PIXEL_H)/*+LEPTON_OFFSET_Y*/, (int)(((long)x*(long)ICON_LEPTON_W)/(long)ICON_PIXEL_W)/*+LEPTON_OFFSET_X*/));} +//inline int Facing_To_16(int facing) {return Facing16[facing];} +inline int Facing_To_32(DirType facing) {return Facing32[facing];} +inline DirType Direction256(COORDINATE coord1, COORDINATE coord2) {return ((DirType)Desired_Facing256(Coord_X(coord1), Coord_Y(coord1), Coord_X(coord2), Coord_Y(coord2)));} +inline DirType Direction(COORDINATE coord1, COORDINATE coord2) {return ((DirType)Desired_Facing256(Coord_X(coord1), Coord_Y(coord1), Coord_X(coord2), Coord_Y(coord2)));} +inline DirType Direction8(COORDINATE coord1, COORDINATE coord2) {return ((DirType)Desired_Facing8(Coord_X(coord1), Coord_Y(coord1), Coord_X(coord2), Coord_Y(coord2)));} +//inline int Direction16(COORDINATE coord1, COORD coord2) {return (Desired_Facing16(Coord_X(coord1), Coord_Y(coord1), Coord_X(coord2), Coord_Y(coord2)));} +inline DirType Direction(CELL cell1, CELL cell2) {return (DirType)(Desired_Facing8(Cell_X(cell1), Cell_Y(cell1), Cell_X(cell2), Cell_Y(cell2)));} +inline COORDINATE Adjacent_Cell(COORDINATE coord, FacingType dir) {return (Coord_Snap(Coord_Add(AdjacentCoord[dir & 0x07], coord)));} +inline COORDINATE Adjacent_Cell(COORDINATE coord, DirType dir) {return Adjacent_Cell(coord, Dir_Facing(dir));} +inline CELL Adjacent_Cell(CELL cell, FacingType dir) {return (CELL)(cell + AdjacentCell[dir]);} +inline CELL Adjacent_Cell(CELL cell, DirType dir) {return (CELL)(cell + AdjacentCell[Dir_Facing(dir)]);} +inline int Lepton_To_Pixel(int lepton) {return ((lepton * ICON_PIXEL_W) + (ICON_LEPTON_W / 2)) / ICON_LEPTON_W;} +inline int Pixel_To_Lepton(int pixel) {return ((pixel * ICON_LEPTON_W) + (ICON_PIXEL_W / 2)) / ICON_PIXEL_W;} +//inline FacingType Facing_To_8(DirType facing) {return (FacingType)(((unsigned char)(facing|0x10))>>5);} +inline COORDINATE XYP_Coord(int x,int y) {return XY_Coord(Pixel_To_Lepton(x), Pixel_To_Lepton(y));}; +inline char const * Text_String(int string) {return(Extract_String(SystemStrings, string));}; + + +template inline T Random_Pick(T a, T b) +{ + return (T)IRandom((int)a, (int)b); +}; + +template inline T Sim_Random_Pick(T a, T b) +{ + return (T)Sim_IRandom((int)a, (int)b); +}; + + +#ifdef CHEAT_KEYS +#define Check_Ptr(ptr,file,line) \ +{ \ + if (!ptr) { \ + Mono_Clear_Screen(); \ + Mono_Printf("NULL Pointer, Module:%s, line:%d!\n",file,line); \ + Prog_End(); \ + exit(EXIT_SUCCESS); \ + } \ +} +#else +#define Check_Ptr(ptr,file,line) +#endif + +/* +** These routines are for coding & decoding multiplayer ID's +*/ +inline PlayerColorType MPlayerID_To_ColorIndex(unsigned short id) {return (PlayerColorType)(id >> 4);} +inline HousesType MPlayerID_To_HousesType(unsigned short id) {return ((HousesType)(id & 0x000f)); } +inline unsigned short Build_MPlayerID(int c_idx, HousesType htype) { return ((c_idx << 4) | htype); } + + + + +// +// True if we are the currently in focus windows app +// +extern bool GameInFocus; + +extern int ScreenWidth; +extern int ScreenHeight; +extern "C" void ModeX_Blit (GraphicBufferClass *source); +extern void Colour_Debug (int call_number); + + +extern unsigned char *InterpolatedPalettes[50]; +extern BOOL PalettesRead; +extern unsigned PaletteCounter; + +extern "C"{ + extern unsigned char PaletteInterpolationTable[SIZE_OF_PALETTE][SIZE_OF_PALETTE]; + extern unsigned char *InterpolationPalette; +} + +extern void Free_Interpolated_Palettes(void); +extern int Load_Interpolated_Palettes(char const *filename, BOOL add=FALSE); + + +#define CELL_BLIT_ONLY 1 +#define CELL_DRAW_ONLY 2 + +/*********************************************************************************************** + * Distance -- Determines the lepton distance between two coordinates. * + * * + * This routine is used to determine the distance between two coordinates. It uses the * + * Dragon Strike method of distance determination and thus it is very fast. * + * * + * INPUT: coord1 -- First coordinate. * + * * + * coord2 -- Second coordinate. * + * * + * OUTPUT: Returns the lepton distance between the two coordinates. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +int Distance_Coord(COORDINATE coord1, COORDINATE coord2); +#pragma aux Distance_Coord parm [eax] [ebx] \ + modify [edx ebx] \ + value [eax] = \ + "mov dx,ax" \ + "sub dx,bx" \ + "jg okx" \ + "neg dx" \ + "okx:" \ + "shr eax,16" \ + "shr ebx,16" \ + "sub ax,bx" \ + "jg oky" \ + "neg ax" \ + "oky:" \ + "cmp ax,dx" \ + "jg ok" \ + "xchg ax,dx" \ + "ok:" \ + "shr dx,1" \ + "add ax,dx" + +inline int Distance(COORDINATE coord1, COORDINATE coord2) +{ +#ifdef NEVER + int diff1, diff2; + + diff1 = Coord_Y(coord1) - Coord_Y(coord2); + if (diff1 < 0) diff1 = -diff1; + diff2 = Coord_X(coord1) - Coord_X(coord2); + if (diff2 < 0) diff2 = -diff2; + if (diff1 > diff2) { + return(diff1 + (diff2>>1)); + } + return(diff2 + (diff1>>1)); +#else + return(Distance_Coord(coord1, coord2)); +#endif +} + + + +/*********************************************************************************************** + * Distance -- Determines the cell distance between two cells. * + * * + * Use this routine to determine the distance between the two cells specified. The distance * + * is returned in cells. * + * * + * INPUT: cell1, cell2 -- The two cells to determine the distance between. * + * * + * OUTPUT: Returns with the distance between the two cells in units of cell size. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +inline int Distance(CELL coord1, CELL coord2) +{ + int diff1, diff2; + + diff1 = Cell_Y(coord1) - Cell_Y(coord2); + if (diff1 < 0) diff1 = -diff1; + diff2 = Cell_X(coord1) - Cell_X(coord2); + if (diff2 < 0) diff2 = -diff2; + if (diff1 > diff2) { + return(diff1 + (diff2>>1)); + } + return(diff2 + (diff1>>1)); +} + + +/*********************************************************************************************** + * CellClass::Cell_Number -- Returns the cell ID number for this cell object. * + * * + * Call this routine if you wish to determine what the cell number ID is for the currrent * + * cell object. This ID number is the index number into the cell array. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the cell number for this cell object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +inline CELL CellClass::Cell_Number(void) const +{ + return(Map.ID(this)); +} + +#ifndef NOMEMCHECK +#define NO_INTERCEPT +#include "memcheck.h" +#endif + + +void WWDOS_Shutdown(void); + +#endif + diff --git a/REGION.H b/REGION.H new file mode 100644 index 0000000..dee57d8 --- /dev/null +++ b/REGION.H @@ -0,0 +1,59 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\region.h_v 2.13 16 Oct 1995 16:45:10 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : REGION.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/09/95 * + * * + * Last Update : March 9, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef REGION_H +#define REGION_H + + +class RegionClass { + public: + RegionClass(void) {Threat = 0;}; + ~RegionClass(void) {}; + int operator != (RegionClass const & region) {return memcmp(this, ®ion, sizeof(RegionClass));}; + int operator == (RegionClass const & region) {return !memcmp(this, ®ion, sizeof(RegionClass));}; + int operator > (RegionClass const & region) {return memcmp(this, ®ion, sizeof(RegionClass)) > 0;}; + int operator < (RegionClass const & region) {return memcmp(this, ®ion, sizeof(RegionClass)) < 0;}; + + void Reset_Threat(void) {Threat = 0;}; + void Adjust_Threat(int threat, int neg) {if (neg) Threat -= threat; else Threat+= threat;}; + int Threat_Value(void) const {return Threat;}; + + protected: + long Threat; +}; + +#endif diff --git a/REG_ICON.ICO b/REG_ICON.ICO new file mode 100644 index 0000000..32ce0b9 Binary files /dev/null and b/REG_ICON.ICO differ diff --git a/REG_ICON.RC b/REG_ICON.RC new file mode 100644 index 0000000..e8543d8 --- /dev/null +++ b/REG_ICON.RC @@ -0,0 +1,16 @@ +/**************************************************************************** + + +REG_ICON.RC + +produced by Borland Resource Workshop + + +*****************************************************************************/ + +#include "reg_icon.rh" + + + +ICON_1 ICON "conquer.ico" + diff --git a/REG_ICON.RH b/REG_ICON.RH new file mode 100644 index 0000000..206d6bc --- /dev/null +++ b/REG_ICON.RH @@ -0,0 +1 @@ +#define ICON_1 1 diff --git a/REINF.CPP b/REINF.CPP new file mode 100644 index 0000000..ea227e6 --- /dev/null +++ b/REINF.CPP @@ -0,0 +1,663 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\reinf.cpv 2.17 16 Oct 1995 16:48:58 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : REINF.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 24, 1994 * + * * + * Last Update : July 4, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Create_Air_Reinforcement -- Creates air strike reinforcement * + * Create_Special_Reinforcement -- Ad hoc reinforcement handler. * + * Do_Reinforcements -- Create and place a reinforcement team. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * Do_Reinforcements -- Create and place a reinforcement team. * + * * + * This routine is called when a reinforcement team must be created and placed on the map. * + * It will create all members of the team and place them at the location determined from * + * the team composition. The reinforcement team should follow team orders until overriden * + * by AI or player intervention. * + * * + * INPUT: teamtype -- Pointer to the team type to create as a reinforcement. * + * * + * OUTPUT: Was the reinforcement successfully created and placed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 05/18/1995 JLB : Returns success or failure condition. * + * 06/19/1995 JLB : Announces reinforcements. * + *=============================================================================================*/ +bool Do_Reinforcements(TeamTypeClass *teamtype) +{ + /* + ** preform some preliminary checks for validity. + */ + if (!teamtype || !teamtype->ClassCount) return(false); + + /* + ** Create the controlling team. All the objects are grouped + ** under this team control. If there are no missions for this team + ** then don't actually create the team -- it won't serve a purpose. + */ + TeamClass * team = NULL; + if (teamtype->MissionCount) { + team = new TeamClass(teamtype, HouseClass::As_Pointer(teamtype->House)); + if (!team) return(false); + team->Force_Active(); + } + + /* + ** Determine if this team contains its own transport. In such a case, the + ** transport is used as a loaner. This is only true, if there is other + ** objects to be transport. Without such cargo objects, then the transport + ** is presumed to be the reinforcement itself and thus should not be a + ** loaner. + */ + bool okvoice = true; // Presume ok to announce reinforcement? + bool airtransport = false; // Transport can fly in? + bool watertransport = false; // Transport needs a beach to land at? + bool onlytransport = true; // Just transport is in reinforcement? + bool hastransport = false; // Group comes with transport? + for (int index=0; index < teamtype->ClassCount; index++) { + if (teamtype->Class[index]->IsTransporter || teamtype->Class[index]->What_Am_I() == RTTI_AIRCRAFTTYPE) { + hastransport = true; + if (teamtype->Class[index]->What_Am_I() == RTTI_AIRCRAFTTYPE) { + airtransport = true; + } else { + watertransport = (((UnitTypeClass const *)teamtype->Class[index])->Type == UNIT_HOVER); + } + } else { + onlytransport = false; + } + } + + /* + ** Now determine how the reinforcement should be delivered. This is largely determined + ** by whether there is a transport with the reinforcements. + */ + SourceType source = SOURCE_NONE; + if (airtransport) { + source = SOURCE_AIR; + } else { + + if (watertransport) { + source = SOURCE_BEACH; + } else { + + /* + ** Special case for the gunboat. It always arrives according to the shipping source. + */ + if (teamtype->Class[0]->What_Am_I() == RTTI_UNITTYPE && ((UnitTypeClass const *)teamtype->Class[0])->Type == UNIT_GUNBOAT) { + source = SOURCE_SHIPPING; + } else { + source = HouseClass::As_Pointer(teamtype->House)->Edge; + } + } + } + + /* + ** If we can't determine where the reinforcement should come from, then delete it + ** and return a failure condition. + */ + if (source == SOURCE_NONE) { + if (team) delete team; + return(false); + } + + /* + ** Now that the official source for the reinforcement has been determined, the + ** objects themselves must be created. + */ + TechnoClass * transport = NULL; + TechnoClass * object = NULL; + for (index = 0; index < teamtype->ClassCount; index++) { + TechnoTypeClass const * tclass = teamtype->Class[index]; + + for (int sub = 0; sub < teamtype->DesiredNum[index]; sub++) { + ScenarioInit++; + FootClass * temp = (FootClass *)tclass->Create_One_Of(HouseClass::As_Pointer(teamtype->House)); + ScenarioInit--; + + if (temp) { + + /* + ** Add the object to the team. This is true even for the transports. The one + ** exception is for the hover lander which never becomes part of the team. + */ + if (team && (temp->What_Am_I() != RTTI_UNIT || *((UnitClass*)temp) != UNIT_HOVER)) { + ScenarioInit++; + team->Add(temp); + ScenarioInit--; + } + + /* + ** Build the list of transporters and passengers. + */ + if (tclass->IsTransporter && (!airtransport || tclass->What_Am_I() == RTTI_AIRCRAFTTYPE)) { + + /* + ** Transports are considered loaners if they are transporting + ** something. They are presumed to only serve as a delivery + ** agent. + */ + if (!onlytransport && temp->What_Am_I() != RTTI_UNIT) { + temp->IsALoaner = true; + } + + /* + ** Link to the list of transports. + */ + temp->Next = transport; + transport = temp; + } else { + + /* + ** A-10s are always considered loaners since the player should + ** never be allowed to control them. + */ + if (temp->What_Am_I() == RTTI_AIRCRAFT && *((AircraftClass *)temp) == AIRCRAFT_A10) { + temp->IsALoaner = true; + } + + /* + ** Link to the list of normal objects. + */ + temp->Next = object; + object = temp; + } + } + } + } + + /* + ** Bail on this reinforcement if no reinforcements could be created. + ** This is probably because the object maximum was reached. + */ + if (!object && !transport) { + if (team) delete team; + return(false); + } + + /* + ** Now it is time to place the objects on the map. If there is a transport, then the + ** transported objects must be placed inside the transport at this time as well. + */ + if (transport) { + if (object) { + + /* + ** For cargo planes that carry reinforcements, don't announce arrival + ** when the transport is created. The announcement will occur when the + ** transport unloads. + */ + if (transport->What_Am_I() == RTTI_AIRCRAFT && *((AircraftClass *)transport) == AIRCRAFT_CARGO) { + okvoice = false; + } + + transport->Attach((FootClass *)object); + } + object = transport; + } + + /* + ** Pick the location where the reinforcements appear and then place + ** them there. + */ + bool placed = false; + CELL cell; + FacingType eface; + switch (source) { + + case SOURCE_SHIPPING: + cell = Map.Calculated_Cell(source, teamtype->House); + object->IsALoaner = true; + if (object->Unlimbo(Cell_Coord(cell), DIR_W)) { + object->Assign_Mission(MISSION_GUARD); + object->Assign_Destination(::As_Target(XY_Cell(Map.MapCellX-1, Cell_Y(Coord_Cell(object->Coord)) ))); + } else { + if (team) delete team; + delete object; + return(false); + } + break; + + case SOURCE_NORTH: + case SOURCE_SOUTH: + case SOURCE_EAST: + case SOURCE_WEST: { + eface = (FacingType)(source << 1); // Facing to enter map. + + if (airtransport) ScenarioInit++; + cell = Map.Calculated_Cell(source, teamtype->House); + if (airtransport) ScenarioInit--; + CELL newcell = cell; + + FootClass * o = (FootClass *)object->Next; + object->Next = 0; + bool ok = true; + while (newcell > 0 && object) { + DirType desiredfacing = Facing_Dir(eface); + if (object->What_Am_I() == RTTI_AIRCRAFT) { + desiredfacing = Random_Pick(DIR_N, DIR_MAX); + } + + if (ok && object->Unlimbo(Cell_Coord(newcell), desiredfacing)) { + + /* + ** If this object is part of a team, then the mission for this + ** object will be guard. The team handler will assign the proper + ** mission that it should follow. + */ + if (object->What_Am_I() == RTTI_AIRCRAFT) { + ok = false; + } else { + if (team) { + object->Assign_Mission(MISSION_GUARD); + } else { + object->Assign_Mission(MISSION_MOVE); + object->Assign_Destination(Adjacent_Cell(newcell, eface)); + } + object->Commence(); + } + + } else { + ok = true; + + /* + ** Could not unlimbo at location specified so find an adjacent location that it can + ** be unlimboed at. If this fails, then abort the whole placement process. + */ + for (FacingType adj = FACING_N; adj < FACING_COUNT; adj++) { + CELL trycell = Adjacent_Cell(newcell, adj); + if (!Map.In_Radar(trycell) && object->Can_Enter_Cell(trycell, adj) == MOVE_OK) { + newcell = trycell; + break; + } + } + if (adj < FACING_COUNT) continue; + newcell = -1; + } + + object = o; + if (object) { + o = (FootClass *)object->Next; + object->Next = 0; + } + } + + /* + ** If there are still objects that could not be placed, then delete them. + */ + if (o) { + while (o) { + FootClass * old = o; + o = (FootClass *)o->Next; + old->Next = 0; + + delete old; + } + + } + } + break; + + /* + ** Bring out the aircraft as separate "groups" of one. + */ + case SOURCE_AIR: { + AircraftClass * thisone = (AircraftClass *)object; + while (thisone) { + AircraftClass * next = (AircraftClass *)thisone->Next; + + /* + ** Find a suitable map entry location. Cargo planes will try to find a cell that + ** exactly lines up with the airfield they will unload at. + */ + CELL newcell; + ScenarioInit++; + newcell = Map.Calculated_Cell(HouseClass::As_Pointer(teamtype->House)->Edge, teamtype->House); + ScenarioInit--; + if (*thisone == AIRCRAFT_CARGO) { + BuildingClass const * building = thisone->Find_Docking_Bay(STRUCT_AIRSTRIP, false); + if (building) { + newcell = XY_Cell(Map.MapCellX+Map.MapCellWidth, Coord_YCell(building->Docking_Coord()+2)); + } + } + thisone->Next = 0; + + ScenarioInit++; + placed = thisone->Unlimbo(Cell_Coord(newcell), DIR_W); + ScenarioInit--; + if (placed) { + if (!team) { + if (thisone->Class->IsFixedWing) { + thisone->Assign_Mission(MISSION_HUNT); + } else { + if (thisone->Class->IsTransporter && thisone->Is_Something_Attached()) { + thisone->Assign_Mission(MISSION_UNLOAD); + } else { + thisone->Assign_Mission(MISSION_MOVE); + } + thisone->Assign_Destination(::As_Target(Map.Calculated_Cell(source, teamtype->House))); + } + thisone->Commence(); + } + } else { + delete thisone; + } + + thisone = next; + } + if (!placed && team) delete team; + + + /* + ** Fixes bug that can happen if the reinforcement cannot be created. + ** This prevent "phantom" teams and team types from being left around. + */ + if (GameToPlay == GAME_NORMAL && !placed) return(false); + + } + break; + + case SOURCE_OCEAN: + case SOURCE_BEACH: + cell = Map.Calculated_Cell(SOURCE_BEACH, teamtype->House); + if (cell) { + CELL edge = XY_Cell(Cell_X(cell), Map.MapCellY+Map.MapCellHeight); + + placed = object->Unlimbo(Cell_Coord(edge), DIR_N); + if (placed) { + if (!team) { + object->IsLocked = false; + object->Assign_Mission(MISSION_UNLOAD); + object->Commence(); + object->Assign_Destination(::As_Target(cell)); + } + } else { + if (team) delete team; + delete object; + return(false); + } + } + break; + + default: + break; + } + + /* + ** Announce when the reinforcements have arrived. + */ + if (okvoice && teamtype->House == PlayerPtr->Class->House) { + Speak(VOX_REINFORCEMENTS); + } + + return(true); +} + + +/*********************************************************************************************** + * Create_Special_Reinforcement -- Ad hoc reinforcement handler. * + * * + * Use this routine to bring on a reinforcement that hasn't been anticipated by the trigger * + * system. An example of this would be replacement harvesters or airfield ordered units. * + * The appropriate transport is created (if necessary) and a mission is assigned such that * + * the object will legally bring itself onto the playing field. * + * * + * INPUT: house -- The owner of this reinforcement. * + * * + * type -- The object to bring on. * + * * + * another -- This is reserved for the transport class in those cases where the * + * transport MUST be forced to a specific type. * + * * + * mission -- The mission to assign this reinforcement team. * + * * + * argument -- Optional team mission argument (usually a waypoint). * + * * + * OUTPUT: Was the special reinforcement created without error? * + * * + * WARNINGS: This routine will fail if a team type cannot be created. * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +bool Create_Special_Reinforcement(HouseClass * house, TechnoTypeClass const * type, TechnoTypeClass const * another, TeamMissionType mission, int argument) +{ + if (house && type) { + TeamTypeClass * team = new TeamTypeClass(); + + if (team) { + + /* + ** If a ground based reinforcement is desired, but the edge of the map that the + ** reinforcement will arrive at is completely covered with water, then add + ** a hover lander for transport. + */ + if (!another && (type->What_Am_I() == RTTI_UNITTYPE || type->What_Am_I() == RTTI_INFANTRYTYPE)) { + + /* + ** Hover lander reinforcements can only arrive from the south. Yes, this is an + ** arbitrary limitation, but that's the way it is (for now). + */ + if (house->Edge == SOURCE_SOUTH) { + bool found = false; + for (int index = Map.MapCellX; index < Map.MapCellX+Map.MapCellWidth-1; index++) { + CELL cell = XY_Cell(index, Map.MapCellY+Map.MapCellHeight); + if (Map[cell].Is_Generally_Clear() && Map[cell-MAP_CELL_W].Is_Generally_Clear()) { + found = true; + break; + } + } + + /* + ** No land route was found for the reinforcement to drive itself onto the + ** map. Assign it a hover lander. This presumes that if the south edge is + ** non drivable, it will have water there instead. Risky, but is not a + ** problem with the current C&C maps. + */ + if (!found) { + mission = TMISSION_NONE; + another = &UnitTypeClass::As_Reference(UNIT_HOVER); + } + } + + if (!another) { + mission = TMISSION_MOVECELL; + argument = Map.Calculated_Cell(SOURCE_AIR, house->Class->House); + } + } + + /* + ** If there is no overridden mission assign to this special reinforcement, then + ** we must assign something. If not, the reinforcement will just sit at the edge + ** of the map. + */ + if (!another && mission == TMISSION_NONE) { + mission = TMISSION_MOVECELL; + argument = Map.Calculated_Cell(SOURCE_AIR, house->Class->House); + } + + /* + ** Fill in the team characteristics. + */ + strcpy((char *)&team->IniName[0], "TEMP"); + team->IsReinforcable = false; + team->IsTransient = true; + team->ClassCount = 1; + team->Class[0] = type; + team->DesiredNum[0] = 1; + team->MissionCount = 1; + if (mission == TMISSION_NONE) { + if (another && (another->What_Am_I() != RTTI_UNITTYPE || ((UnitTypeClass const *)another)->Type != UNIT_HOVER)) { + team->MissionList[0].Mission = TMISSION_UNLOAD; + team->MissionList[0].Argument = WAYPT_REINF; + } + } else { + team->MissionList[0].Mission = mission; + team->MissionList[0].Argument = argument; + } + team->House = house->Class->House; + if (another) { + team->ClassCount++; + team->Class[1] = another; + team->DesiredNum[1] = 1; + } + + bool ok = Do_Reinforcements(team); + if (!ok && GameToPlay == GAME_NORMAL) { + delete team; + } + return(ok); + } + } + return(false); +} + + +/*********************************************************************************************** + * Create_Air_Reinforcement -- Creates air strike reinforcement * + * * + * This routine is used to launch an airstrike. It will create the necessary aircraft and * + * assign them to attack the target specified. This routine bypasses the normal * + * reinforcement logic since it doesn't need the sophistication of unloading and following * + * team mission lists. * + * * + * INPUT: house -- The purpetrator of this air strike. * + * * + * air -- The type of aircraft to make up this airstrike. * + * * + * number -- The number of aircraft in this airstrike. * + * * + * mission -- The mission to assign the aircraft. * + * * + * tarcom -- The target to assign these aircraft. * + * * + * navcom -- The navigation target to assign (if necessary). * + * * + * OUTPUT: Returns the number of aircraft created for this airstrike. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Commented. * + *=============================================================================================*/ +int Create_Air_Reinforcement(HouseClass *house, AircraftType air, int number, MissionType mission, TARGET tarcom, TARGET navcom) +{ + /* + ** Get a pointer to the class of the object that we are going to create. + */ + TechnoTypeClass const * type = (TechnoTypeClass *)&AircraftTypeClass::As_Reference(air); + + /* + ** Loop through the number of objects we are supposed to create and + ** create and place them on the map. + */ + for (int sub = 0; sub < number; sub++) { + + /* + ** Create one of the required objects. If this fails we could have + ** a real problem. + */ + ScenarioInit++; + TechnoClass * obj = (TechnoClass *)type->Create_One_Of(house); + ScenarioInit--; + if (!obj) return(sub); + + /* + ** Flying objects always have the IsALoaner bit set. + */ + obj->IsALoaner = true; + + /* + ** Find a cell for the object to come in on. This is stolen from the + ** the code that handles a SOURCE_AIR in the normal logic. + */ + SourceType source = house->Edge; + switch (source) { + case SOURCE_NORTH: + case SOURCE_EAST: + case SOURCE_SOUTH: + case SOURCE_WEST: + break; + + default: + source = SOURCE_NORTH; + break; + } + CELL newcell = Map.Calculated_Cell(source, house->Class->House); + + /* + ** Try and place the object onto the map. + */ + ScenarioInit++; + int placed = obj->Unlimbo(Cell_Coord(newcell), DIR_N); + ScenarioInit--; + if (placed) { + + /* + ** If we suceeded in placing the obj onto the map then + ** now we need to give it a mission and destination. + */ + obj->Assign_Mission(mission); + + /* + ** If a navcom was specified then set it. + */ + if (navcom != TARGET_NONE) { + obj->Assign_Destination(navcom); + } + + /* + ** If a tarcom was specified then set it. + */ + if (tarcom != TARGET_NONE) { + obj->Assign_Target(tarcom); + } + + /* + ** Start the object into action. + */ + obj->Commence(); + } else { + delete obj; + sub--; + return(sub); + } + } + return(sub); +} diff --git a/RULES.MAK b/RULES.MAK new file mode 100644 index 0000000..b9d753a --- /dev/null +++ b/RULES.MAK @@ -0,0 +1,423 @@ +# +# Command & Conquer(tm) +# Copyright 2025 Electronic Arts Inc. +# +# 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 3 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# Size of the program shape buffer (passed to MAKESHPS.EXE) +SHAPEBUFFSIZE = 20000 + +# Assign the paths +# +!if$d(DAVID) +ROOTDIR = f:\projects\c&c\\ +MAPDIR = f:\projects\c&c\\ +ROOTCODEDIR = f:\projects\c&c\code\david +ROOTCDDIR = f:\projects\c&c\cddavid\\ +ROOTCDSDIR = f:\projects\c&c\cddavid\\ +ABSROOT = ..\\ +WAVDIR = f:\projects\c&c\audio\ingame\\ +#WAVDIR = r:\\ +LANGDIR = +INGAMEDIR =ingame +VQADIR = f:\projects\c&c\art\\ +!else +!if$d(BARRY) +ROOTDIR = f:\projects\c&c\\ +MAPDIR = c:\projects\c&c\\ +ROOTCODEDIR = f:\projects\c&c\code\barry +ROOTCDDIR = s:\\ +ROOTCDSDIR = s:\\ +ABSROOT = ..\\ +#WAVDIR = f:\projects\c&c\audio\ingame\\ +WAVDIR = r:\\ +LANGDIR=\german +INGAMEDIR=german +VQADIR = f:\projects\c&c\art\\ +!else +ROOTDIR = ..\\ +MAPDIR = ..\\ +ROOTCODEDIR = .\\ +#ROOTCDDIR = f:\projects\c&c95\cd\\ +#ROOTCDSDIR = f:\projects\c&c95\cd\\ +#ROOTCDDIR = f:\projects\c&c95\cd\japanese\\ +#ROOTCDSDIR = f:\projects\c&c95\cd\japanese\\ +#ROOTCDSDIR = v:\projects\c&c95\cdjapan\\ +ROOTCDDIR = ..\cd\\ +ROOTCDSDIR = ..\\ +!if$d(SLAVE) +ABSROOT = $(SLAVE) +WAVDIR = $(SLAVE) +VQADIR = $(SLAVE) +!else +ABSROOT = ..\\ +WAVDIR = ..\audio\ingame\\ #..\\ +VQADIR = f:\projects\c&c\art\\ +VQADIR = f:\projects\c&c95\art\\ +#VQADIR = d:\\ +!endif +LANGDIR = \\japan\\ +#LANGDIR = +INGAMEDIR =ingame +!endif +!endif + +.path.a6 = $(ABSROOT)code\cps\\ # Game formatted audio data +.path.a60 = $(ABSROOT)code\cps\\ # Game formatted audio data +.path.a61 = $(ABSROOT)code\cps\\ # Game formatted audio data +.path.a62 = $(ABSROOT)code\cps\\ # Game formatted audio data +.path.a63 = $(ABSROOT)code\cps\\ # Game formatted audio data +.path.a64 = $(ABSROOT)code\cps\\ # Game formatted audio data +.path.a6a = $(ABSROOT)code\cps\\ # Game formatted audio data +.path.a6j = $(ABSROOT)code\cps\\ # Game formatted audio data +.path.a8 = $(ABSROOT)code\cps\\ # Game formatted audio data +.path.a80 = $(ABSROOT)code\cps\\ # Game formatted audio data +.path.a81 = $(ABSROOT)code\cps\\ # Game formatted audio data +.path.a82 = $(ABSROOT)code\cps\\ # Game formatted audio data +.path.a83 = $(ABSROOT)code\cps\\ # Game formatted audio data +.path.a84 = $(ABSROOT)code\cps\\ # Game formatted audio data +.path.a8a = $(ABSROOT)code\cps\\ # Game formatted audio data +.path.a8j = $(ABSROOT)code\cps\\ # Game formatted audio data +.path.anm = $(ROOTDIR)art\$(INGAMEDIR)\\ # Raw animation file +.path.ash = .\\ # Autogenerated assembly header file +.path.asm = .\\ # Raw assembly file +.path.aud = $(ABSROOT)code\cps\\ # Game formatted audio data +.path.bin = $(MAPDIR)maps\\ # Scenario map data files. +.path.c = .\\ # Raw C file +.path.dmo = $(ROOTCDSDIR)DEMO\\ # CD for Demo Version +.path.dm2 = $(ROOTCDSDIR)DEMO2\\ # CD for Demo Version +.path.exp = $(ROOTCDSDIR)COVERT\\ # CD #1 expansion missions +.path.cpp = .\\ # Raw C++ file +.path.cps = $(ROOTCODEDIR)cps\\ # Compressed art +.path.dat = $(ROOTCODEDIR)cps\\ # Generic data +.path.des = $(ROOTCODEDIR)cps\\ # Desert icons +.path.dip = $(ROOTCODEDIR)cps\\ # Generic compressed text (obsolete) +.path.eng = $(ROOTCODEDIR)cps\\ # English compressed text +.path.exe = $(ROOTDIR)run\\ # Executable (relative path from OBJ directory) +.path.fin = $(ROOTDIR)art\$(INGAMEDIR)\fin\\ # Finale source animation +.path.fnt = $(ROOTCODEDIR)cps\\ # Font (compressed) +.path.foc = $(ROOTDIR)audio\french\\ # French digitized voices +.path.fre = $(ROOTCODEDIR)cps\\ # French compressed text +.path.gdi = $(ROOTCODEDIR)cps\\ # Good guy compressed files +.path.ger = $(ROOTCODEDIR)cps\\ # German compressed text +.path.goc = $(ROOTDIR)audio\german\\ # German digitized voices +.path.h = .\\ # C header file +.path.i = .\\ # Assembly header +.path.icn = $(ROOTCODEDIR)cps\\ # Icon file (compressed) +.path.ini = $(MAPDIR)maps\\ # INI control files +.path.jp = $(ROOTCODEDIR)cps\\ # Dinosaur compressed files +.path.jun = $(ROOTCODEDIR)cps\\ # Jungle icons +.path.juv = $(ABSROOT)code\cps\\ # Juvenile sound effects. +.path.lbm = $(ROOTDIR)art\$(INGAMEDIR)\\ # Source art (uncompressed) +.path.mak = .\\ # Shape making control file +.path.map = $(ROOTCODEDIR)cps\\ # Icon map data file +.path.mid = $(ROOTDIR)audio\mid\\ # Midi file +.path.cd1 = $(ROOTCDSDIR)CD1\\ # CD #1 (GDI) +.path.cd2 = $(ROOTCDSDIR)CD2\\ # CD #2 (NOD) +.path.mix = $(ROOTCDDIR) # Mix-file location. +.path.mrf = $(ROOTCODEDIR)cps\\ # Palette morph data files +.path.nod = $(ROOTCODEDIR)cps\\ # Bad guy compressed files +.path.o = obj\\ # Overlay object file +.path.obj = obj\\ # Object file +.path.out = .\\ # Temporary parser file. +.path.pak = $(ROOTDIR)run\\ # Packed file +.path.pal = $(ROOTCODEDIR)cps\\ # Palette file (processed) +.path.shp = $(ROOTCODEDIR)cps\\ # Shape file (processed) +.path.sym = .\\ # Precompiled header symbol file. +.path.tbl = $(ROOTCODEDIR)cps\\ # Remap table file. +.path.tem = $(ROOTCODEDIR)cps\\ # Temperate icons +.path.txt = $(ROOTDIR)eng\\ # Default location for text files. +.path.v00 = $(ABSROOT)code\cps\\ # Infantry response. +.path.v01 = $(ABSROOT)code\cps\\ # Infantry response. +.path.v02 = $(ABSROOT)code\cps\\ # Infantry response. +.path.v03 = $(ABSROOT)code\cps\\ # Infantry response. +.path.v04 = $(ABSROOT)code\cps\\ # Infantry response. +.path.v16 = $(ABSROOT)code\cps\\ # Game formatted audio data +.path.v8 = $(ABSROOT)code\cps\\ # Game formatted audio data +.path.vqa = $(VQADIR)movies$(LANGDIR)\\ # VQA movie files location. +.path.vqp = $(ABSROOT)code\cps\\ # VQA Movie Interpolated palette files +.path.wav = $(WAVDIR) # Digitized sample file (generic) +.path.win = $(ROOTCODEDIR)cps\\ # Winter icons +.path.wsa = $(ROOTCODEDIR)cps\\ # Processed animation file +.path.wv = $(ROOTDIR)audio\wv\\ # Digitized sample file (generic) +.path.xmi = $(ROOTDIR)audio\xmi\\ # MT-32 midi scores +.path.pcx = $(ROOTCODEDIR)cps\\ #.pcx files + + +########################################################################## +# Rules + +# +# Rule for converting anim files. +# +.anm.wsa: + utils\animate $*.anm $(.path.anm)$&.lbm $(.path.wsa)$&.wsa -l -f -p + +# +# Rule for converting art files. +# +.lbm.cps: + utils\wwcomp $*.lbm $(.path.cps)$&.cps -ex -i + +# +# Rule for converting outtake first frame art files. +# +{$(.path.lbm)outtake}.lbm.cps: + utils\wwcomp $*.lbm $(.path.cps)$&.cps -ex + +{$(.path.anm)outtake}.anm.wsa: + utils\animate $*.anm $(.path.anm)outtake\$&.lbm $(.path.wsa)$&.wsa -l -f -p + +{$(.path.anm)score}.anm.wsa: + utils\animate $*.anm $(.path.wsa)$&.wsa -p + +{$(.path.anm)score\\$(LANGDIR)\\}.anm.wsa: + utils\animate $*.anm $(.path.wsa)$&.wsa -p + +######################################################### +# Rule for creating palette files. +# +{$(.path.lbm)palettes}.lbm.pal: + utils\wwcomp $*.lbm -ppal + copy $&.pal $(.path.pal)$&.pal + del $&.pal + +{$(.path.lbm)score}.lbm.pal: + utils\wwcomp $*.lbm -ppal + copy $&.pal $(.path.pal)$&.pal + del $&.pal + +######################################################### +# Rule for creating vehicles. +{$(.path.anm)units}.anm.shp: + utils\keyframe $*.anm $(.path.shp)$&.shp -l + +######################################################### +# Rule for creating buildings. +{$(.path.anm)building}.anm.shp: + utils\keyframe $*.anm $(.path.shp)$&.shp -l + +######################################################### +# Rule for creating misc shape animations. +{$(.path.anm)anim}.anm.shp: + utils\keyframe $*.anm $(.path.shp)$&.shp -l + +######################################################### +# Rule for converting font files. +{$(.path.lbm)fonts}.lbm.fnt: + echo Creating font file "$&.fnt". + utils\fontmake $*.lbm -o$(.path.fnt)$&.fnt + +######################################################### +# Rules for creating overlay files. +{$(.path.lbm)overlay}.lbm.cps: + utils\wwcomp $*.lbm $(.path.cps)$&.cps + +{$(.path.anm)overlay}.anm.shp: + utils\keyframe $*.anm $(.path.shp)$&.shp -l + +######################################################### +# Rules for creating temperate files. +{$(.path.lbm)temperat\\$(LANGDIR)\\}.lbm.tem: + utils\iconmap -r -w3 -o$(.path.tem)$&.tem $*.lbm + +{$(.path.anm)temperat\\$(LANGDIR)\\}.anm.tem: + utils\keyframe $*.anm $(.path.tem)$&.tem -l + +######################################################### +# Rules for creating winter files. +{$(.path.lbm)winter\\$(LANGDIR)\\}.lbm.win: + utils\iconmap -r -w3 -o$(.path.win)$&.win $*.lbm + +{$(.path.anm)winter\\$(LANGDIR)\\}.anm.win: + utils\keyframe $*.anm $(.path.win)$&.win -l + +######################################################### +# Rules for creating desert files. +{$(.path.lbm)desert\\$(LANGDIR)\\}.lbm.des: + utils\iconmap -r -w3 -o$(.path.des)$&.des $*.lbm + +{$(.path.anm)desert\\$(LANGDIR)\\}.anm.des: + utils\keyframe $*.anm $(.path.des)$&.des -l + +######################################################### +# Rules for creating jungle files. +{$(.path.lbm)jungle}.lbm.jun: + utils\iconmap -r -w3 -o$(.path.jun)$&.jun $*.lbm + +{$(.path.anm)jungle}.anm.jun: + utils\keyframe $*.anm $(.path.jun)$&.jun -l + + +######################################################### +# Generic icon file creation. +.lbm.icn: + utils\iconmap -r -w3 -o$(.path.icn)$&.icn $*.lbm + + +######################################################### +# Text files. +{eng}.txt.eng: + utils\textmake $*.txt $(.path.eng)$&.eng $&.h + +{ger}.txt.ger: + utils\textmake $*.txt $(.path.ger)$&.ger $&.h + +{fre}.txt.fre: + utils\textmake $*.txt $(.path.fre)$&.fre $&.h + +######################################################### +# Score/mapsel files. +{$(.path.anm)score}.anm.shp: + utils\keyframe $*.anm $(.path.shp)$&.shp -l + +{$(.path.lbm)score}.lbm.cps: + utils\wwcomp $*.lbm $(.path.cps)$&.cps -ex -i + + +######################################################### +# Generic shape files. +#.lbm.shp: +# utils\makeshps -q $*.lbm $&.mak $(.path.cps)$&.shp $(SHAPEBUFFSIZE) + +{$(.path.anm)generic}.anm.shp: + utils\keyframe $*.anm $(.path.shp)$&.shp -l + +.c.lob: + utils\LINT -ml -si2 -sl4 +v $(WWLIB)WWLIB.LNT $*.c >$&.err + +# Generic shape file creation. +{$(.path.anm)}.anm.shp: + utils\keyframe $*.anm $(.path.shp)$&.shp -l + +{$(.path.lbm)units}.lbm.cps: + utils\wwcomp $*.lbm $(.path.cps)$&.cps -ex -i + + +########################################################## +# Hi-res shape files +# +{$(.path.anm)hires}.anm.shp: + utils\keyframe $*.anm $(.path.shp)$&.shp -l +# Hi-res font files +{$(.path.lbm)hires}.lbm.fnt: + echo Creating font file "$&.fnt". + utils\fontmake $*.lbm -o$(.path.fnt)$&.fnt + + +################################### +# NOD data file creation rules. +{$(.path.anm)nod}.anm.nod: + utils\keyframe $*.anm $(.path.nod)$&.nod -l + +{$(.path.lbm)nod}.lbm.nod: + utils\wwcomp $*.lbm $(.path.nod)$&.nod -ex -i + +################################### +# GDI data file creation rules. +{$(.path.anm)gdi}.anm.gdi: + utils\keyframe $*.anm $(.path.gdi)$&.gdi -l + +{$(.path.lbm)gdi}.lbm.gdi: + utils\wwcomp $*.lbm $(.path.gdi)$&.gdi -ex -i + +################################### +# Dino data file creation rules. +{$(.path.anm)jp}.anm.jp: + utils\keyframe $*.anm $(.path.jp)$&.jp -l + +{$(.path.lbm)jp}.lbm.jp: + utils\wwcomp $*.lbm $(.path.jp)$&.jp -ex -i + +####################################### +# Rules to convert 8 bit audio files. +{$(.path.wav)audio8\scores\mono}.wav.a8: + utils\audiomak $(.path.wav)audio8\scores\mono\$&.wav $(.path.aud)$&.a8 + +{$(.path.wav)audio8\scores\mono\olf}.wav.v8: + utils\audiomak $(.path.wav)audio8\scores\mono\old\$&.wav $(.path.aud)$&.v8 + +{$(.path.wav)audio8\speech}.wav.a8: + utils\audiomak $(.path.wav)audio8\speech\$&.wav $(.path.aud)$&.a8 + +{$(.path.wav)audio8\sfx\juvenile}.wav.a8j: + utils\audiomak $(.path.wav)audio8\sfx\juvenile\$&.wav $(.path.juv)$&.a8j + +{$(.path.wav)audio8\sfx\adult}.wav.a8a: + utils\audiomak $(.path.wav)audio8\sfx\adult\$&.wav $(.path.aud)$&.a8a + +{$(.path.wav)audio8\sfx\generic}.wav.a8: + utils\audiomak $(.path.wav)audio8\sfx\generic\$&.wav $(.path.aud)$&.a8 + +{$(.path.wav)audio8\sfx\b&b}.wav.a8: + utils\audiomak $(.path.wav)audio8\sfx\b&b\$&.wav $(.path.aud)$&.a8 + +{$(.path.wav)audio8\sfx\v00}.wav.a80: + utils\audiomak $(.path.wav)audio8\sfx\v00\$&.wav $(.path.aud)$&.a80 + +{$(.path.wav)audio8\sfx\v01}.wav.a81: + utils\audiomak $(.path.wav)audio8\sfx\v01\$&.wav $(.path.aud)$&.a81 + +{$(.path.wav)audio8\sfx\v02}.wav.a82: + utils\audiomak $(.path.wav)audio8\sfx\v02\$&.wav $(.path.aud)$&.a82 + +{$(.path.wav)audio8\sfx\v03}.wav.a83: + utils\audiomak $(.path.wav)audio8\sfx\v03\$&.wav $(.path.aud)$&.a83 + +{$(.path.wav)audio8\sfx\v04}.wav.a84: + utils\audiomak $(.path.wav)audio8\sfx\v04\$&.wav $(.path.aud)$&.a84 + + +####################################### +# Rules to convert 16 bit audio files. +{$(.path.wav)audio16\sfx\mobius$(LANGDIR)}.wav.a6: + utils\audiomak $(.path.wav)audio16\sfx\mobius$(LANGDIR)\$&.wav $(.path.aud)$&.a6 + +{$(.path.wav)audio16\scores\mono}.wav.a6: + utils\audiomak $(.path.wav)audio16\scores\mono\$&.wav $(.path.aud)$&.a6 + +{$(.path.wav)audio16\scores\mono\old}.wav.v16: + utils\audiomak $(.path.wav)audio16\scores\mono\old\$&.wav $(.path.aud)$&.v16 + +{$(.path.wav)audio16\speech$(LANGDIR)}.wav.a6: + utils\audiomak $(.path.wav)audio16\speech$(LANGDIR)\$&.wav $(.path.aud)$&.a6 + +{$(.path.wav)audio16\sfx\juvenile}.wav.a6j: + utils\audiomak $(.path.wav)audio16\sfx\juvenile\$&.wav $(.path.juv)$&.a6j + +{$(.path.wav)audio16\sfx\adult$(LANGDIR)}.wav.a6a: + utils\audiomak $(.path.wav)audio16\sfx\adult$(LANGDIR)\$&.wav $(.path.aud)$&.a6a + +{$(.path.wav)audio16\sfx\generic$(LANGDIR)}.wav.a6: + utils\audiomak $(.path.wav)audio16\sfx\generic$(LANGDIR)\$&.wav $(.path.aud)$&.a6 + +{$(.path.wav)audio16\sfx\b&b$(LANGDIR)}.wav.a6: + utils\audiomak $(.path.wav)audio16\sfx\b&b$(LANGDIR)\$&.wav $(.path.aud)$&.a6 + +{$(.path.wav)audio16\sfx\v00$(LANGDIR)}.wav.a60: + utils\audiomak $(.path.wav)audio16\sfx\v00$(LANGDIR)\$&.wav $(.path.aud)$&.a60 + +{$(.path.wav)audio16\sfx\v01$(LANGDIR)}.wav.a61: + utils\audiomak $(.path.wav)audio16\sfx\v01$(LANGDIR)\$&.wav $(.path.aud)$&.a61 + +{$(.path.wav)audio16\sfx\v02$(LANGDIR)}.wav.a62: + utils\audiomak $(.path.wav)audio16\sfx\v02$(LANGDIR)\$&.wav $(.path.aud)$&.a62 + +{$(.path.wav)audio16\sfx\v03$(LANGDIR)}.wav.a63: + utils\audiomak $(.path.wav)audio16\sfx\v03$(LANGDIR)\$&.wav $(.path.aud)$&.a63 + +{$(.path.wav)audio16\sfx\v04}.wav.a64: + utils\audiomak $(.path.wav)audio16\sfx\v04\$&.wav $(.path.aud)$&.a64 + diff --git a/SAVEDLG.H b/SAVEDLG.H new file mode 100644 index 0000000..7e7adac --- /dev/null +++ b/SAVEDLG.H @@ -0,0 +1,71 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\savedlg.h_v 2.14 16 Oct 1995 16:45:12 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SAVEDLG.H * + * * + * Programmer : Maria del Mar McCready Legg, Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : Jan 18, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SAVEDLG_H +#define SAVEDLG_H + +#include "gadget.h" + +class SaveOptionsClass +{ + private: + + enum SaveOptionsClassEnums { + BUTTON_CANCEL=200, + BUTTON_SAVE, + OPTION_WIDTH=216, + OPTION_HEIGHT=122, + OPTION_X=((320 - OPTION_WIDTH) / 2) & ~7, + OPTION_Y=(200 - OPTION_HEIGHT) / 2, + NUMBER_OF_BUTTONS=2, + CAPTION_Y_POS=5, + BORDER1_LEN=49, + BUTTON_CANCEL_X=90, + BUTTON_CANCEL_Y=103, + LISTBOX_X=40, + LISTBOX_Y=24, + LISTBOX_W=136, + LISTBOX_H=72 + }; + + public: + SaveOptionsClass (void) { }; + void Process (void); +}; + + +#endif diff --git a/SAVELOAD.CPP b/SAVELOAD.CPP new file mode 100644 index 0000000..d9cc57d --- /dev/null +++ b/SAVELOAD.CPP @@ -0,0 +1,1392 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\saveload.cpv 2.18 16 Oct 1995 16:48:44 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SAVELOAD.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 23, 1994 * + * * + * Last Update : June 24, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Code_All_Pointers -- Code all pointers. * + * Decode_All_Pointers -- Decodes all pointers. * + * Get_Savefile_Info -- gets description, scenario #, house * + * Load_Game -- loads a saved game * + * Load_Misc_Values -- Loads miscellaneous variables. * + * Load_Misc_Values -- loads miscellaneous variables * + * Read_Object -- reads an object from disk, in a safe way * + * Save_Game -- saves a game to disk * + * Save_Misc_Values -- saves miscellaneous variables * + * Target_To_TechnoType -- converts TARGET to TechnoTypeClass * + * TechnoType_To_Target -- converts TechnoTypeClass to TARGET * + * Write_Object -- reads an object from disk, in a safe way * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +********************************** Defines ********************************** +*/ +#define SAVEGAME_VERSION (DESCRIP_MAX + \ + 0x01000003 + ( \ + sizeof(AircraftClass) + \ + sizeof(AircraftTypeClass) + \ + sizeof(AnimClass) + \ + sizeof(AnimTypeClass) + \ + sizeof(BuildingClass) + \ + sizeof(BuildingTypeClass) + \ + sizeof(BulletClass) + \ + sizeof(BulletTypeClass) + \ + sizeof(HouseClass) + \ + sizeof(HouseTypeClass) + \ + sizeof(InfantryClass) + \ + sizeof(InfantryTypeClass) + \ + sizeof(OverlayClass) + \ + sizeof(OverlayTypeClass) + \ + sizeof(SmudgeClass) + \ + sizeof(SmudgeTypeClass) + \ + sizeof(TeamClass) + \ + sizeof(TeamTypeClass) + \ + sizeof(TemplateClass) + \ + sizeof(TemplateTypeClass) + \ + sizeof(TerrainClass) + \ + sizeof(TerrainTypeClass) + \ + sizeof(UnitClass) + \ + sizeof(UnitTypeClass) + \ + sizeof(MouseClass) + \ + sizeof(CellClass) + \ + sizeof(FactoryClass) + \ + sizeof(BaseClass) + \ + sizeof(LayerClass) + \ + sizeof(BriefingText) + \ + sizeof(Waypoint))) + + +/*************************************************************************** + * Save_Game -- saves a game to disk * + * * + * Saving the Map: * + * DisplayClass::Save() invokes CellClass's Write() for every cell * + * that needs to be saved. A cell needs to be saved if it contains * + * any special data at all, such as a TIcon, or an Occupier. * + * The cell saves its own CellTrigger pointer, converted to a TARGET. * + * * + * Saving game objects: * + * - Any object stored in an ArrayOf class needs to be saved. The ArrayOf* + * Save() routine invokes each object's Write() routine, if that * + * object's IsActive is set. * + * * + * Saving the layers: * + * The Map's Layers (Ground, Air, etc) of things that are on the map, * + * and the Logic's Layer of things to process both need to be saved. * + * LayerClass::Save() writes the entire layer array to disk * + * * + * Saving the houses: * + * Each house needs to be saved, to record its Credits, Power, etc. * + * * + * Saving miscellaneous data: * + * There are a lot of miscellaneous variables to save, such as the * + * map's dimensions, the player's house, etc. * + * * + * INPUT: * + * id numerical ID, for the file extension * + * * + * OUTPUT: * + * true = OK, false = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + *=========================================================================*/ +bool Save_Game(int id,char *descr) +{ + RawFileClass file; + char name[_MAX_FNAME+_MAX_EXT]; + int i; + unsigned long version; + unsigned scenario; + HousesType house; + char descr_buf[DESCRIP_MAX]; + + scenario = Scenario; // get current scenario # + house = PlayerPtr->Class->House; // get current house + + /* + ** Generate the filename to save + */ + sprintf(name, "SAVEGAME.%03d", id); + + /* + ** Code everybody's pointers + */ + Code_All_Pointers(); + + /* + ** Open the file + */ + if (!file.Open(name, WRITE)) { + Decode_All_Pointers(); + return(false); + } + + /* + ** Save the description, scenario #, and house + ** (scenario # & house are saved separately from the actual Scenario & + ** PlayerPtr globals for convenience; we can quickly find out which + ** house & scenario this save-game file is for by reading these values. + ** Also, PlayerPtr is stored in a coded form in Save_Misc_Values(), + ** which may or may not be a HousesType number; so, saving 'house' + ** here ensures we can always pull out the house for this file.) + */ + sprintf(descr_buf, "%s\r\n",descr); // put CR-LF after text + descr_buf[strlen(descr_buf) + 1] = 26; // put CTRL-Z after NULL + + if (file.Write(descr_buf, DESCRIP_MAX) != DESCRIP_MAX) { + file.Close(); + return(false); + } + + if (file.Write(&scenario, sizeof(scenario)) != sizeof(scenario)) { + file.Close(); + return(false); + } + + if (file.Write(&house, sizeof(house)) != sizeof(house)) { + file.Close(); + return(false); + } + + /* + ** Save the save-game version, for loading verification + */ + version = SAVEGAME_VERSION; + + if (file.Write(&version, sizeof(version)) != sizeof(version)) { + file.Close(); + return(false); + } + + Call_Back(); + /* + ** Save the map. The map must be saved first, since it saves the Theater. + */ + Map.Save(file); + + Call_Back(); + /* + ** Save all game objects. This code saves every object that's stored in a + ** TFixedIHeap class. + */ + if (!Houses.Save(file) || + !TeamTypes.Save(file) || + !Teams.Save(file) || + !Triggers.Save(file) || + !Aircraft.Save(file) || + !Anims.Save(file) || + !Buildings.Save(file) || + !Bullets.Save(file) || + !Infantry.Save(file) || + !Overlays.Save(file) || + !Smudges.Save(file) || + !Templates.Save(file) || + !Terrains.Save(file) || + !Units.Save(file) || + !Factories.Save(file)) { + file.Close(); + + Decode_All_Pointers(); + + return(false); + } + + Call_Back(); + /* + ** Save the Logic & Map layers + */ + if (!Logic.Save(file)) { + file.Close(); + Decode_All_Pointers(); + return(false); + } + + for (i = 0; i < LAYER_COUNT; i++) { + if (!Map.Layer[i].Save(file)) { + file.Close(); + Decode_All_Pointers(); + return(false); + } + } + + /* + ** Save the Score + */ + if (!Score.Save(file)) { + file.Close(); + Decode_All_Pointers(); + return(false); + } + + /* + ** Save the AI Base + */ + if (!Base.Save(file)) { + file.Close(); + Decode_All_Pointers(); + return(false); + } + + /* + ** Save miscellaneous variables. + */ + if (!Save_Misc_Values(file)) { + file.Close(); + Decode_All_Pointers(); + return(false); + } + + Call_Back(); + /* + ** Close the file; we're done + */ + file.Close(); + Decode_All_Pointers(); + + return(true); +} + + +/*************************************************************************** + * Load_Game -- loads a saved game * + * * + * This routine loads the data in the same way it was saved out. * + * * + * Loading the Map: * + * - DisplayClass::Load() invokes CellClass's Load() for every cell * + * that was saved. * + * - The cell loads its own CellTrigger pointer. * + * * + * Loading game objects: * + * - IHeap's Load() routine loads the # of objects stored, and loads * + * each object. * + * - Triggers: Add themselves to the HouseTriggers if they're associated * + * with a house * + * * + * Loading the layers: * + * LayerClass::Load() reads the entire layer array to disk * + * * + * Loading the houses: * + * Each house is loaded in its entirety. * + * * + * Loading miscellaneous data: * + * There are a lot of miscellaneous variables to load, such as the * + * map's dimensions, the player's house, etc. * + * * + * INPUT: * + * id numerical ID, for the file extension * + * * + * OUTPUT: * + * true = OK, false = error * + * * + * WARNINGS: * + * If this routine returns false, the entire game will be in an * + * unknown state, so the scenario will have to be re-initialized. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + *=========================================================================*/ +bool Load_Game(int id) +{ + RawFileClass file; + char name[_MAX_FNAME+_MAX_EXT]; + int i; + unsigned long version; + unsigned scenario; + HousesType house; + char descr_buf[DESCRIP_MAX]; + + /* + ** Generate the filename to load + */ + sprintf(name, "SAVEGAME.%03d", id); + + /* + ** Open the file + */ + if (!file.Open(name, READ)) { + return(false); + } + + /* + ** Read & discard the save-game's header info + */ + if (file.Read(descr_buf, DESCRIP_MAX) != DESCRIP_MAX) { + file.Close(); + return(false); + } + + if (file.Read(&scenario, sizeof(scenario)) != sizeof(scenario)) { + file.Close(); + return(false); + } + + if (file.Read(&house, sizeof(house)) != sizeof(house)) { + file.Close(); + return(false); + } + + Call_Back(); + /* + ** Clear the scenario so we start fresh; this calls the Init_Clear() routine + ** for the Map, and all object arrays. It has the following important + ** effects: + ** - Every cell is cleared to 0's, via MapClass::Init_Clear() + ** - All heap elements' are cleared + ** - The Houses are Initialized, which also clears their HouseTriggers + ** array + ** - The map's Layers & Logic Layer are cleared to empty + ** - The list of currently-selected objects is cleared + */ + Clear_Scenario(); + + /* + ** Read in & verify the save-game ID code + */ + if (file.Read(&version,sizeof(version)) != sizeof(version)) { + file.Close(); + return(false); + } + + if (version != SAVEGAME_VERSION) { + file.Close(); + return(false); + } + + Call_Back(); + /* + ** Set the required CD to be in the drive according to the scenario + ** loaded. + */ + if (RequiredCD != -2) { + if (scenario >= 20 && scenario <60 && GameToPlay == GAME_NORMAL) { + RequiredCD = 2; + } else { + if (scenario >= 60){ + /* + ** This is a gateway bonus scenario + */ + RequiredCD = -1; + }else{ + if (house == HOUSE_GOOD) { + RequiredCD = 0; + } else { + RequiredCD = 1; + } + } + } + } + if(!Force_CD_Available(RequiredCD)) { + Prog_End(); + exit(EXIT_FAILURE); + } + + Call_Back(); + + /* + ** Load the map. The map comes first, since it loads the Theater & init's + ** mixfiles. The map calls all the type-class's Init routines, telling them + ** what the Theater is; this must be done before any objects are created, so + ** they'll be properly created. + */ + Map.Load(file); + + Call_Back(); + /* + ** Load the object data. + */ + if (!Houses.Load(file) || + !TeamTypes.Load(file) || + !Teams.Load(file) || + !Triggers.Load(file) || + !Aircraft.Load(file) || + !Anims.Load(file) || + !Buildings.Load(file) || + !Bullets.Load(file) || + !Infantry.Load(file) || + !Overlays.Load(file) || + !Smudges.Load(file) || + !Templates.Load(file) || + !Terrains.Load(file) || + !Units.Load(file) || + !Factories.Load(file)) { + file.Close(); + return(false); + } + + Call_Back(); + /* + ** Load the Logic & Map Layers + */ + if (!Logic.Load(file)) { + file.Close(); + return(false); + } + for (i = 0; i < LAYER_COUNT; i++) { + if (!Map.Layer[i].Load(file)) { + file.Close(); + return(false); + } + } + + Call_Back(); + /* + ** Load the Score + */ + if (!Score.Load(file)) { + file.Close(); + return(false); + } + + /* + ** Load the AI Base + */ + if (!Base.Load(file)) { + file.Close(); + return(false); + } + + /* + ** Load miscellaneous variables, including the map size & the Theater + */ + if (!Load_Misc_Values(file)) { + file.Close(); + return(false); + } + + file.Close(); + Decode_All_Pointers(); + Map.Init_IO(); + Map.Flag_To_Redraw(true); + + ScenarioInit = 0; + +#ifdef DEMO + if (Scenario != 10 && Scenario != 1 && Scenario != 6) { + Clear_Scenario(); + return(false); + } +#endif + + Call_Back(); + return(true); +} + + +/*************************************************************************** + * Save_Misc_Values -- saves miscellaneous variables * + * * + * INPUT: * + * file file to use for writing * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/29/1994 BR : Created. * + *=========================================================================*/ +bool Save_Misc_Values(FileClass &file) +{ + int i; + int count; // # ptrs in 'CurrentObject' + ObjectClass * ptr; // for saving 'CurrentObject' ptrs + + /* + ** Player's House. + */ + if (file.Write(&PlayerPtr, sizeof(PlayerPtr)) != sizeof(PlayerPtr)) { + return(false); + } + + /* + ** Save this scenario number. + */ + if (file.Write(&Scenario, sizeof(Scenario)) != sizeof(Scenario)) { + return(false); + } + + /* + ** Save frame #. + */ + if (file.Write(&Frame, sizeof(Frame)) != sizeof(Frame)) { + return(false); + } + + /* + ** Save VQ Movie names. + */ + if (file.Write(WinMovie, sizeof(WinMovie)) != sizeof(WinMovie)) { + return(false); + } + + if (file.Write(LoseMovie, sizeof(LoseMovie)) != sizeof(LoseMovie)) { + return(false); + } + + /* + ** Save currently-selected objects list. + ** Save the # of ptrs in the list. + */ + count = CurrentObject.Count(); + if (file.Write(&count, sizeof(count)) != sizeof(count)) { + return(false); + } + + /* + ** Save the pointers. + */ + for (i = 0; i < count; i++) { + ptr = CurrentObject[i]; + if (file.Write(&ptr, sizeof(ptr)) != sizeof(ptr)) { + return(false); + } + } + + /* + ** Save the list of waypoints. + */ + if (file.Write(Waypoint, sizeof(Waypoint)) != sizeof(Waypoint)) { + return(false); + } + + file.Write(&ScenDir, sizeof(ScenDir)); + file.Write(&ScenVar, sizeof(ScenVar)); + file.Write(&CarryOverMoney, sizeof(CarryOverMoney)); + file.Write(&CarryOverPercent, sizeof(CarryOverPercent)); + file.Write(&BuildLevel, sizeof(BuildLevel)); + file.Write(BriefMovie, sizeof(BriefMovie)); + file.Write(Views, sizeof(Views)); + file.Write(&EndCountDown, sizeof(EndCountDown)); + file.Write(BriefingText, sizeof(BriefingText)); + + // This is new... + file.Write(ActionMovie, sizeof(ActionMovie)); + + return(true); +} + + +/*********************************************************************************************** + * Load_Misc_Values -- Loads miscellaneous variables. * + * * + * INPUT: file -- The file to load the misc values from. * + * * + * OUTPUT: Was the misc load process successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 BRR : Created. * + *=============================================================================================*/ +bool Load_Misc_Values(FileClass &file) +{ + int i; + int count; // # ptrs in 'CurrentObject' + ObjectClass * ptr; // for loading 'CurrentObject' ptrs + + /* + ** Player's House. + */ + if (file.Read(&PlayerPtr, sizeof(PlayerPtr)) != sizeof(PlayerPtr)) { + return(false); + } + + /* + ** Read this scenario number. + */ + if (file.Read(&Scenario,sizeof(Scenario)) != sizeof(Scenario)) { + return(false); + } + + /* + ** Load frame #. + */ + if (file.Read(&Frame, sizeof(Frame)) != sizeof(Frame)) { + return(false); + } + + /* + ** Load VQ Movie names. + */ + if (file.Read(WinMovie, sizeof(WinMovie)) != sizeof(WinMovie)) { + return(false); + } + + if (file.Read(LoseMovie, sizeof(LoseMovie)) != sizeof(LoseMovie)) { + return(false); + } + + /* + ** Load currently-selected objects list. + ** Load the # of ptrs in the list. + */ + if (file.Read(&count, sizeof(count)) != sizeof(count)) { + return(false); + } + + /* + ** Load the pointers. + */ + for (i = 0; i < count; i++) { + if (file.Read(&ptr, sizeof(ptr)) != sizeof(ptr)) { + return(false); + } + CurrentObject.Add(ptr); // add to the list + } + + /* + ** Save the list of waypoints. + */ + if (file.Read(Waypoint, sizeof(Waypoint)) != sizeof(Waypoint)) { + return(false); + } + + file.Read(&ScenDir, sizeof(ScenDir)); + file.Read(&ScenVar, sizeof(ScenVar)); + file.Read(&CarryOverMoney, sizeof(CarryOverMoney)); + file.Read(&CarryOverPercent, sizeof(CarryOverPercent)); + file.Read(&BuildLevel, sizeof(BuildLevel)); + file.Read(BriefMovie, sizeof(BriefMovie)); + file.Read(Views, sizeof(Views)); + file.Read(&EndCountDown, sizeof(EndCountDown)); + file.Read(BriefingText, sizeof(BriefingText)); + + if (file.Seek(0, SEEK_CUR) < file.Size()) { + file.Read(ActionMovie, sizeof(ActionMovie)); + } + + return(true); +} + + +/*********************************************************************************************** + * Code_All_Pointers -- Code all pointers. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 BRR : Created. * + *=============================================================================================*/ +void Code_All_Pointers(void) +{ + int i; + + /* + ** The Map. + */ + Map.Code_Pointers(); + + /* + ** The ArrayOf's. + */ + TeamTypes.Code_Pointers(); + Teams.Code_Pointers(); + Triggers.Code_Pointers(); + Aircraft.Code_Pointers(); + Anims.Code_Pointers(); + Buildings.Code_Pointers(); + Bullets.Code_Pointers(); + Infantry.Code_Pointers(); + Overlays.Code_Pointers(); + Smudges.Code_Pointers(); + Templates.Code_Pointers(); + Terrains.Code_Pointers(); + Units.Code_Pointers(); + Factories.Code_Pointers(); + + /* + ** The Layers. + */ + Logic.Code_Pointers(); + for (i = 0; i < LAYER_COUNT; i++) { + Map.Layer[i].Code_Pointers(); + } + + /* + ** The Score. + */ + Score.Code_Pointers(); + + /* + ** The Base. + */ + Base.Code_Pointers(); + + /* + ** PlayerPtr. + */ + PlayerPtr = (HouseClass *)(PlayerPtr->Class->House); + + /* + ** Currently-selected objects. + */ + for (i = 0; i < CurrentObject.Count(); i++) { + CurrentObject[i] = (ObjectClass *)CurrentObject[i]->As_Target(); + } + + /* + ** Houses must be coded last, because the Class->House member of the HouseClass + ** is used to code HouseClass pointers for all other objects, and if Class is + ** coded, it will point to a meaningless value. + */ + Houses.Code_Pointers(); +} + + +/*********************************************************************************************** + * Decode_All_Pointers -- Decodes all pointers. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 BRR : Created. * + *=============================================================================================*/ +void Decode_All_Pointers(void) +{ + int i; + + /* + ** The Map. + */ + Map.Decode_Pointers(); + + /* + ** Decode houses first, so we can properly decode all other objects' + ** House pointers + */ + Houses.Decode_Pointers(); + + /* + ** The ArrayOf's. + */ + TeamTypes.Decode_Pointers(); + Teams.Decode_Pointers(); + Triggers.Decode_Pointers(); + Aircraft.Decode_Pointers(); + Anims.Decode_Pointers(); + Buildings.Decode_Pointers(); + Bullets.Decode_Pointers(); + Infantry.Decode_Pointers(); + Overlays.Decode_Pointers(); + Smudges.Decode_Pointers(); + Templates.Decode_Pointers(); + Terrains.Decode_Pointers(); + Units.Decode_Pointers(); + Factories.Decode_Pointers(); + + /* + ** The Layers. + */ + Logic.Decode_Pointers(); + for (i = 0; i < LAYER_COUNT; i++) { + Map.Layer[i].Decode_Pointers(); + } + + /* + ** The Score. + */ + Score.Decode_Pointers(); + + /* + ** The Base. + */ + Base.Decode_Pointers(); + + /* + ** PlayerPtr. + */ + PlayerPtr = HouseClass::As_Pointer((HousesType)PlayerPtr); + Whom = PlayerPtr->Class->House; + switch (PlayerPtr->Class->House) { + case HOUSE_GOOD: + ScenPlayer = SCEN_PLAYER_GDI; + break; + + case HOUSE_BAD: + ScenPlayer = SCEN_PLAYER_NOD; + break; + + case HOUSE_JP: + ScenPlayer = SCEN_PLAYER_JP; + break; + } + Check_Ptr(PlayerPtr,__FILE__,__LINE__); + + Set_Scenario_Name(ScenarioName, Scenario, ScenPlayer, ScenDir, ScenVar); + + /* + ** Currently-selected objects. + */ + for (i = 0; i < CurrentObject.Count(); i++) { + CurrentObject[i] = As_Object((TARGET)CurrentObject[i]); + Check_Ptr(CurrentObject[i],__FILE__,__LINE__); + } + + /* + ** Last-Minute Fixups; to resolve these pointers properly requires all other + ** pointers to be loaded & decoded. + */ + if (Map.PendingObjectPtr) { + Map.PendingObject = &Map.PendingObjectPtr->Class_Of(); + Check_Ptr((void *)Map.PendingObject, __FILE__, __LINE__); + Map.Set_Cursor_Shape(Map.PendingObject->Occupy_List(true)); + } else { + Map.PendingObject = 0; + Map.Set_Cursor_Shape(0); + } +} + + +/*********************************************************************************************** + * Read_Object -- reads an object from disk * + * * + * This routine reads in an object and fills in the virtual function table pointer. * + * * + * INPUT: * + * ptr pointer to object to read * + * base_size size of object's absolute base class * + * class_size size of the class itself * + * file file to use for I/O * + * vtable virtual function table pointer value, NULL if none * + * * + * OUTPUT: * + * true = OK, false = error * + * * + * WARNINGS: * + * This routine ASSUMES the program modules are compiled with: * + * -Vb- Always make the virtual function table ptr 2 bytes long * + * -Vt Put the virtual function table after the 1st class's data * + * * + * ALSO, the class used to compute 'base_size' must come first in a multiple-inheritence * + * hierarchy. AND, if your class multiply-inherits from other classes, only ONE of those * + * classes can contain virtual functions! If you include virtual functions in the other * + * classes, the compiler will generate multiple virtual function tables, and this load/save * + * technique will fail. * + * * + * Each class hierarchy is stored in memory as a chain: first the data for the base-est * + * class, then the virtual function table pointer for this hierarchy, then the data for * + * all derived classes. If any of these derived classes multiply-inherit, the base class * + * for the multiple inheritance is stored as a separate chain following this chain. The * + * new chain will contain its own virtual function table pointer, if the multiply- * + * inherited hierarchy contains any virtual functions. Thus, the declaration * + * class A * + * class B: public A * + * class C: public B, X * + * is stored as: * + * A data * + * A's Virtual Table Pointer * + * B data * + * X data * + * [X's Virtual Table Pointer] * + * C data * + * * + * and * + * class A * + * class B: public A * + * class C: public X, B * + * is stored in memory as: * + * X data * + * [X's Virtual Table Pointer] * + * A data * + * A's Virtual Table Pointer * + * B data * + * C data * + * * + * * + * HISTORY: * + * 01/10/1995 BR : Created. * + *=============================================================================================*/ +bool Read_Object(void *ptr, int base_size, int class_size, FileClass & file, void * vtable) +{ + int size; // object size in bytes + + /* + ** Read size of this chunk. + */ + if (file.Read(&size,sizeof(size)) != sizeof(size)) { + return(false); + } + + /* + ** Error if incorrect size. + */ + if (size != class_size) { + return(false); + } + + /* + ** Read object data. + */ + if (file.Read(ptr, class_size) != (class_size)) { + return(false); + } + + /* + ** Fill in VTable. + */ + if (vtable) { + ((void **)(((char *)ptr) + base_size - 4))[0] = vtable; + } + + return(true); +} + + +/*********************************************************************************************** + * Write_Object -- reads an object from disk, in a safe way * + * * + * This routine writes an object in 2 pieces, skipping the embedded * + * virtual function table pointer. * + * * + * INPUT: * + * ptr pointer to object to write * + * class_size size of the class itself * + * file file to use for I/O * + * * + * OUTPUT: * + * true = OK, false = error * + * * + * WARNINGS: * + * This routine ASSUMES the program modules are compiled with: * + * -Vb- Always make the virtual function table ptr 2 bytes long * + * -Vt Put the virtual function table after the 1st class's data * + * * + * Also see warnings for Read_Object(). * + * * + * HISTORY: * + * 01/10/1995 BR : Created. * + *=============================================================================================*/ +bool Write_Object(void *ptr, int class_size, FileClass & file) +{ + /* + ** Save size of this chunk. + */ + if (file.Write(&class_size,sizeof(class_size)) != sizeof(class_size)) { + return(false); + } + + /* + ** Save object data. + */ + if (file.Write(ptr, class_size) != (class_size)) { + return(false); + } + + return(true); +} + + +/*************************************************************************** + * Get_Savefile_Info -- gets description, scenario #, house * + * * + * INPUT: * + * id numerical ID, for the file extension * + * buf buffer to store description in * + * scenp ptr to variable to hold scenario * + * housep ptr to variable to hold house * + * * + * OUTPUT: * + * true = OK, false = error (save-game file invalid) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/12/1995 BR : Created. * + *=========================================================================*/ +bool Get_Savefile_Info(int id, char *buf, unsigned *scenp, HousesType *housep) +{ + RawFileClass file; + char name[_MAX_FNAME+_MAX_EXT]; + unsigned long version; + char descr_buf[DESCRIP_MAX]; + + /* + ** Generate the filename to load + */ + sprintf(name, "SAVEGAME.%03d", id); + + /* + ** If the file opens OK, read the file + */ + if (file.Open(name, READ)) { + + /* + ** Read in the description, scenario #, and the house + */ + if (file.Read(descr_buf, DESCRIP_MAX) != DESCRIP_MAX) { + file.Close(); + return(false); + } + + descr_buf[strlen(descr_buf) - 2] = '\0'; // trim off CR/LF + strcpy(buf, descr_buf); + + if (file.Read(scenp, sizeof(unsigned)) != sizeof(unsigned)) { + file.Close(); + return(false); + } + + if (file.Read(housep, sizeof(HousesType)) != sizeof(HousesType)) { + file.Close(); + return(false); + } + + /* + ** Read & verify the save-game version # + */ + if (file.Read(&version,sizeof(version)) != sizeof(version)) { + file.Close(); + return(false); + } + + if (version!=SAVEGAME_VERSION) { + file.Close(); + return(false); + } + + file.Close(); + + return(true); + } + return(false); +} + + +/*************************************************************************** + * TechnoType_To_Target -- converts TechnoTypeClass to TARGET * + * * + * INPUT: * + * ptr pointer to convert * + * * + * OUTPUT: * + * target value * + * * + * WARNINGS: * + * Be certain that you only use the returned target value by passing * + * it to Target_To_TechnoType; do NOT call As_Techno, or you'll get * + * a totally invalid pointer. * + * * + * HISTORY: * + * 01/12/1995 BR : Created. * + *=========================================================================*/ +TARGET TechnoType_To_Target(TechnoTypeClass const * ptr) +{ + TARGET target; + + switch (ptr->What_Am_I()) { + case RTTI_INFANTRYTYPE: + target = Build_Target(KIND_INFANTRY, ((InfantryTypeClass const *)ptr)->Type); + break; + + case RTTI_UNITTYPE: + target = Build_Target(KIND_UNIT, ((UnitTypeClass const *)ptr)->Type); + break; + + case RTTI_AIRCRAFTTYPE: + target = Build_Target(KIND_AIRCRAFT, ((AircraftTypeClass const *)ptr)->Type); + break; + + case RTTI_BUILDINGTYPE: + target = Build_Target(KIND_BUILDING, ((BuildingTypeClass const *)ptr)->Type); + break; + + default: + target = 0; + break; + } + + return(target); +} + + +/*************************************************************************** + * Target_To_TechnoType -- converts TARGET to TechnoTypeClass * + * * + * INPUT: * + * target TARGET value to convert * + * * + * OUTPUT: * + * pointer to the TechnoTypeClass for this target value * + * * + * WARNINGS: * + * The TARGET value MUST have been generated with TechnoType_To_Target;* + * If you give this routine a target generated by an As_Target() * + * routine, it will return a bogus pointer. * + * * + * HISTORY: * + * 01/12/1995 BR : Created. * + *=========================================================================*/ +TechnoTypeClass const * Target_To_TechnoType(TARGET target) +{ + switch (Target_Kind(target)) { + case KIND_INFANTRY: + return(&InfantryTypeClass::As_Reference((InfantryType)Target_Value(target))); + + case KIND_UNIT: + return(&UnitTypeClass::As_Reference((UnitType)Target_Value(target))); + + case KIND_AIRCRAFT: + return(&AircraftTypeClass::As_Reference((AircraftType)Target_Value(target))); + + case KIND_BUILDING: + return(&BuildingTypeClass::As_Reference((StructType)Target_Value(target))); + } + return(NULL); +} + + +/*************************************************************************** + * Get_VTable -- gets the VTable pointer for the given object * + * * + * INPUT: * + * ptr pointer to check * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 01/12/1995 BR : Created. * + *=========================================================================*/ +void * Get_VTable(void *ptr, int base_size) +{ + return(((void **)(((char *)ptr) + base_size - 4))[0]); +} + + +/*************************************************************************** + * Set_VTable -- sets the VTable pointer for the given object * + * * + * INPUT: * + * ptr pointer to check * + * base_size size of base class * + * vtable value of VTable to plug in * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 01/12/1995 BR : Created. * + *=========================================================================*/ +void Set_VTable(void *ptr, int base_size, void *vtable) +{ + ((void **)(((char *)ptr) + base_size - 4))[0] = vtable; +} + + +#if 0 +/**************************************************************************** +Dump routine: prints everything about everything related to the Save/Load +process (OK, not exactly everything, but lots of stuff) +****************************************************************************/ +void Dump(void) +{ + int i,j; + FILE *fp; + char *layername[] = { + "Ground", + "Air", + "Top" + }; + + /* + ------------------------------- Open file -------------------------------- + */ + fp = fopen("dump.txt","wt"); + + /* + ------------------------------ Logic Layer ------------------------------- + */ + fprintf(fp,"--------------------- Logic Layer ---------------------\n"); + fprintf(fp,"Count: %d\n",Logic.Count()); + for (j = 0; j < Logic.Count(); j++) { + fprintf(fp, "Entry %d: %x \n",j,Logic[j]); + } + fprintf(fp,"\n"); + + /* + ------------------------------- Map Layers ------------------------------- + */ + for (i = 0; i < LAYER_COUNT; i++) { + fprintf(fp,"----------------- Map Layer %s ---------------------\n", + layername[i]); + fprintf(fp,"Count: %d\n",Map.Layer[i].Count()); + for (j = 0; j < Map.Layer[i].Count(); j++) { + fprintf(fp, "Entry %d: %x \n",j,Map.Layer[i][j]); + } + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ TeamTypes --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",TeamTypes.ActiveCount); + for (i = 0; i < TEAMTYPE_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d Name:%s\n",i,TeamTypes[i].IsActive, + TeamTypes[i].Get_Name()); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Teams --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Teams.ActiveCount); + for (i = 0; i < TEAM_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d Name:%s\n",i,Teams[i].IsActive, + Teams[i].Class->Get_Name()); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Triggers --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Triggers.ActiveCount); + for (i = 0; i < TRIGGER_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d Name:%s\n",i,Triggers[i].IsActive, + Triggers[i].Get_Name()); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Aircraft --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Aircraft.ActiveCount); + for (i = 0; i < AIRCRAFT_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d \n",i,Aircraft[i].IsActive); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Anims --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Anims.ActiveCount); + for (i = 0; i < ANIM_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d \n",i,Anims[i].IsActive); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Buildings --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Buildings.ActiveCount); + for (i = 0; i < BUILDING_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d \n",i,Buildings[i].IsActive); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Bullets --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Bullets.ActiveCount); + for (i = 0; i < BULLET_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d \n",i,Bullets[i].IsActive); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Infantry --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Infantry.ActiveCount); + for (i = 0; i < INFANTRY_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d \n",i,Infantry[i].IsActive); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Overlays --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Overlays.ActiveCount); + for (i = 0; i < OVERLAY_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d \n",i,Overlays[i].IsActive); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Reinforcements --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Reinforcements.ActiveCount); + for (i = 0; i < REINFORCEMENT_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d \n",i,Reinforcements[i].IsActive); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Smudges --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Smudges.ActiveCount); + for (i = 0; i < SMUDGE_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d \n",i,Smudges[i].IsActive); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Templates --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Templates.ActiveCount); + for (i = 0; i < TEMPLATE_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d \n",i,Templates[i].IsActive); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Terrains --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Terrains.ActiveCount); + for (i = 0; i < TERRAIN_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d \n",i,Terrains[i].IsActive); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Units --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Units.ActiveCount); + for (i = 0; i < UNIT_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d \n",i,Units[i].IsActive); + } + fprintf(fp,"\n"); + + fprintf(fp,"------------------ Factories --------------------------\n"); + fprintf(fp,"ActiveCount: %d\n",Factories.ActiveCount); + for (i = 0; i < FACTORY_MAX; i++) { + fprintf(fp,"Entry %d: Active:%d \n",i,Factories[i].IsActive); + } + fprintf(fp,"\n"); + + fclose(fp); + + /* + ---------------------------- Flush the cache ----------------------------- + */ + fp = fopen("dummy.bin","wt"); + for (i = 0; i < 100; i++) { + fprintf(fp,"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n"); + } + fclose(fp); +} +#endif + diff --git a/SCENARIO.CPP b/SCENARIO.CPP new file mode 100644 index 0000000..2588895 --- /dev/null +++ b/SCENARIO.CPP @@ -0,0 +1,727 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\scenario.cpv 2.17 16 Oct 1995 16:52:08 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SCENARIO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : August 24, 1995 [JLB] * + * * + * This module handles the scenario reading and writing. Scenario related * + * code that is executed between scenario play can also be here. * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Clear_Scenario -- Clears all data in preparation for scenario load. * + * Do_Lose -- Display losing comments. * + * Do_Restart -- Handle the restart mission process. * + * Do_Win -- Display winning congratulations. * + * Fill_In_Data -- Recreate all data that is not loaded with scenario. * + * Read_Scenario -- Reads a scenario from disk. * + * Restate_Mission -- Handles restating the mission objective. * + * Start_Scenario -- Starts the scenario. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +extern int PreserveVQAScreen; + + +/*********************************************************************************************** + * Start_Scenario -- Starts the scenario. * + * * + * This routine will start the scenario. In addition to loading the scenario data, it will * + * play the briefing and action movies. * + * * + * INPUT: root -- Pointer to the filename root for this scenario (e.g., "SCG01EA"). * + * * + * briefing -- Should the briefing be played? Normally this is true except when the * + * scenario is restarting. * + * * + * OUTPUT: Was the scenario started without error? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +bool Start_Scenario(char *root, bool briefing) +{ + + if (!Read_Scenario(root)) { + CCDebugString ("C&C95 - Failed to read scenario.\n"); + return(false); + } + CCDebugString ("C&C95 - Scenario read OK.\n"); + +#ifdef DEMO + + if (briefing) { + Play_Movie(BriefMovie); + Play_Movie(ActionMovie, TransitTheme); + } + Theme.Queue_Song(THEME_AOI); + +#else + + /* + ** Install some hacks around the movie playing to account for the choose- + ** sides introduction. We don't want an intro movie on scenario 1, and + ** we don't want a briefing movie on GDI scenario 1. + */ + if (Scenario < 20 && (!Special.IsJurassic || !AreThingiesEnabled)) { + if (Scenario != 1 || Whom == HOUSE_GOOD) { + Play_Movie(IntroMovie); + } + + if ((Scenario > 1 || Whom == HOUSE_BAD) && briefing) { + PreserveVQAScreen = (Scenario == 1); + Play_Movie(BriefMovie); + } + Play_Movie(ActionMovie, TransitTheme); + if (TransitTheme == THEME_NONE) { + Theme.Queue_Song(THEME_AOI); + } + } else { + Play_Movie(BriefMovie); + Play_Movie(ActionMovie, TransitTheme); + +#ifdef NEWMENU + + char buffer[25]; + sprintf(buffer, "%s.VQA", BriefMovie); + CCFileClass file(buffer); + + if (GameToPlay == GAME_NORMAL && !file.Is_Available()) { + VisiblePage.Clear(); + Set_Palette(GamePalette); +// Show_Mouse(); + /* + ** Show the mission briefing. Pretend we are inside the main loop so the palette + ** will be correct on the textured buttons. + */ + bool oldinmain = InMainLoop; + InMainLoop = true; + Restate_Mission(ScenarioName, TXT_OK, TXT_NONE); + InMainLoop = oldinmain; +// Hide_Mouse(); + if (TransitTheme == THEME_NONE) { + Theme.Queue_Song(THEME_AOI); + } + } + +#endif + } +#endif + + /* + ** Set the options values, since the palette has been initialized by Read_Scenario + */ + CCDebugString ("C&C95 - About to call Options.Set.\n"); + Options.Set(); + CCDebugString ("C&C95 - About to return from Start_Scenario.\n"); + return(true); +} + + +/*********************************************************************************************** + * Read_Scenario -- Reads a scenario from disk. * + * * + * This will read a scenario from disk. Use this to begin a scenario. * + * It doesn't perform any rendering, it merely sets up the system * + * with the proper data. Setting of the right game state will start * + * the scenario running. * + * * + * INPUT: root -- Scenario root filename * + * * + * OUTPUT: none * + * * + * WARNINGS: You must clear out the system variables before calling * + * this function. Use the Clear_Scenario() function. * + * It is assumed that Scenario is set to the current scenario number. * + * * + * HISTORY: * + * 07/22/1991 : Created. * + * 02/03/1992 JLB : Uses house identification. * + *=============================================================================================*/ +bool Read_Scenario(char *root) +{ + CCDebugString ("C&C95 - In Read_Scenario.\n"); + Clear_Scenario(); + ScenarioInit++; + if (Read_Scenario_Ini(root)) { + + Fill_In_Data(); + + + /* + ** SPECIAL CASE: + ** Clear out the tutor flags for scenarios one and two. This is designed + ** so that tutorial message will reappear in scenario two. + */ + if (Scenario < 5) { + TutorFlags[0] = 0L; + TutorFlags[1] = 0L; + } + + } else { + + Fade_Palette_To(GamePalette, FADE_PALETTE_FAST, Call_Back); + Show_Mouse(); + CCMessageBox().Process(TXT_UNABLE_READ_SCENARIO); + Hide_Mouse(); + return(false); + } + ScenarioInit--; + CCDebugString ("C&C95 - Leaving Read_Scenario.\n"); + return(true); +} + + +/*********************************************************************************************** + * Fill_In_Data -- Recreate all data that is not loaded with scenario. * + * * + * This routine is called after the INI file for the scenario has been processed. It will * + * infer the game state from the scenario INI data. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +void Fill_In_Data(void) +{ + /* + ** The basic scenario data load does not contain the full set of + ** game data. We now must fill in the missing pieces. + */ + ScenarioInit++; + + for (int index = 0; index < Buildings.Count(); index++) { + Buildings.Ptr(index)->Update_Buildables(); + } + + Map.Flag_To_Redraw(true); + + /* + ** Bring up the score display on the radar map when starting a multiplayer + ** game. + */ + if (GameToPlay != GAME_NORMAL) { + Map.Player_Names(1); + } + + ScenarioInit--; +} + + +/*********************************************************************************************** + * Clear_Scenario -- Clears all data in preparation for scenario load. * + * * + * This routine will clear out all data specific to a scenario in * + * preparation for a subsequent scenario data load. This will free * + * all units, animations, and icon maps. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/22/1991 : Created. * + * 03/21/1992 JLB : Changed buffer allocations, so changes memset code. * + * 07/13/1995 JLB : End count down moved here. * + *=============================================================================================*/ +void Clear_Scenario(void) +{ + EndCountDown = TICKS_PER_SECOND * 30; + CrateCount = 0; + CrateTimer = 0; + CrateMaker = false; + + /* + ** Call everyone's Init routine, except the Map's; for the Map, only call + ** MapClass::Init, which clears the Cell array. The Display::Init requires + ** a Theater argument, and the theater is not known at this point; also, it + ** would reload MixFiles, which isn't desired. Display::Read_INI calls its + ** own Init, which will Init the entire Map hierarchy. + */ + Map.Init_Clear(); + Score.Init(); + Logic.Init(); + + HouseClass::Init(); + ObjectClass::Init(); + TeamTypeClass::Init(); + TeamClass::Init(); + TriggerClass::Init(); + AircraftClass::Init(); + AnimClass::Init(); + BuildingClass::Init(); + BulletClass::Init(); + InfantryClass::Init(); + OverlayClass::Init(); + SmudgeClass::Init(); + TemplateClass::Init(); + TerrainClass::Init(); + UnitClass::Init(); + + FactoryClass::Init(); + + Base.Init(); + + CurrentObject.Clear(); + + Invalidate_Cached_Icons(); +} + + +/*********************************************************************************************** + * Do_Win -- Display winning congratulations. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + * 01/01/1995 JLB : Carries money forward into next scenario. * + *=============================================================================================*/ +void Do_Win(void) +{ + Map.Set_Default_Mouse(MOUSE_NORMAL); + Hide_Mouse(); + + /* + ** If this is a multiplayer game, clear the game's name so we won't respond + ** to game queries any more (in Call_Back) + */ + if (GameToPlay != GAME_NORMAL) { + MPlayerGameName[0] = 0; + } + + /* + ** Determine a cosmetic center point for the text. + */ + int x = Map.TacPixelX + (Lepton_To_Pixel(Map.TacLeptonWidth)/2); + int y = Map.TacPixelY + (Lepton_To_Pixel(Map.TacLeptonHeight)/2) -32; + + /* + ** Announce win to player. + */ + Set_Logic_Page(SeenBuff); +#if !(GERMAN | FRENCH) + Fancy_Text_Print(TXT_MISSION, x, y, WHITE, TBLACK, TPF_CENTER|TPF_VCR); +#endif + Fancy_Text_Print(TXT_SCENARIO_WON, x, y+30, WHITE, TBLACK, TPF_CENTER|TPF_VCR); + CountDownTimer.Set(TIMER_SECOND * 3); + Stop_Speaking(); + Speak(VOX_ACCOMPLISHED); + while (CountDownTimer.Time() || Is_Speaking()) { + Call_Back(); + } + + /* + ** Stop here if this is a multiplayer game. + */ + if (GameToPlay != GAME_NORMAL) { + if (!PlaybackGame) { + MPlayerGamesPlayed++; + Multi_Score_Presentation(); + MPlayerCurGame++; + if (MPlayerCurGame >= MAX_MULTI_GAMES) { + MPlayerCurGame = MAX_MULTI_GAMES - 1; + } + } + GameActive = 0; + Show_Mouse(); + return; + } + + /* + ** Play the winning movie and then start the next scenario. + */ + if (RequiredCD != -2) { + if (Scenario >= 20 && Scenario <60 && GameToPlay == GAME_NORMAL) { + RequiredCD = 2; + } else { + if (Scenario >=60){ + RequiredCD = -1; + }else{ + if (PlayerPtr->Class->House == HOUSE_GOOD) { + RequiredCD = 0; + } else { + RequiredCD = 1; + } + } + } + } + +#ifndef DEMO + Play_Movie(WinMovie); +#endif + + Keyboard::Clear(); + + /* + ** Do the ending screens only if not playing back a recorded game. + */ + if (!PlaybackGame) { + +#ifdef DEMO + + switch (Scenario) { + case 1: + Score.Presentation(); + Scenario = 10; + break; + + case 10: + Score.Presentation(); + Scenario = 6; + break; + + default: + Score.Presentation(); + GDI_Ending(); + GameActive = false; + Show_Mouse(); + return; +// Prog_End(); +// exit(0); +// break; + } + +#else + +#ifdef NEWMENU + if (Scenario >= 20) { + Keyboard::Clear(); + Score.Presentation(); + GameActive = false; + Show_Mouse(); + return; + } +#endif + + if (PlayerPtr->Class->House == HOUSE_BAD && Scenario == 13) { + Nod_Ending(); + //Prog_End(); + //exit(0); + SeenBuff.Clear(); + Show_Mouse(); + GameActive = false; + return; + } + if (PlayerPtr->Class->House == HOUSE_GOOD && Scenario == 15) { + GDI_Ending(); + //Prog_End(); + //exit(0); + SeenBuff.Clear(); + Show_Mouse(); + GameActive = false; + return; + } + + if ( (Special.IsJurassic && AreThingiesEnabled) && Scenario == 5) { + Prog_End(); + exit(0); + } + + if (!Special.IsJurassic || !AreThingiesEnabled) { + Keyboard::Clear(); + InterpolationPaletteChanged = TRUE; + InterpolationPalette = Palette; + Score.Presentation(); + + /* + ** Skip scenario #7 if the airfield was blown up. + */ + if (Scenario == 6 && PlayerPtr->Class->House == HOUSE_GOOD && SabotagedType == STRUCT_AIRSTRIP) { + Scenario++; + } + + Map_Selection(); + } + Scenario++; +#endif + Keyboard::Clear(); + } + + CarryOverMoney = PlayerPtr->Credits; + + int pieces = PlayerPtr->NukePieces; + + /* + ** Generate a new scenario filename + */ + Set_Scenario_Name(ScenarioName, Scenario, ScenPlayer, ScenDir, ScenVar); + Start_Scenario(ScenarioName); + + PlayerPtr->NukePieces = pieces; + + /* + ** Destroy the building that was sabotaged in the previous scenario. This only + ** applies to GDI mission #7. + */ + if (SabotagedType != STRUCT_NONE && Scenario == 7 && PlayerPtr->Class->House == HOUSE_GOOD) { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + if (building && !building->IsInLimbo && building->House != PlayerPtr && building->Class->Type == SabotagedType) { + building->Limbo(); + delete building; + break; + } + } + + /* + ** Remove the building from the prebuild list. + */ + for (index = 0; index < Base.Nodes.Count(); index++) { + BaseNodeClass * node = Base.Get_Node(index); + + if (node && node->Type == SabotagedType) { + Base.Nodes.Delete(index); + break; + } + } + } + SabotagedType = STRUCT_NONE; + + Map.Render(); + Fade_Palette_To(GamePalette, FADE_PALETTE_FAST, Call_Back); + Show_Mouse(); +} + + +/*********************************************************************************************** + * Do_Lose -- Display losing comments. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + *=============================================================================================*/ +void Do_Lose(void) +{ + Map.Set_Default_Mouse(MOUSE_NORMAL); + Hide_Mouse(); + + /* + ** If this is a multiplayer game, clear the game's name so we won't respond + ** to game queries any more (in Call_Back) + */ + if (GameToPlay != GAME_NORMAL) { + MPlayerGameName[0] = 0; + } + + /* + ** Determine a cosmetic center point for the text. + */ + int x = Map.TacPixelX + (Lepton_To_Pixel(Map.TacLeptonWidth)/2); + int y = Map.TacPixelY + (Lepton_To_Pixel(Map.TacLeptonHeight)/2) -32; + + /* + ** Announce win to player. + */ + Set_Logic_Page(SeenBuff); + Fancy_Text_Print(TXT_MISSION, x, y, WHITE, TBLACK, TPF_CENTER|TPF_VCR); + Fancy_Text_Print(TXT_SCENARIO_LOST, x, y+30, WHITE, TBLACK, TPF_CENTER|TPF_VCR); + CountDownTimer.Set(TIMER_SECOND * 3); + Stop_Speaking(); + Speak(VOX_FAIL); + while (CountDownTimer.Time() || Is_Speaking()) { + Call_Back(); + } + +#ifdef OBSOLETE + if (Debug_Play_Map) { + Go_Editor(true); + Show_Mouse(); + return; + } +#endif + + /* + ** Stop here if this is a multiplayer game. + */ + if (GameToPlay != GAME_NORMAL) { + if (!PlaybackGame) { + MPlayerGamesPlayed++; + Multi_Score_Presentation(); + MPlayerCurGame++; + if (MPlayerCurGame >= MAX_MULTI_GAMES) { + MPlayerCurGame = MAX_MULTI_GAMES - 1; + } + } + GameActive = 0; + Show_Mouse(); + return; + } + + Play_Movie(LoseMovie); + + /* + ** Start same scenario again + */ + Set_Palette(GamePalette); + Show_Mouse(); + if (!PlaybackGame && !CCMessageBox().Process(TXT_TO_REPLAY, TXT_YES, TXT_NO)) { + Hide_Mouse(); + Keyboard::Clear(); + Start_Scenario(ScenarioName, false); + Map.Render(); + } else { + Hide_Mouse(); + GameActive = 0; + } + + Fade_Palette_To(GamePalette, FADE_PALETTE_FAST, Call_Back); + Show_Mouse(); +} + + +/*********************************************************************************************** + * Do_Restart -- Handle the restart mission process. * + * * + * This routine is called in the main game loop when the mission must be restarted. This * + * routine will throw away the current game and reload the appropriate mission. The * + * game will "resume" at the start of the mission. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/24/1995 JLB : Created. * + *=============================================================================================*/ +void Do_Restart(void) +{ + bool hidden = Get_Mouse_State(); + + if (hidden) Show_Mouse(); + CCMessageBox().Process(TXT_RESTARTING, TXT_NONE); + Map.Set_Default_Mouse(MOUSE_NORMAL); + Keyboard::Clear(); + Start_Scenario(ScenarioName, false); + if (hidden) Hide_Mouse(); + Keyboard::Clear(); + Map.Render(); +} + + +/*********************************************************************************************** + * Restate_Mission -- Handles restating the mission objective. * + * * + * This routine will display the mission objective (as text). It will also give the * + * option to redisplay the mission briefing video. * + * * + * INPUT: name -- The scenario name. This is the unique identifier for the scenario * + * briefing text as it appears in the "MISSION.INI" file. * + * * + * OUTPUT: Returns the response from the dialog. This will either be 1 if the video was * + * requested, or 0 if the return to game options button was selected. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/23/1995 JLB : Created. * + * 08/06/1995 JLB : Uses preloaded briefing text. * + *=============================================================================================*/ +bool Restate_Mission(char const * name, int button1, int button2) +{ + if (name) { +#ifdef JAPANESE + char fname[14]; + strcpy(fname, name); + strcat(fname,".CPS"); + + if(CCFileClass(fname).Is_Available()) { + CCMessageBox box(TXT_NONE, true); + return(box.Process(fname, button1, button2)); + } +#else + /* + ** Make sure that if there is no briefing movie, that the briefing text is + ** the only option available. + */ + bool brief = true; +#ifdef NEWMENU + char buffer[25]; + char buffer1[25]; + sprintf(buffer, "%s.VQA", BriefMovie); + sprintf(buffer1, "%s.VQA", ActionMovie); + CCFileClass file1(buffer); + CCFileClass file2(buffer1); + if (!file1.Is_Available() && !file2.Is_Available()) { + button1 = TXT_OK; + button2 = TXT_NONE; + brief = false; + } +#endif + + /* + ** If mission object text was found, then display it. + */ + if (strlen(BriefingText)) { + static char _buff[512]; + + strcpy(_buff, BriefingText); +// strcpy(_ShapeBuffer, BriefingText); + + bool hidden = Get_Mouse_State(); + if (hidden) Show_Mouse(); + + if (CCMessageBox(TXT_OBJECTIVE).Process(_buff, button1, button2)) { + if (hidden) Hide_Mouse(); + return(true); + } + if (hidden) Hide_Mouse(); + if (!brief) return(true); + return(false); + } +#endif + } + return(false); +} diff --git a/SCORE.CPP b/SCORE.CPP new file mode 100644 index 0000000..e10c2e0 --- /dev/null +++ b/SCORE.CPP @@ -0,0 +1,2316 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\score.cpv 2.17 16 Oct 1995 16:49:54 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SCORE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 19, 1994 * + * * + * Last Update : May 3, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Call_Back_Delay -- Combines Call_Back() and Delay() functions * + * Draw_Bar_Graphs -- Draw "Casualties" bar graphs * + * Draw_InfantryMan -- Draw one guy in score screen, update animation * + * Draw_Infantrymen -- Draw all the guys on the score screen * + * New_Infantry_Anim -- Start up a new animation for one of the infantrymen * + * ScoreClass::Count_Up_Print -- Prints a number (up to its max) into a string, cleanly * + * ScoreClass::Delay -- Pauses waiting for keypress. * + * ScoreClass::DO_GDI_GRAPH -- Show # of people or buildings killed on GDI score screen * + * ScoreClass::Presentation -- Main routine to display score screen. * + * ScoreClass::Print_Graph_Title -- Prints title on score screen. * + * ScoreClass::Print_Minutes -- Print out hours/minutes up to max * + * ScoreClass::Pulse_Bar_Graph -- Pulses the bargraph color. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "textblit.h" +#define SCORETEXT_X 184 +#define SCORETEXT_Y 8 +#define CASUALTY_Y 88 +#define BUILDING_X 256 +#define BUILDING_Y 128 +#define BARGRAPH_X 266 +#define MAX_BAR_X 318 // max possible is 319 because of bar's right shadow +#define SIZEGBAR 119 +#define HALLFAME_X 19 +#define HALLFAME_Y 120 + +#define MULTISCOREX 30 + +#define TEDIT_FAME 1 +#define NUMINFANTRYMEN 15 +#define NUMFAMENAMES 7 +#define MAX_FAMENAME_LENGTH 12 + +struct InfantryAnim { + int xpos; + int ypos; + void const *shapefile; + void const *remap; + int anim; + int stage; + char delay; + InfantryTypeClass const *Class; +} InfantryMan[NUMINFANTRYMEN]; +void Draw_InfantryMen(void); +void Draw_InfantryMan(int index); +void New_Infantry_Anim(int index, int anim); +void Draw_Bar_Graphs(int i, int gkilled, int nkilled, int ckilled); +void Animate_Cursor(int pos, int ypos); +void Animate_Score_Objs(void); +void Cycle_Wait_Click(void); +int ScorePass; + +void const * Beepy6; +int ControlQ; // cheat key to skip past score/mapsel screens +bool StillUpdating; + +GraphicBufferClass *PseudoSeenBuff; +GraphicBufferClass *TextPrintBuffer; + +#ifdef WRITE_LBM +PUBLIC bool CCWrite_LBM_File(CCFileClass &lbmhandle, BufferClass& buff, short bitplanes, unsigned char *palette); +#endif + +unsigned char RemapCiv[256]={ + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0xD0,0x18,0x19,0xD1,0xD2,0xD3,0xD4,0x1E,0xD5, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, + 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, + 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F, + 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, + 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0xD6,0xD7,0xD8,0xD9,0x7E,0xDA, + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xDB,0xDC,0xDD,0xDE,0xDB,0xDC,0xDD,0xDE,0xDF, + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}; + +unsigned char const ScoreRemapGrey[256] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 0..15 + 16, 17, 18, 19, 20,176, 22,208, 24, 25,209,210,211,212, 30,213, // 16..31 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 32..47 + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 48..63 + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 64..79 + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // 80..95 + 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,118,119,120,121,214,215,216,217,149,218, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,108,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,219,220,221,222,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const ScoreRemapYellow[256] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 0..15 + 16, 17, 18, 19, 20,176, 22,208, 24, 25,209,210,211,212, 30,213, // 16..31 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 32..47 + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 48..63 + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 64..79 + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // 80..95 + 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,118,119,120,121,214,215,216,217,149,218, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,108,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,219,220,221,222,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const ScoreRemapBldg[256]={ + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, + 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, + 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, + 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F, + 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, + 0x70,0x71,0xCE,0xC5,0x49,0x48,0x47,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F, + 0x80,0x81,0xB8,0x83,0x7C,0x7A,0x76,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, + 0x74,0x91,0x92,0x93,0x94,0x95,0xB5,0x97,0x98,0xCF,0x4B,0x7F,0x9C,0x9D,0x9E,0x9F, + 0xA0,0xA1,0xA2,0x83,0x79,0xA5,0xA6,0xA7,0x43,0x99,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xBB,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF +}; +unsigned char const ScoreRemapFBall[256]={ + 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, + 0xB0,0xE5,0x82,0xE4,0xE3,0xE2,0xB1,0xD0,0xE1,0xE0,0xD1,0xD2,0xD3,0xD4,0xDF,0xD5, + 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0xE6,0xE7,0xA0,0xE8,0x2E,0x2F, + 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, + 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, + 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F, + 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F, + 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F, + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF +}; + +TextBlitClass BlitList; + + +char *ScreenNames[2]={"S-GDIIN2.WSA","SCRSCN1.WSA"}; + +//extern short StreamLowImpact; + +struct Fame { + char name[MAX_FAMENAME_LENGTH]; + int score; + int level; +}; + +ScoreAnimClass *ScoreObjs[MAXSCOREOBJS]; + + +ScoreAnimClass::ScoreAnimClass(int x, int y, void const * data) +{ + BlitList.Add (x*2, y*2, x*2, y*2, 2* String_Pixel_Width ( (char*)data ) , 16); + XPos = x; + YPos = y; + Timer.Set(0); + Timer.Start(); + DataPtr = data; +} + + +ScoreTimeClass::ScoreTimeClass(int xpos, int ypos, void const * data, int max, int timer) : + ScoreAnimClass(xpos, ypos, data) +{ + Stage = 0; + MaxStage = max; + TimerReset = timer; +} + +void ScoreTimeClass::Update(void) +{ + GraphicViewPortClass *oldpage; + if (!Timer.Time()) { + Timer.Set(TimerReset); + if (++Stage >= MaxStage) Stage = 0; + oldpage = LogicPage; + Set_Logic_Page(PseudoSeenBuff); + CC_Draw_Shape(DataPtr, Stage, XPos, YPos, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + Set_Logic_Page(oldpage); + } +} + +ScoreCredsClass::ScoreCredsClass(int xpos, int ypos, void const * data, int max, int timer) : + ScoreAnimClass(xpos, ypos, data) +{ + Stage = 0; + MaxStage = max; + TimerReset = timer; + Clock1 = MixFileClass::Retrieve("CLOCK1.AUD"); + CashTurn = MixFileClass::Retrieve("CASHTURN.AUD"); +} + + +void ScoreCredsClass::Update(void) +{ + GraphicViewPortClass *oldpage; + if (!Timer.Time()) { + Timer.Set(TimerReset); + if (++Stage >= MaxStage) Stage = 0; + oldpage = LogicPage; + Set_Logic_Page(PseudoSeenBuff); + if (Stage <22) { + Play_Sample(Clock1, 255, Options.Normalize_Sound(70)); + } else { + if (Stage==24) { + Play_Sample(CashTurn, 255, Options.Normalize_Sound(70)); + } + } + CC_Draw_Shape(DataPtr,Stage, XPos, YPos, WINDOW_MAIN,SHAPE_WIN_REL, 0, 0); + Set_Logic_Page(oldpage); + } +} + + +ScorePrintClass::ScorePrintClass(int string, int xpos, int ypos, void const * palette, int background) : + ScoreAnimClass(xpos, ypos, Text_String(string)) +{ + Background = background; + PrimaryPalette = palette; + Stage = 0; +} + + +ScorePrintClass::ScorePrintClass(void const * string, int xpos, int ypos, void const * palette, int background) : + ScoreAnimClass(xpos, ypos, string) +{ + Background = background; + PrimaryPalette = palette; + Stage = 0; +} + + +void ScorePrintClass::Update(void) +{ + static char localstr[2]={0,0}; + static char _whitepal[]={0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F}; + + if (Stage && (((char *)DataPtr)[Stage-1]==0) ) { + for (int i = 0; i < MAXSCOREOBJS; i++) { + if (ScoreObjs[i] == this) { + ScoreObjs[i] = 0; + } + } + BlitList.Add (XPos*2, YPos*2, XPos*2, YPos*2,(Stage*6)+14, 8*2); + delete this; + return; + } + + StillUpdating = true; + + if (!Timer.Time()) { + Timer.Set(1); + + int pos = XPos+(Stage*6); + if (Stage) { + + localstr[0]=((char *)DataPtr)[Stage-1]; + + /* + ** Clear out the white letter overlay + */ + static char const _blackpal[]={BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK}; + Set_Font_Palette(_blackpal); + TextPrintBuffer->Print(localstr, 2*(pos-6),2*(YPos-1), TBLACK, TBLACK); + TextPrintBuffer->Print(localstr, 2*(pos-6),2*(YPos+1), TBLACK, TBLACK); + TextPrintBuffer->Print(localstr, 2*(pos-6+1),2*(YPos), TBLACK, TBLACK); + + Set_Font_Palette(PrimaryPalette); + TextPrintBuffer->Print(localstr, 2*(pos-6),2*YPos, TBLACK, TBLACK); + } + if (((char *)DataPtr)[Stage]) { + localstr[0]=((char *)DataPtr)[Stage]; + Set_Font_Palette(_whitepal); + TextPrintBuffer->Print(localstr, pos*2, 2*(YPos-1), TBLACK, TBLACK); + TextPrintBuffer->Print(localstr, pos*2, 2*(YPos+1), TBLACK, TBLACK); + TextPrintBuffer->Print(localstr, (pos+1)*2,2*YPos , TBLACK, TBLACK); + } + Stage++; + } +} + + + + + + + + + + + +MultiStagePrintClass::MultiStagePrintClass(int string, int xpos, int ypos, void const * palette, int background) : + ScoreAnimClass(xpos, ypos, Text_String(string)) +{ + Background = background; + PrimaryPalette = palette; + Stage = 0; +} + + +MultiStagePrintClass::MultiStagePrintClass(void const * string, int xpos, int ypos, void const * palette, int background) : + ScoreAnimClass(xpos, ypos, string) +{ + Background = background; + PrimaryPalette = palette; + Stage = 0; +} + + +void MultiStagePrintClass::Update(void) +{ + static char localstr[2]={0,0}; + static char _whitepal[]={0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F}; + + if (Stage && (((char *)DataPtr)[Stage-1]==0) ) { + for (int i = 0; i < MAXSCOREOBJS; i++) { + if (ScoreObjs[i] == this) { + ScoreObjs[i] = 0; + } + } + BlitList.Add (XPos*2, YPos*2, XPos*2, YPos*2,(Stage*6)+14, 8*2); + delete this; + return; + } + + StillUpdating = true; + + if (!Timer.Time()) { + Timer.Set(1); + + /* + ** Do 10 stages at once + */ + for (int wibble = 0 ; wibble < 10 ; wibble ++){ + + int pos = XPos+(Stage*6); + if (Stage) { + + localstr[0]=((char *)DataPtr)[Stage-1]; + + /* + ** Clear out the white letter overlay + */ + static char const _blackpal[]={BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK,BLACK}; + Set_Font_Palette(_blackpal); + TextPrintBuffer->Print(localstr, 2*(pos-6),2*(YPos-1), TBLACK, TBLACK); + TextPrintBuffer->Print(localstr, 2*(pos-6),2*(YPos+1), TBLACK, TBLACK); + TextPrintBuffer->Print(localstr, 2*(pos-6+1),2*(YPos), TBLACK, TBLACK); + + Set_Font_Palette(PrimaryPalette); + TextPrintBuffer->Print(localstr, 2*(pos-6),2*YPos, TBLACK, TBLACK); + } + if (((char *)DataPtr)[Stage]) { + localstr[0]=((char *)DataPtr)[Stage]; + Set_Font_Palette(_whitepal); + TextPrintBuffer->Print(localstr, pos*2, 2*(YPos-1), TBLACK, TBLACK); + TextPrintBuffer->Print(localstr, pos*2, 2*(YPos+1), TBLACK, TBLACK); + TextPrintBuffer->Print(localstr, (pos+1)*2,2*YPos , TBLACK, TBLACK); + } + Stage++; + + if ( ( (char *) DataPtr) [Stage-1] == 0 ) break; + } + } +} + + + + + + +ScoreScaleClass::ScoreScaleClass(void const * string, int xpos, int ypos, char const palette[]) : + ScoreAnimClass(xpos, ypos, string) +{ + Palette = &palette[0]; + Stage = 5; +} + + +void ScoreScaleClass::Update(void) +{ + static int _destx[]={0,80,107,134,180,228}; + static int _destw[]={6,20, 30, 40, 60, 80}; + + /* + ** Restore the background for the scaled-up letter + */ + if (!Timer.Time()) { + Timer.Set(1); + if (Stage != 5) { + TextPrintBuffer->Blit(HidPage, _destx[Stage+1]*2, YPos*2, _destx[Stage+1]*2, YPos*2, _destw[Stage+1]*2, _destw[Stage+1]*2); + //SysMemPage.Blit(*PseudoSeenBuff, _destx[Stage+1], YPos, _destx[Stage+1], YPos, _destw[Stage+1], _destw[Stage+1]); + } + if (Stage) { + Set_Font_Palette(Palette); + TextPrintBuffer->Fill_Rect(0,0, 7*2,7*2, TBLACK); + TextPrintBuffer->Print((char *)DataPtr, 0,0, TBLACK, TBLACK); + TextPrintBuffer->Scale(HidPage, 0,0, _destx[Stage]*2, YPos*2, 5*2,5*2, _destw[Stage]*2, _destw[Stage]*2, true); + + //SysMemPage.Fill_Rect(0,0, 7,7, TBLACK); + //SysMemPage.Print((char *)DataPtr, 0,0, TBLACK, TBLACK); + //SysMemPage.Scale(*PseudoSeenBuff, 0,0, _destx[Stage], YPos, 5,5, _destw[Stage], _destw[Stage], true); + Stage--; + } else { + Set_Font_Palette(Palette); + for (int i = 0; i < MAXSCOREOBJS; i++) { + if (ScoreObjs[i]==this) ScoreObjs[i] = 0; + } + TextPrintBuffer->Print((char *)DataPtr, XPos*2,YPos*2, TBLACK, TBLACK); + //TextPrintBuffer->Blit(HidPage, XPos*2, YPos*2, XPos*2, YPos*2,2*6,2*6); + //BlitList.Add (XPos, YPos, XPos, YPos, 6,6); + + //SysMemPage.Print((char *)DataPtr, XPos,YPos, TBLACK, TBLACK); + //SysMemPage.Blit(*PseudoSeenBuff, XPos,YPos, XPos, YPos, 6,6); + delete this; + return; + } + } +} + +int Alloc_Object(ScoreAnimClass *obj) +{ + int i,ret; + + for (i = ret = 0; i < MAXSCOREOBJS; i++) { + if (!ScoreObjs[i]) { + ScoreObjs[i] = obj; + ret = i; + break; + } + } + return(ret); +} + + + + + +TextBlitClass::TextBlitClass (void) +{ + Clear(); +} + + + +void TextBlitClass::Add (int x, int y, int dx, int dy, int w, int h) +{ + if ( Count < MAX_ENTRIES ){ + + BlitListo [Count].SourceX = x; + BlitListo [Count].SourceY = y; + BlitListo [Count].DestX = dx; + BlitListo [Count].DestY = dy; + BlitListo [Count].Width = w; + BlitListo [Count].Height = h; + + Count++; + } +} + + +void TextBlitClass::Clear(void) +{ + Count = 0; +} + + + + +void TextBlitClass::Update(void) +{ + if (TextPrintBuffer){ + + if (HidPage.Lock()){ + + for (int i=0 ; iBlit (HidPage, BlitListo[i].SourceX, + BlitListo[i].SourceY, + BlitListo[i].DestX, + BlitListo[i].DestY, + BlitListo[i].Width, + BlitListo[i].Height, + true); + } + + HidPage.Unlock(); + } + } +} + + + +void Disable_Uncompressed_Shapes (void); +void Enable_Uncompressed_Shapes (void); + +/*********************************************************************************************** + * ScoreClass::Presentation -- Main routine to display score screen. * + * * + * This is the main routine that displays the score screen graphics. * + * It gets called at the end of each scenario and is used to present * + * the results and a rating of the player's battle. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 : Created. * + *=============================================================================================*/ +void ScoreClass::Presentation(void) +{ + //static char const _redpal[]={0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x21,0x2F}; + static char const _redpal[]={0x20,0x22,0x24,0x26,0x28,0x28,0x28,0x28,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x21,0x2F}; + static char const _greenpal[]={0x10,0x12,0x14,0x16,0x18,0x18,0x18,0x18,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x10,0x1F}; + static char const _bluepal[]={0x60,0x62,0x64,0x66,0x68,0x68,0x68,0x68,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x61,0x6F}; + //static char const _bluepal[]={0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x61,0x6F}; + static char const _yellowpal[]={0x0,0x0,0xEC,0x0,0xEB,0x0,0xEA,0x0,0xE9,0x0,0x0,0x0,0x0,0x0,0xED,0x0}; + static int const _casuax[2]={144,146}; + static int const _casuay[2]={ 78, 90}; + static int const _gditxx[2]={150,224}; + static int const _gditxy[2]={ 90, 90}; + static int const _nodtxx[2]={150,224}; + static int const _nodtxy[2]={102,102}; +// static int _bargrx[2]={297,SCORETEXT_X+64}; +// static int _bargry[2]={ 90,CASUALTY_Y + 2}; + static int const _bldggy[2]={138,128}; + static int const _bldgny[2]={150,140}; + +// int gdikilled, nodkilled, civkilled, max, i, k, shapenum; + int i; + int max; + void const * yellowptr; + void const * redptr; + CCFileClass file(FAME_FILE_NAME); + struct Fame hallfame[NUMFAMENAMES]; + void *anim, *oldfont; + int oldfontxspacing = FontXSpacing; + int house = PlayerPtr->Class->House; // 0 or 1 + char inter_pal[15]; + + /* + ** Choose an appropriate palette file for the interpolation + */ + if (house == HOUSE_GOOD){ + sprintf(inter_pal,"SCORPAL1.PAL"); + }else{ + sprintf(inter_pal,"SNODPAL1.PAL"); + } + + + if (Special.IsJurassic && AreThingiesEnabled) return; + + PseudoSeenBuff = new GraphicBufferClass(320,200,(void*)NULL); + TextPrintBuffer = new GraphicBufferClass(SeenBuff.Get_Width(), SeenBuff.Get_Height(), (void*)NULL); + TextPrintBuffer->Clear(); + BlitList.Clear(); + Disable_Uncompressed_Shapes (); + + ControlQ = 0; + FontXSpacing = 0; + Map.Override_Mouse_Shape(MOUSE_NORMAL); + Theme.Queue_Song(THEME_WIN1); + + VisiblePage.Clear(); + PseudoSeenBuff->Clear(); + SysMemPage.Clear(); + WWMouse->Erase_Mouse(&HidPage, TRUE); + HiddenPage.Clear(); + Set_Palette(BlackPalette); + + Set_Logic_Page(SysMemPage); + + void const * country4 = MixFileClass::Retrieve("COUNTRY4.AUD"); + void const * sfx4 = MixFileClass::Retrieve("SFX4.AUD"); + Beepy6 = MixFileClass::Retrieve("BEEPY6.AUD"); + + /* + ** Load the background for the score screen + */ + anim = Open_Animation(ScreenNames[house],NULL,0L,(WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE),Palette); + + unsigned minutes = (unsigned)((ElapsedTime / (long)TIMER_MINUTE))+1; + + /* + ** Determine leadership rating. + */ + unsigned leadership = 0; + for (int index = 0; index < Logic.Count(); index++) { + ObjectClass * object = Logic[index]; + if (object->Owner() == house) { + leadership++; + } + } + + HouseClass *houses[3]; + for (index = 0; index < 3; index++) { + houses[index] =(HouseClass::As_Pointer((HousesType)(HOUSE_GOOD+index))); + } + + GKilled = (HouseClass::As_Pointer(HOUSE_GOOD))->UnitsLost; + NKilled = (HouseClass::As_Pointer(HOUSE_BAD))->UnitsLost; + CKilled = (HouseClass::As_Pointer(HOUSE_NEUTRAL))->UnitsLost; + GBKilled = (HouseClass::As_Pointer(HOUSE_GOOD))->BuildingsLost; + NBKilled = (HouseClass::As_Pointer(HOUSE_BAD))->BuildingsLost; + CBKilled = (HouseClass::As_Pointer(HOUSE_NEUTRAL))->BuildingsLost; + + /* + ** New - ST 6/12/96 2:40PM + */ + GHarvested = (HouseClass::As_Pointer(HOUSE_GOOD))->HarvestedCredits; + NHarvested = (HouseClass::As_Pointer(HOUSE_BAD))->HarvestedCredits; + + if (!leadership) leadership++; + leadership = Cardinal_To_Fixed(GKilled+GBKilled+leadership, leadership); + leadership = Fixed_To_Cardinal(100, leadership); + if (leadership > 100) leadership = 100; + + /* + ** Determine efficiency rating. + */ + int gharv = GHarvested; + int init = PlayerPtr->InitialCredits; + int cred = PlayerPtr->Available_Money(); + + unsigned efficiency = Cardinal_To_Fixed( (house == HOUSE_GOOD ? GHarvested : NHarvested) + (unsigned)PlayerPtr->InitialCredits+1, (unsigned)PlayerPtr->Available_Money()+1); + if (!efficiency) efficiency++; + efficiency = Fixed_To_Cardinal(100, efficiency); + + if (efficiency > 100) efficiency = 100; + /* + ** Calculate total score + */ + long total = ((leadership * 40) + (4600) + (efficiency * 14))/100; + if (!total) total++; + total *= (BuildLevel+1); + +// Load up the shapes for the Nod score screen + if (house == HOUSE_GOOD) { + yellowptr = MixFileClass::Retrieve("BAR3YLW.SHP"); + redptr = MixFileClass::Retrieve("BAR3RED.SHP"); + } + +/* Change to the six-point font for Text_Print */ + oldfont = Set_Font(ScoreFontPtr); + Call_Back(); + +/* --- Now display the background animation --- */ + Hide_Mouse(); + Animate_Frame(anim, SysMemPage, 1); + SysMemPage.Blit(*PseudoSeenBuff); + Increase_Palette_Luminance (Palette , 30,30,30,63); + InterpolationPalette = Palette; + InterpolationPaletteChanged = TRUE; + Read_Interpolation_Palette(inter_pal); + Interpolate_2X_Scale( PseudoSeenBuff , &SeenBuff , inter_pal); + Fade_Palette_To(Palette, FADE_PALETTE_FAST, Call_Back); + + Play_Sample(country4, 255, Options.Normalize_Sound(90)); + + int frame = 1; + StreamLowImpact = true; + while (frame < Get_Animation_Frame_Count(anim)) { + Animate_Frame(anim, *PseudoSeenBuff, frame++); + ////////////////Interpolate_2X_Scale( PseudoSeenBuff , &SeenBuff , NULL); + Call_Back_Delay(2); + } + StreamLowImpact = false; + Call_Back(); + Close_Animation(anim); + + /* + ** Background's up, so now load various shapes and animations + */ + void const * timeshape = MixFileClass::Retrieve("TIME.SHP"); + ScoreObjs[0] = new ScoreTimeClass(233, 2, timeshape, 30, 4); + + void const * hiscore1shape = MixFileClass::Retrieve("HISCORE1.SHP"); + void const * hiscore2shape = MixFileClass::Retrieve("HISCORE2.SHP"); + ScoreObjs[1] = new ScoreTimeClass(4, 97, hiscore1shape, 10, 4); + ScoreObjs[2] = new ScoreTimeClass(8, 172, hiscore2shape, 10, 4); + + /* Now display the stuff */ + PseudoSeenBuff->Blit(SysMemPage); + + + if (house == HOUSE_BAD) { + + /* + ** load the logo + */ + void const * logoptr = MixFileClass::Retrieve("LOGOS.SHP"); + CC_Draw_Shape(logoptr, 1, 0, 0, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + + Bit_It_In_Scale(0,0, 128,104-16, &SysMemPage, PseudoSeenBuff, &SeenBuff , 1); + } + + Set_Logic_Page(PseudoSeenBuff); + +#ifdef FRENCH + Alloc_Object(new ScorePrintClass(TXT_SCORE_TIME, 200, 3,_greenpal)); +#else + Alloc_Object(new ScorePrintClass(TXT_SCORE_TIME, 206, 3,_greenpal)); +#endif + Alloc_Object(new ScorePrintClass(TXT_SCORE_LEAD, 182, 26,_greenpal)); + Alloc_Object(new ScorePrintClass(TXT_SCORE_EFFI, 182, 38,_greenpal)); + Alloc_Object(new ScorePrintClass(TXT_SCORE_TOTA, 182, 50,_greenpal)); + Play_Sample(sfx4, 255, Options.Normalize_Sound(120)); + Call_Back_Delay(13); + + max = MAX((long)leadership, (long)efficiency); + int scorecounter = 0; + Keyboard::Clear(); + + BlitList.Add (264*2, 26*2, 264*2, 26*2, 4*12 , 12); + BlitList.Add (264*2, 38*2, 264*2, 38*2, 4*12 , 12); + BlitList.Add (264*2, 50*2, 264*2, 50*2, 4*12 , 12); + BlitList.Add (275*2, 9*2, 275*2, 9*2, 64, 12 ); //Minutes + for (i = 0; i <= 160; i++) { + Set_Font_Palette(_greenpal); + Count_Up_Print("%3d%%", i, leadership, 264, 26); + if (i>=30) Count_Up_Print("%3d%%", i-30, efficiency, 264, 38); + if (i>=60) { + Count_Up_Print("%3d",scorecounter, total, 264, 50); + scorecounter += total/100; + } + Print_Minutes(minutes); + Call_Back_Delay(1); + Play_Sample(Beepy6, 255, Options.Normalize_Sound(60)); + if (Check_Key() && i < (max-5) ) { + i=158; + Keyboard::Clear(); + } + } + Count_Up_Print("%3d", total, total, 264, 50); + + Call_Back_Delay(60); + + if (house == HOUSE_BAD) Show_Credits(house, _greenpal); + + Call_Back_Delay(60); + + /* + ** Show stats on # of units killed + */ + Set_Logic_Page(*PseudoSeenBuff); + Play_Sample(sfx4, 255, Options.Normalize_Sound(90)); + Alloc_Object(new ScorePrintClass(TXT_SCORE_CASU, _casuax[house], _casuay[house],_redpal)); + Call_Back_Delay(9); + if (house == HOUSE_BAD) { + Alloc_Object(new ScorePrintClass(TXT_SCORE_NEUT, 200, 114,_redpal)); + Call_Back_Delay(4); + } + + Alloc_Object(new ScorePrintClass(TXT_SCORE_GDI, _gditxx[house], _gditxy[house],_redpal)); + Alloc_Object(new ScorePrintClass(TXT_SCORE_NOD, _nodtxx[house], _nodtxy[house],_redpal)); + Call_Back_Delay(6); + + Set_Font_Palette(_redpal); + if (house == HOUSE_BAD) { + Do_Nod_Casualties_Graph(); + } else { + Do_GDI_Graph(yellowptr, redptr, GKilled + CKilled, NKilled, 88); + } + + Set_Logic_Page(*PseudoSeenBuff); + + /* + ** Print out stats on buildings destroyed + */ + Play_Sample(sfx4, 255, Options.Normalize_Sound(90)); + if (house == HOUSE_GOOD) { + Alloc_Object(new ScorePrintClass(TXT_SCORE_BUIL, 144, 126,_greenpal)); + Call_Back_Delay(9); + } else { + Alloc_Object(new ScorePrintClass(TXT_SCORE_BUIL1, 146, 128,_greenpal)); + Alloc_Object(new ScorePrintClass(TXT_SCORE_BUIL2, 146, 136,_greenpal)); + Call_Back_Delay(9); + Alloc_Object(new ScorePrintClass(TXT_SCORE_NEUT, 200, 152,_greenpal)); + Call_Back_Delay(4); + } + Alloc_Object(new ScorePrintClass(TXT_SCORE_GDI, _gditxx[house], _bldggy[house],_greenpal)); + Alloc_Object(new ScorePrintClass(TXT_SCORE_NOD, _gditxx[house], _bldgny[house],_greenpal)); + Call_Back_Delay(7); + + if (house==HOUSE_BAD) { + Call_Back_Delay(6); + Set_Font_Palette(_greenpal); + Do_Nod_Buildings_Graph(); + } else { + Do_GDI_Graph(yellowptr, redptr, GBKilled + CBKilled, NBKilled, 136); + } + + // Wait for text printing to complete + while (StillUpdating){ + Call_Back_Delay(1); + } + + if (Keyboard::Check()) Keyboard::Clear(); + + if (house == HOUSE_GOOD) Show_Credits(house, _greenpal); + + /* + ** Hall of fame display and processing + */ + Play_Sample(sfx4, 255, Options.Normalize_Sound(90)); + Alloc_Object(new ScorePrintClass(TXT_SCORE_TOP, 28, 110,_bluepal)); + Call_Back_Delay(9); + + /* + ** First check for the existence of the file, and if there isn't one, + ** make a new one filled with blanks. + */ + if (!file.Is_Available()) { + + // hall of fame doesn't exist, so blank it out & write it + file.Open(WRITE); + + for (i = 0; i < NUMFAMENAMES; i++) { + hallfame[i].name[0] = + hallfame[i].score = + hallfame[i].level = 0; + file.Write(&hallfame[i], sizeof(struct Fame)); + } + + file.Close(); + } + + file.Open(READ); + for (i = 0; i < NUMFAMENAMES; i++) { + file.Read(&hallfame[i], sizeof(struct Fame)); + } + file.Close(); + + /* + ** If the player's score is good enough to bump someone off the list, + ** remove their data, move everyone down a notch, and set index = where + ** their info goes + */ + if(hallfame[NUMFAMENAMES-1].score >= total) + hallfame[NUMFAMENAMES-1].score = 0; + for (index = 0; index < NUMFAMENAMES; index++) { + if (total > hallfame[index].score) { + if (index < (NUMFAMENAMES-1)) for (i = (NUMFAMENAMES-1); i > index; i--) hallfame[i] = hallfame[i-1]; + hallfame[index].score = total; + hallfame[index].level = Scenario; +// hallfame[index].level = BuildLevel; + //hallfame[index].name[0] = 0; // blank out the name + memset (hallfame[index].name, ' ', sizeof (hallfame[index].name) -1); + hallfame[index].name[sizeof (hallfame[index].name)-1] = 0; + break; + } + } + + /* + ** Now display the hall of fame + */ + Set_Logic_Page(*PseudoSeenBuff); + for (i = 0; i < NUMFAMENAMES; i++) { + Alloc_Object(new ScorePrintClass(hallfame[i].name, HALLFAME_X, HALLFAME_Y + (i*8), _bluepal)); + if (hallfame[i].score) { + char *str = (char *)(SysMemPage.Get_Buffer()) + i*32; + sprintf(str,"%d", hallfame[i].score); + Alloc_Object(new ScorePrintClass(str, HALLFAME_X+(6*15), HALLFAME_Y + (i*8), _bluepal, BLACK)); + if (hallfame[i].level < 20) { + sprintf(str+16,"%d", hallfame[i].level); + } else { + strcpy(str+16, "**"); + } + Alloc_Object(new ScorePrintClass(str+16, HALLFAME_X+(6*12), HALLFAME_Y + (i*8), _bluepal, BLACK)); + Call_Back_Delay(13); + } + } + + // Wait for text printing to complete + while (StillUpdating){ + Call_Back_Delay(1); + } + + /* + ** If the player's on the hall of fame, have him enter his name now + */ + Keyboard::Clear(); + if (index < NUMFAMENAMES) { + Input_Name(hallfame[index].name, HALLFAME_X, HALLFAME_Y + (index*8), _bluepal); + + file.Open(WRITE); + for (i = 0; i < NUMFAMENAMES; i++) { + file.Write(&hallfame[i], sizeof(struct Fame)); + } + file.Close(); + } else { +#ifdef FRENCH + Alloc_Object(new ScorePrintClass(TXT_MAP_CLICK2, 145, 190, _yellowpal)); +#else + Alloc_Object(new ScorePrintClass(TXT_MAP_CLICK2, 149, 190, _yellowpal)); +#endif + Cycle_Wait_Click(); + } + +#ifdef WRITE_LBM + file.Open("e:scorscrn.lbm",WRITE); + SeenBuff.Blit(SysMemPage,0,0, 0,0, 320,200); + CCWrite_LBM_File(file, SysMemPage, 8, Palette); + file.Close(); +#endif + + Keyboard::Clear(); + +/* get rid of all the animating objects */ + for (i = 0; i < MAXSCOREOBJS; i++) if (ScoreObjs[i]) { + delete ScoreObjs[i]; + ScoreObjs[i] = 0; + } + Fade_Palette_To(BlackPalette, FADE_PALETTE_FAST, NULL); + VisiblePage.Clear(); + + Show_Mouse(); +// Map_Selection(); + + Theme.Queue_Song(THEME_NONE); + + Fade_Palette_To(BlackPalette, FADE_PALETTE_FAST, NULL); + VisiblePage.Clear(); + Set_Palette(GamePalette); + + Set_Font(oldfont); + FontXSpacing = oldfontxspacing; + ControlQ = 0; + + Set_Logic_Page (SeenBuff); + + delete PseudoSeenBuff; + delete TextPrintBuffer; + TextPrintBuffer = NULL; + BlitList.Clear(); + Enable_Uncompressed_Shapes(); +} + + +void Cycle_Wait_Click(void) +{ + int counter; + int minclicks = 20; + unsigned long timingtime = TickCount.Time(); + SerialPacketType sendpacket; + SerialPacketType receivepacket; + int packetlen; + + + Keyboard::Clear(); + while (minclicks || (!Check_Key() && !ControlQ) ) { + + if (GameToPlay == GAME_NULL_MODEM || + GameToPlay == GAME_MODEM){ + //GameToPlay == GAME_INTERNET) { + + // + // send a timing packet if enough time has gone by. + // + if ( (TickCount.Time() - timingtime) > PACKET_TIMING_TIMEOUT) { + sendpacket.Command = SERIAL_SCORE_SCREEN; + sendpacket.ResponseTime = NullModem.Response_Time(); + sendpacket.ID = ModemGameToPlay; + + NullModem.Send_Message (&sendpacket, sizeof(sendpacket), 0); + timingtime = TickCount.Time(); + } + + if (NullModem.Get_Message (&receivepacket, &packetlen) > 0) { + // throw packet away + packetlen = packetlen; + } + + NullModem.Service(); + } + + Call_Back_Delay(1); + if (minclicks) { + minclicks--; + Keyboard::Clear(); + } + + counter = ((++counter) & 7); + + if (!counter) { + unsigned char r,g,b; + + r = Palette[233*3+0]; + g = Palette[233*3+1]; + b = Palette[233*3+2]; + + for (int i = 233; i < 237; i++) { + Palette[i*3+0] = Palette[(i+1)*3+0]; + Palette[i*3+1] = Palette[(i+1)*3+1]; + Palette[i*3+2] = Palette[(i+1)*3+2]; + } + Palette[237*3+0] = r; + Palette[237*3+1] = g; + Palette[237*3+2] = b; + + Set_Palette(Palette); + } + } + Keyboard::Clear(); +} + +void ScoreClass::Do_Nod_Buildings_Graph(void) +{ + int shapenum; + InfantryTypeClass const *ramboclass; + + void const * factptr = MixFileClass::Retrieve("FACT.SHP"); + void const * rmboptr = MixFileClass::Retrieve("RMBO.SHP"); + void const * fball1ptr = MixFileClass::Retrieve("FBALL1.SHP"); + ramboclass = &InfantryTypeClass::As_Reference(INFANTRY_E5); + + /* + ** Print the # of buildings on the hidpage so we only need to do it once + */ + PseudoSeenBuff->Blit(SysMemPage); + Set_Logic_Page(SysMemPage); + Call_Back_Delay(30); + BlitList.Add (2* (BUILDING_X + 8), 2* (BUILDING_Y), 2* (BUILDING_X+8), 2* (BUILDING_Y), 5*12 , 12); + BlitList.Add (2* (BUILDING_X + 8), 2* (BUILDING_Y + 12), 2* (BUILDING_X+8), 2* (BUILDING_Y + 12), 5*12 , 12); + BlitList.Add (2* (BUILDING_X + 8), 2* (BUILDING_Y + 24), 2* (BUILDING_X+8), 2* (BUILDING_Y + 24), 5*12 , 12); + + TextPrintBuffer->Print( 0, (BUILDING_X + 8)*2, BUILDING_Y*2, TBLACK, TBLACK); + TextPrintBuffer->Print( 0, (BUILDING_X + 8)*2, (BUILDING_Y + 12)*2, TBLACK, TBLACK); + TextPrintBuffer->Print( 0, (BUILDING_X + 8)*2, (BUILDING_Y + 24)*2, TBLACK, TBLACK); + + /* + ** Here's the animation/draw loop for blowing up the factory + */ + for (int i=0; i<98; i++) { + SysMemPage.Blit(SysMemPage, BUILDING_X, BUILDING_Y, 0, 0, 320-BUILDING_X,48); + shapenum = 0; // no damage + if (i >= 60) { + shapenum = Extract_Shape_Count(factptr) - 2; // some damage + if (i == 60) { + Shake_Screen(6); + Sound_Effect(VOC_CRUMBLE, VOL_FULL, 0); + } + if (i > 65) { + shapenum = Extract_Shape_Count(factptr) - 1; // mega damage + } + } + + /* + ** Draw the building before Rambo + */ + if (i < 68) { + CC_Draw_Shape(factptr,shapenum, 0, 0, WINDOW_MAIN, + SHAPE_FADING|SHAPE_WIN_REL, ScoreRemapBldg,Map.UnitShadow); + + } + + /* + ** Now draw some fires, if appropriate + */ + if (i >= 61) { + int firecount = Extract_Shape_Count(fball1ptr); + int shapeindex = (i-61)>>1; + if (shapeindex < firecount) { + CC_Draw_Shape(fball1ptr, shapeindex, 10, 10, WINDOW_MAIN, + SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL,//|SHAPE_GHOST, + ScoreRemapFBall,0); + } + if (i > 64) { + shapeindex = (i-64)>>1; + if (shapeindex < firecount) { + CC_Draw_Shape(fball1ptr, shapeindex, 50, 30, WINDOW_MAIN, + SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL,//|SHAPE_GHOST, + ScoreRemapFBall,0); + } + } + } + /* + ** Draw the Rambo character running away from the building + */ + CC_Draw_Shape(rmboptr, (ramboclass->DoControls[DO_WALK].Frame + ramboclass->DoControls[DO_WALK].Jump*6) + ((i>>1)%ramboclass->DoControls[DO_WALK].Count), + i+32, 40, WINDOW_MAIN, + SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL,//|SHAPE_GHOST, + ScoreRemapYellow,Map.UnitShadow); + SysMemPage.Blit(*PseudoSeenBuff, 0, 0, BUILDING_X, BUILDING_Y, 320-BUILDING_X,48); + + /* + ** Extra font related stuff. ST - 7/29/96 2:22PM + */ + Interpolate_2X_Scale(PseudoSeenBuff , &HidPage ,NULL); + BlitList.Update(); + WWMouse->Draw_Mouse(&HidPage); + HidPage.Blit(SeenBuff); + WWMouse->Erase_Mouse(&HidPage, TRUE); + //Interpolate_2X_Scale( PseudoSeenBuff , &SeenBuff , NULL); + + if (!Check_Key()) Call_Back_Delay(1); + } + + i = MAX(GBKilled, NBKilled); + i = MAX(i, CBKilled); + + for (int q = 0; q <= i; q++) { + Count_Up_Print( "%d", q, GBKilled,BUILDING_X + 8,BUILDING_Y ); + Count_Up_Print( "%d", q, NBKilled,BUILDING_X + 8,BUILDING_Y + 12); + Count_Up_Print( "%d", q, CBKilled,BUILDING_X + 8,BUILDING_Y + 24); + if (!Check_Key()) { + Play_Sample(Beepy6, 255, Options.Normalize_Sound(110)); + Call_Back_Delay(1); + } + } +} + + +/*************************************************************************** + * DO_GDI_GRAPH -- Show # of people or buildings killed on GDI score screen* + * * + * * + * * + * INPUT: yellowptr, redptr = pointers to shape file for graphs * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/03/1995 BWG : Created. * + *=========================================================================*/ + +void ScoreClass::Do_GDI_Graph(void const * yellowptr, void const * redptr, int gkilled, int nkilled, int ypos) +{ + int i, max; + int gdikilled = gkilled, nodkilled=nkilled; + + max = MAX(gdikilled, nodkilled); + if (!max) max=1; + + gdikilled = (gdikilled * SIZEGBAR) / max; + nodkilled = (nodkilled * SIZEGBAR) / max; + if (max < 20) { + gdikilled = gkilled * 5; + nodkilled = nkilled * 5; + } + + max = MAX(gdikilled, nodkilled); + if (!max) max=1; + + // Draw the white-flash shape on the hidpage + Set_Logic_Page(SysMemPage); + SysMemPage.Fill_Rect(0,0, 124,9, TBLACK); + CC_Draw_Shape(redptr, 120, 0,0, WINDOW_MAIN,SHAPE_WIN_REL, 0, 0); + Set_Logic_Page(PseudoSeenBuff); + + BlitList.Add (2* 297, 2* (ypos+2), 2* 297, 2* (ypos+2), 5*12 , 12); + + for (i = 1; i <= gdikilled; i++) { + if (i != gdikilled) { + CC_Draw_Shape(yellowptr,i, 172, ypos, WINDOW_MAIN,SHAPE_WIN_REL, 0, 0); + } else { + SysMemPage.Blit(*PseudoSeenBuff,0,0, 172, ypos, 3+gdikilled,9); + } + + Count_Up_Print("%d", (i*gkilled) / max, gkilled, 297, ypos+2); + if (!Check_Key()) { + Play_Sample(Beepy6, 255, Options.Normalize_Sound(110)); + Call_Back_Delay(2); + } + } + CC_Draw_Shape(yellowptr,gdikilled, 172,ypos , WINDOW_MAIN,SHAPE_WIN_REL, 0, 0); + Count_Up_Print("%d", gkilled, gkilled, 297, ypos+ 2); + if (!Check_Key()) Call_Back_Delay(40); + + BlitList.Add (2* 297, 2* (ypos+14), 2* 297, 2* (ypos+14), 5*12 , 12); + for (i = 1; i <= nodkilled; i++) { + if (i != nodkilled) { + CC_Draw_Shape(redptr, i, 172, ypos+12, WINDOW_MAIN,SHAPE_WIN_REL, 0, 0); + } else { + SysMemPage.Blit(*PseudoSeenBuff,0,0, 172,ypos+12, 3+nodkilled,9); + } + + Count_Up_Print("%d", (i*nkilled) / max, nkilled, 297, ypos+14); + if (!Check_Key()) { + Play_Sample(Beepy6, 255, Options.Normalize_Sound(110)); + Call_Back_Delay(2); + } + } + +// if (Keyboard::Check()) Keyboard::Clear(); + + /* + ** Make sure accurate count is printed at end + */ + CC_Draw_Shape( redptr,nodkilled, 172,ypos+12, WINDOW_MAIN,SHAPE_WIN_REL, 0, 0); + Count_Up_Print("%d", nkilled, nkilled, 297, ypos+14); + if (!Check_Key()) Call_Back_Delay(40); +} + + +void ScoreClass::Do_Nod_Casualties_Graph(void) +{ + int i, gdikilled, nodkilled, civkilled, max; + + void const * e1ptr = MixFileClass::Retrieve("E1.SHP"); + void const * c1ptr = MixFileClass::Retrieve("C1.SHP"); + + gdikilled = GKilled; + nodkilled = NKilled; + civkilled = CKilled; + max = MAX(gdikilled, nodkilled); + max = MAX(max, civkilled); + + if (!max) max=1; + if ((gdikilled > (MAX_BAR_X - BARGRAPH_X)) || (nodkilled > (MAX_BAR_X - BARGRAPH_X)) || (civkilled > (MAX_BAR_X - BARGRAPH_X))) { + gdikilled = (gdikilled * (MAX_BAR_X - BARGRAPH_X)) / max; + nodkilled = (nodkilled * (MAX_BAR_X - BARGRAPH_X)) / max; + civkilled = (civkilled * (MAX_BAR_X - BARGRAPH_X)) / max; + } + + max = MAX(gdikilled, nodkilled); + max = MAX(max, civkilled); + if (!max) max=1; + + /* + ** Initialize a bunch of objects for the infantrymen who pose for the bar + ** graphs of casualties. + */ + int q = NUMINFANTRYMEN/3; + int r = q*2; + for (i = 0; i < NUMINFANTRYMEN/3; i++) { + InfantryMan[i+0].xpos = + InfantryMan[i+q].xpos = + InfantryMan[i+r].xpos = (i*10) + 7; + InfantryMan[i+0].ypos = 11; + InfantryMan[i+q].ypos = 21; + InfantryMan[i+r].ypos = 31; + InfantryMan[i+0].shapefile = + InfantryMan[i+q].shapefile = e1ptr; + InfantryMan[i+r].shapefile = c1ptr; + InfantryMan[i+0].remap = ScoreRemapYellow; + InfantryMan[i+q].remap = ScoreRemapGrey; + InfantryMan[i+r].remap = RemapCiv; + InfantryMan[i+0].anim = + InfantryMan[i+q].anim = + InfantryMan[i+r].anim = 0; + InfantryMan[i+0].stage = + InfantryMan[i+q].stage = + InfantryMan[i+r].stage = 0; + InfantryMan[i+0].delay = + InfantryMan[i+q].delay = + InfantryMan[i+r].delay = Random() & 0x1F; + InfantryMan[i+0].Class = + InfantryMan[i+q].Class = &InfantryTypeClass::As_Reference(INFANTRY_E1); + InfantryMan[i+r].Class = &InfantryTypeClass::As_Reference(INFANTRY_C1); + + } + + /* + ** Draw the infantrymen and pause briefly before running the graph + */ + Draw_InfantryMen(); + SysMemPage.Blit(*PseudoSeenBuff, 0, 0, BARGRAPH_X, CASUALTY_Y, 320-BARGRAPH_X, 34); + //Interpolate_2X_Scale( PseudoSeenBuff , &SeenBuff, NULL); + /* + ** Extra font related stuff. ST - 7/29/96 2:22PM + */ + Interpolate_2X_Scale(PseudoSeenBuff , &HidPage ,NULL); + BlitList.Update(); + WWMouse->Draw_Mouse(&HidPage); + HidPage.Blit(SeenBuff); + WWMouse->Erase_Mouse(&HidPage, TRUE); + + Call_Back_Delay(40); + + BlitList.Add (2* (SCORETEXT_X + 64), 2* (CASUALTY_Y + 2), 2* (SCORETEXT_X + 64), 2* (CASUALTY_Y + 2), 5*12 , 12); + BlitList.Add (2* (SCORETEXT_X + 64), 2* (CASUALTY_Y + 14), 2* (SCORETEXT_X + 64), 2* (CASUALTY_Y + 14), 5*12 , 12); + BlitList.Add (2* (SCORETEXT_X + 64), 2* (CASUALTY_Y + 26), 2* (SCORETEXT_X + 64), 2* (CASUALTY_Y + 26), 5*12 , 12); + + for (i = 1; i <= max; i++) { + // Draw & update infantrymen 3 times for every tick on the graph (i) + for (int q = 0; q < 3; q++) { + Draw_InfantryMen(); + Draw_Bar_Graphs(i, gdikilled, nodkilled, civkilled); + SysMemPage.Blit(*PseudoSeenBuff, 0, 0, BARGRAPH_X, CASUALTY_Y, 320-BARGRAPH_X, 34); + + Count_Up_Print("%d", (i*GKilled) / max, GKilled, SCORETEXT_X+64, CASUALTY_Y + 2); + Count_Up_Print("%d", (i*NKilled) / max, NKilled, SCORETEXT_X+64, CASUALTY_Y + 14); + Count_Up_Print("%d", (i*CKilled) / max, CKilled, SCORETEXT_X+64, CASUALTY_Y + 26); + if (!Check_Key()) Call_Back_Delay(3); + } + Play_Sample(Beepy6, 255, Options.Normalize_Sound(110)); + } + if (Check_Key()) Keyboard::Clear(); + + /* + ** Make sure accurate count is printed at end + */ + Count_Up_Print("%d", GKilled, GKilled, SCORETEXT_X+64, CASUALTY_Y + 2); + Count_Up_Print("%d", NKilled, NKilled, SCORETEXT_X+64, CASUALTY_Y + 14); + Count_Up_Print("%d", CKilled, CKilled, SCORETEXT_X+64, CASUALTY_Y + 26); + + /* + ** Finish up death animations, if there are any active + */ + int k = 1; + while (k) { + for (i=k=0; i= DO_GUN_DEATH) k=1; + if (k) Draw_InfantryMen(); + Draw_Bar_Graphs(max, gdikilled, nodkilled, civkilled); + SysMemPage.Blit(*PseudoSeenBuff, 0, 0, BARGRAPH_X, CASUALTY_Y, 320-BARGRAPH_X, 34); + Call_Back_Delay(1); + } +} + + +void ScoreClass::Show_Credits(int house, char const pal[]) +{ + static int _credsx[2]={276,276}; + static int _credsy[2]={173,58}; + static int _credpx[2]={228,236}; +#ifdef GERMAN + static int _credpy[2]={181, 74}; + static int _credtx[2]={162,162}; + static int _credty[2]={173, 62}; +#else + static int _credpy[2]={189-12, 74}; + static int _credtx[2]={182,182}; + static int _credty[2]={179-12, 62}; +#endif + + int credobj,i; + int min,add; + + void const * credshape = MixFileClass::Retrieve("CREDS.SHP"); + + Alloc_Object(new ScorePrintClass(TXT_SCORE_ENDCRED, _credtx[house], _credty[house],pal)); + Call_Back_Delay(15); + + credobj = Alloc_Object(new ScoreCredsClass(_credsx[house], _credsy[house], credshape, 32, 2)); + min = PlayerPtr->Available_Money() / 100; + + /* + ** Print out total credits left at end of scenario + */ + i = -50; + + BlitList.Add (2* (_credpx[house]), 2* (_credpy[house]), 2* (_credpx[house]), 2* (_credpy[house]), 5*12 , 12); + + do { + add = 5; + if ((PlayerPtr->Available_Money() - i) > 100 ) add += 15; + if ((PlayerPtr->Available_Money() - i) > 1000) add += 30; + if (add < min) add = min; + i += add; + + if (i < 0) i=0; + + Set_Font_Palette(pal); + Count_Up_Print("%d", i, PlayerPtr->Available_Money(), _credpx[house], _credpy[house]); + Call_Back_Delay(2); + if (Check_Key()) { + i=PlayerPtr->Available_Money() - 5; + Keyboard::Clear(); + } + } while (i < PlayerPtr->Available_Money()) ; + + // Make sure the credits object doesn't freeze on the white stage + while (((ScoreTimeClass *)ScoreObjs[credobj])->Stage >= 20 && !ControlQ) { + Call_Back_Delay(1); + } + delete ScoreObjs[credobj]; + ScoreObjs[credobj] = 0; +} + + +/*************************************************************************** + * SCORECLASS::PRINT_MINUTES -- Print out hours/minutes up to max * + * * + * Same as count-up-print, but for the time * + * * + * INPUT: current minute count and maximum * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void ScoreClass::Print_Minutes(int minutes) +{ + char str[20]; + if (minutes >= 60) { + if ((minutes/60) > 9) minutes = (9*60 + 59); + sprintf(str,Text_String(TXT_SCORE_TIMEFORMAT1), (minutes / 60),(minutes % 60)); + } else { + sprintf(str,Text_String(TXT_SCORE_TIMEFORMAT2), minutes); + } + TextPrintBuffer->Print(str, 275*2, 9*2, TBLACK, TBLACK); +} + + +/*********************************************************************************************** + * ScoreClass::Count_Up_Print -- Prints a number (up to its max) into a string, cleanly. * + * * + * This routine prints out a number (like 70) or its maximum number, into a string, onto * + * the screen, on a clean section of the screen, and blits it forward to the seenpage so you* + * can print without flashing and can print over something (to count up %'s). * + * * + * INPUT: str = string to print into * + * percent = # to print * + * max = # to print if percent > max * + * xpos = x pixel coord * + * ypos = y pixel coord * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/07/1995 BWG : Created. * + *=============================================================================================*/ +void ScoreClass::Count_Up_Print(char *str, int percent, int max, int xpos, int ypos) +{ + char destbuf[64]; + int width; + + sprintf(destbuf, str, percent <= max ? percent : max); + width = strlen(destbuf) * 7; + + + + +// HidPage.Blit(HidPage, xpos, ypos, 0, 0, width, 8); +// Set_Logic_Page(HidPage); +// LogicPage->Print( destbuf, 0, 0, WHITE, TBLACK); +// HidPage.Blit(SeenBuff, 0, 0, xpos, ypos, width, 8); + + TextPrintBuffer->Fill_Rect (xpos*2, ypos*2, (xpos + width)*2, (ypos+7)*2, BLACK); + TextPrintBuffer->Print (destbuf, xpos*2, ypos*2, WHITE, TBLACK); + + + //TextPrintBuffer->Blit(*TextPrintBuffer, xpos*2, ypos*2, 0, 0, width*2, 8*2); + //TextPrintBuffer->Print(destbuf, 0, 0, WHITE, TBLACK); + //TextPrintBuffer->Blit(SeenBuff, 0, 0, xpos*2, ypos*2, width*2, 8*2); + +// PseudoSeenBuff->Print( destbuf, xpos, ypos, TBLACK, BLACK); +// Interpolate_2X_Scale( PseudoSeenBuff , &SeenBuff , NULL); + +} + + +/*********************************************************************************************** + * ScoreClass::Input_Name -- Gets the name from the keyboard * + * * + * This routine handles keyboard input, and does a nifty zooming letter effect too. * + * * + * INPUT: str = string to put user's typing into * + * xpos = x pixel coord * + * ypos = y pixel coord * + * pal = text remapping palette to print using * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/15/1995 BWG : Created. * + *=============================================================================================*/ +void ScoreClass::Input_Name(char str[], int xpos, int ypos, char const pal[]) +{ + int key, ascii, index=0; + + void const * keystrok = MixFileClass::Retrieve("KEYSTROK.AUD"); + + /* + ** Ready the hidpage so it can restore background under zoomed letters + */ + PseudoSeenBuff->Blit(SysMemPage); + + do { + Call_Back(); + Animate_Score_Objs(); + Animate_Cursor(index, ypos); + /* + ** Extra font related stuff. ST - 7/29/96 2:22PM + */ + Interpolate_2X_Scale (PseudoSeenBuff , &HidPage ,NULL); + BlitList.Update(); + HidPage.Blit(SeenBuff); + + if (Check_Key()) { //if (Keyboard::Check()) { + key = Get_Key(); //key = Keyboard::Get(); + + if (index == MAX_FAMENAME_LENGTH-2) { + while (Check_Key()) { + Get_Key(); + } + } + + /* + ** If they hit 'backspace' when they're on the last letter, + ** turn it into a space instead. + */ + if ((key == KA_BACKSPACE) && (index == MAX_FAMENAME_LENGTH-2) ) { + if (str[index] && str[index]!=32) key = 32; + } + if (key == KA_BACKSPACE) { //if (key == KN_BACKSPACE) { + if (index) { + str[--index] = 0; + + int xposindex6 = xpos+(index*6); + + PseudoSeenBuff->Fill_Rect(xposindex6,ypos,xposindex6+6,ypos+6,TBLACK); + SysMemPage.Fill_Rect(xposindex6,ypos,xposindex6+6,ypos+6,TBLACK); + TextPrintBuffer->Fill_Rect(xposindex6*2,ypos*2,(xposindex6+6)*2,(ypos+6)*2, BLACK); + } + + } else if (key!=KA_RETURN) { //else if (key != KN_RETURN && key!=KN_KEYPAD_RETURN) { + ascii = key; //ascii = KN_To_KA(key); + if (ascii >= 'a' && ascii <= 'z') ascii -= ('a' - 'A'); +//if (ascii >='A' && ascii<='Z' || ascii == ' ') { +if ( (ascii >= '!' && ascii <= KA_TILDA) || ascii == ' ') { + PseudoSeenBuff->Fill_Rect(xpos + (index*6), ypos, xpos + (index*6)+6, ypos+5, TBLACK); + SysMemPage.Fill_Rect(xpos + (index*6), ypos, xpos + (index*6)+6, ypos+5, TBLACK); + TextPrintBuffer->Fill_Rect(2*(xpos + (index*6)), ypos*2, 2*(xpos + (index*6)+6), 2*(ypos+6), BLACK); + str[index] = ascii; + str[index+1] = 0; + + int objindex; + Play_Sample(keystrok, 255, Options.Normalize_Sound(255)); + objindex = Alloc_Object(new ScoreScaleClass(str+index,xpos+(index*6), ypos, pal)); + while (ScoreObjs[objindex]) Call_Back_Delay(1); + + if (index < (MAX_FAMENAME_LENGTH-2) ) index++; + } + } + } + } while(key!=KA_RETURN); // } while(key != KN_RETURN && key!=KN_KEYPAD_RETURN); +} + + +void Animate_Cursor(int pos, int ypos) +{ + static int _lastpos, _state; + static CountDownTimerClass _timer; + + ypos += 7; // move cursor to bottom of letter + + // If they moved the cursor, erase old one and force state=0, to make green draw right away + if (pos != _lastpos) { + PseudoSeenBuff->Draw_Line(HALLFAME_X + (_lastpos*6),ypos, HALLFAME_X + (_lastpos*6) + 5, ypos, TBLACK); + TextPrintBuffer->Fill_Rect(2*(HALLFAME_X + (_lastpos*6)),2*ypos, 2*(HALLFAME_X + (_lastpos*6) + 5), 2*ypos+1, BLACK); + _lastpos = pos; + _state = 0; + } + + PseudoSeenBuff->Draw_Line(HALLFAME_X + (pos*6),ypos, HALLFAME_X + (pos*6)+5, ypos, _state ? LTBLUE : TBLACK); + TextPrintBuffer->Fill_Rect(2*(HALLFAME_X + (pos*6)),2*ypos, 2*(HALLFAME_X + (pos*6)+5), 2*ypos+1, _state ? LTBLUE : BLACK); + + /* + ** Toggle the color of the cursor, green or black, if it's time to do so. + */ + if (!_timer.Time()) { + _state ^= 1; + _timer.Set(5); + } +} + + +/*************************************************************************** + * Draw_InfantryMen -- Draw all the guys on the score screen * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void Draw_InfantryMen() +{ + int k; + +// Only draw the infantrymen if we're playing Nod... GDI wouldn't execute +// people like that. + + /* + ** First restore the background + */ + SysMemPage.Blit(SysMemPage, BARGRAPH_X, CASUALTY_Y, 0, 0, 320-BARGRAPH_X, 34); + Set_Logic_Page(SysMemPage); + + /* + ** Then draw all the infantrymen on the clean SysMemPage + */ + for (k = 0; k < NUMINFANTRYMEN; k++) Draw_InfantryMan(k); + /* + ** They'll all be blitted over to the seenpage after the graphs are drawn + */ +} + +/*************************************************************************** + * Draw_InfantryMan -- Draw one guy in score screen, update animation * + * * + * This routine draws one of the infantrymen in the "Casualties" area * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void Draw_InfantryMan(int index) +{ + int stage; + + /* If the infantryman's dead, just abort this function */ + if (InfantryMan[index].anim == -1) return; + + stage = InfantryMan[index].stage + InfantryMan[index].Class->DoControls[InfantryMan[index].anim].Frame; + + CC_Draw_Shape(InfantryMan[index].shapefile, + stage, + InfantryMan[index].xpos, + InfantryMan[index].ypos, + WINDOW_MAIN, + SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL,//|SHAPE_GHOST, + InfantryMan[index].remap, + Map.UnitShadow); + /* + ** see if it's time to run a new anim + */ + if (--InfantryMan[index].delay < 0) { + InfantryMan[index].delay = 3; + if (++InfantryMan[index].stage >= InfantryMan[index].Class->DoControls[InfantryMan[index].anim].Count) { + + /* + ** was he playing a death anim? If so, and it's done, erase him + */ + if (InfantryMan[index].anim >= DO_GUN_DEATH) { + InfantryMan[index].anim = -1; + } else { + New_Infantry_Anim(index, DO_STAND_READY); + } + } + } +} + + +/*************************************************************************** + * New_Infantry_Anim -- Start up a new animation for one of the infantrymen* + * * + * * + * * + * INPUT: index: which of the 30 infantrymen to affect * + * anim: which animation sequence to start him into * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void New_Infantry_Anim(int index, int anim) +{ + InfantryMan[index].anim = anim; + InfantryMan[index].stage = 0; + if (anim >= DO_GUN_DEATH) { + InfantryMan[index].delay = 1; // start right away + } else { + InfantryMan[index].delay = Random() & 15; + } +} + + +/*************************************************************************** + * Draw_Bar_Graphs -- Draw "Casualties" bar graphs * + * * + * * + * * + * INPUT: i = current count of how far to draw graph * + * gkilled = # of GDI forces killed (adjusted to fit in space) * + * nkilled = # of Nod forces killed (adjusted to fit in space) * + * ckilled = # of civilians killed (adjusted to fit in space) * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void Draw_Bar_Graphs(int i, int gkilled, int nkilled, int ckilled) +{ + if (gkilled) { + LogicPage->Fill_Rect(0, 0+4, 0+MIN(i,gkilled), 0+5, LTCYAN); + LogicPage->Draw_Line(0+1, 0+6, 0+MIN(i,gkilled)+1, 0+6, TBLACK); + LogicPage->Draw_Line(0+MIN(i,gkilled)+1,0+5, 0+MIN(i,gkilled)+1, 0+5, TBLACK); + if (i <= gkilled) { + int anim = InfantryMan[i/11].anim; + if (anim!=-1 && anim < DO_GUN_DEATH) { + if (i/11) { + New_Infantry_Anim(i/11,DO_GUN_DEATH + (Random() & 3)); + } else { + New_Infantry_Anim(i/11,DO_GUN_DEATH); + } +// Sound_Effect(Random_Pick(VOC_SCREAM1, VOC_SCREAM5)); + } + } + } + if (nkilled) { + LogicPage->Fill_Rect(0, 0+16, 0+MIN(i,nkilled), 0+17, RED); + LogicPage->Draw_Line(0+1, 0+18, 0+MIN(i,nkilled)+1, 0+18, TBLACK); + LogicPage->Draw_Line(0+MIN(i,nkilled)+1, 0+17, 0+MIN(i,nkilled)+1, 0+17, TBLACK); + if (i <= nkilled) { + int anim = InfantryMan[(NUMINFANTRYMEN/3)+(i/11)].anim; + if (anim!=-1 && anim < DO_GUN_DEATH) { + if (i/11) { + New_Infantry_Anim((NUMINFANTRYMEN/3)+(i/11),DO_GUN_DEATH + (Random() & 3)); + } else { + New_Infantry_Anim((NUMINFANTRYMEN/3)+(i/11),DO_GUN_DEATH); + } +// Sound_Effect(Random_Pick(VOC_SCREAM1, VOC_SCREAM5)); + } + } + } + + if (ckilled) { + LogicPage->Fill_Rect(0, 0+28, 0+MIN(i,ckilled), 0+29, RED); + LogicPage->Draw_Line(0+1, 0+30, 0+MIN(i,ckilled)+1, 0+30, TBLACK); + LogicPage->Draw_Line(0+MIN(i,ckilled)+1, 0+29, 0+MIN(i,ckilled)+1, 0+29, TBLACK); + if (i <= ckilled) { + int anim = InfantryMan[((NUMINFANTRYMEN*2)/3)+(i/11)].anim; + if (anim!=-1 && anim < DO_GUN_DEATH) { + if (i/11) { + New_Infantry_Anim(((NUMINFANTRYMEN*2)/3)+(i/11),DO_GUN_DEATH + (Random() & 3)); + } else { + New_Infantry_Anim(((NUMINFANTRYMEN*2)/3)+(i/11),DO_GUN_DEATH); + } +// Sound_Effect(Random_Pick(VOC_SCREAM1, VOC_SCREAM5)); + } + } + } +} + + +/*************************************************************************** + * Call_Back_Delay -- Combines Call_Back() and Delay() functions * + * * + * This is just to cut down on code size and typing a little. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void Call_Back_Delay(int time) +{ + CountDownTimerClass cd; + + if (!ControlQ) { + if (Keyboard::Down(KN_LCTRL) && Keyboard::Down(KN_Q)) { + ControlQ = 1; + Keyboard::Clear(); + } + } + if (ControlQ) time=0; + + cd.Set(time); + StreamLowImpact = true; + do { + Call_Back(); + //Animate_Score_Objs(); + //Interpolate_2X_Scale(PseudoSeenBuff , &SeenBuff ,NULL); + + //if (Get_Mouse_State()){ + // Interpolate_2X_Scale(PseudoSeenBuff , &SeenBuff ,NULL); + //BlitList.Update(); + //}else{ + Animate_Score_Objs(); + Interpolate_2X_Scale(PseudoSeenBuff , &HidPage ,NULL); + BlitList.Update(); + WWMouse->Draw_Mouse(&HidPage); + HidPage.Blit(SeenBuff); + WWMouse->Erase_Mouse(&HidPage, TRUE); + //} + } while(cd.Time()); + StreamLowImpact = false; +} + + +void Animate_Score_Objs() +{ + StillUpdating = false; + for (int i = 0; i < MAXSCOREOBJS; i++) { + if (ScoreObjs[i]) { + ScoreObjs[i]->Update(); + } + } +} + +char *Int_Print(int a) +{ + static char str[10]; + + sprintf(str,"%d",a); + return str; +} + + +/*********************************************************************************************** + * Multi_Score_Presentation -- Multiplayer routine to display score screen. * + * * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/11/1995 BWG: Created. * + *=============================================================================================*/ +void Multi_Score_Presentation(void) +{ + static char const _cycleyellowpal[]={0x0,0xec,0xEb,0xea,0xE9,0xe9,0xE9,0x0,0xE9,0x0,0x0,0x0,0x0,0x0,0xED,0x0}; + + static char const _greenpal[]= {0x0,0x12,0x14,0x16,0x18,0x18,0x18,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x10,0x0}; + static char const _redpal[]= {0x0,0x22,0x24,0x26,0x28,0x28,0x28,0x0,0x28,0x0,0x0,0x0,0x0,0x0,0x20,0x0}; + static char const _graypal[]= {0x0,0xca,0xCb,0xcc,0xCd,0xcd,0xCd,0x0,0xCD,0x0,0x0,0x0,0x0,0x0,0xC8,0x0}; + static char const _orangepal[]={0x0,0xd1,0xD2,0xd3,0xD4,0xd4,0xD4,0x0,0xD4,0x0,0x0,0x0,0x0,0x0,0xD0,0x0}; + static char const _bluepal[]= {0x0,0x2,0x0a,0xb,0x0b,0xb,0x0B,0x0,0x0B,0x0,0x0,0x0,0x0,0x0,0x09,0x0}; + static char const _yellowpal[]={0x0,0x5,0xee,0xf1,0xf2,0xf2,0xF2,0xf2,0xF2,0x0,0x0,0x0,0x0,0x0,0x7D,0x0}; + + //static char const _greenpal[]= {0x0,0x0,0x12,0x0,0x14,0x0,0x16,0x0,0x18,0x0,0x0,0x0,0x0,0x0,0x10,0x0}; + //static char const _redpal[]= {0x0,0x0,0x22,0x0,0x24,0x0,0x26,0x0,0x28,0x0,0x0,0x0,0x0,0x0,0x20,0x0}; + //static char const _graypal[]= {0x0,0x0,0xCA,0x0,0xCB,0x0,0xCC,0x0,0xCD,0x0,0x0,0x0,0x0,0x0,0xC8,0x0}; + //static char const _orangepal[]={0x0,0x0,0xD1,0x0,0xD2,0x0,0xD3,0x0,0xD4,0x0,0x0,0x0,0x0,0x0,0xD0,0x0}; + //static char const _bluepal[]= {0x0,0x0,0x02,0x0,0x0A,0x0,0x0B,0x0,0x0B,0x0,0x0,0x0,0x0,0x0,0x09,0x0}; + //static char const _yellowpal[]={0x0,0x0,0x05,0x0,0xEE,0x0,0xF1,0x0,0xF2,0x0,0x0,0x0,0x0,0x0,0x7D,0x0}; + + static char const *_colors[]={_yellowpal,_redpal,_bluepal,_orangepal,_greenpal,_graypal}; + + int i,k; + void *oldfont, *anim; + int oldfontxspacing = FontXSpacing; + char const *pal; + + + FontXSpacing = 0; + Map.Override_Mouse_Shape(MOUSE_NORMAL); + Theme.Queue_Song(THEME_WIN1); + + PseudoSeenBuff = new GraphicBufferClass(320,200,(void*)NULL); + TextPrintBuffer = new GraphicBufferClass(SeenBuff.Get_Width(), SeenBuff.Get_Height() ,(void*)NULL); + BlitList.Clear(); + + SysMemPage.Clear(); + PseudoSeenBuff->Clear(); + HiddenPage.Clear(); + TextPrintBuffer->Clear(); + + Set_Palette(BlackPalette); + + anim = Open_Animation("MLTIPLYR.WSA",NULL,0L,(WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE),Palette); + Hide_Mouse(); + + /* + ** Display the background animation + */ + VisiblePage.Clear(); + InterpolationPaletteChanged = TRUE; + InterpolationPalette = Palette; + Increase_Palette_Luminance (Palette , 30,30,30,63); + Animate_Frame(anim, *PseudoSeenBuff, 1); + Interpolate_2X_Scale( PseudoSeenBuff , &SeenBuff , "MULTSCOR.PAL"); + Fade_Palette_To(Palette, FADE_PALETTE_FAST, Call_Back); + + int frame = 1; + while (frame < Get_Animation_Frame_Count(anim)) { + Animate_Frame(anim, *PseudoSeenBuff, frame++); + Call_Back_Delay(2); + } + Close_Animation(anim); + + /* Change to the six-point font for Text_Print */ + oldfont = Set_Font(ScoreFontPtr); + Call_Back(); + + Set_Logic_Page(*PseudoSeenBuff); + + /* + ** Move all the scores over a notch if there's more games than can be + ** shown (which is known by MPlayerCurGame == MAX_MULTI_GAMES-1); + */ + if (MPlayerCurGame == MAX_MULTI_GAMES-1) { + for(i = 0; i < MAX_MULTI_NAMES; i++) { + for(k = 0; k < MAX_MULTI_GAMES-1; k++) { + MPlayerScore[i].Kills[k] = MPlayerScore[i].Kills[k+1]; + } + } + } + + int y = 41; + for(i = 0; i < MAX_MULTI_NAMES; i++) { + if (strlen(MPlayerScore[i].Name)) { + pal = _colors[MPlayerScore[i].Color]; + + Alloc_Object(new ScorePrintClass(MPlayerScore[i].Name, 15, y,pal)); + Call_Back_Delay(20); + + Alloc_Object(new ScorePrintClass(Int_Print(MPlayerScore[i].Wins), 118, y, pal)); + Call_Back_Delay(6); + + for(k = 0; k <= MIN(MPlayerCurGame, MAX_MULTI_GAMES-2); k++) { + if (MPlayerScore[i].Kills[k] >= 0) { + Alloc_Object(new ScorePrintClass(Int_Print(MPlayerScore[i].Kills[k]), 225+(24*k), y, pal)); + Call_Back_Delay(6); + } + } + y += 12; + } + } + +#if (FRENCH) + Alloc_Object(new ScorePrintClass(TXT_MAP_CLICK2, 90 /*(320-strlen(Text_String(TXT_MAP_CLICK2)))/2*/, 185, _cycleyellowpal)); +#else + Alloc_Object(new ScorePrintClass(TXT_MAP_CLICK2, 109 /*(320-strlen(Text_String(TXT_MAP_CLICK2)))/2*/, 185, _cycleyellowpal)); +#endif + Cycle_Wait_Click(); + +/* get rid of all the animating objects */ + for (i = 0; i < MAXSCOREOBJS; i++) if (ScoreObjs[i]) { + delete ScoreObjs[i]; + ScoreObjs[i] = 0; + } + + Theme.Queue_Song(THEME_NONE); + + Fade_Palette_To(BlackPalette, FADE_PALETTE_FAST, NULL); + VisiblePage.Clear(); + Set_Palette(GamePalette); + + Set_Logic_Page (SeenBuff); + + delete PseudoSeenBuff; + delete TextPrintBuffer; + BlitList.Clear(); + + Set_Font(oldfont); + FontXSpacing = oldfontxspacing; + ControlQ = 0; + Show_Mouse(); +} + +#ifdef WRITE_LBM + // A BitMapHeader is stored in a BMHD chunk. This structure MUST be an even size +typedef struct { + unsigned short w, h; // raster width & height in pixels + unsigned short x, y; // position for this image + unsigned char planes; // # source bitplanes + unsigned char masking; // masking technique + unsigned char compression; // compression algoithm + unsigned char pad1; // UNUSED. For consistency, put 0 here. + unsigned short transcolor; // transparent "color number" + unsigned char xaspect, yaspect; // aspect ratio, a rational number x/y + unsigned short pagewidth, pageheight; // source "page" size in pixels +} BitMapHeaderType; + +// All values in LocalHeader are always the same except planes. This is set in Write_BMHD +// the WORD values must be in low-high order for compatibility. + +static BitMapHeaderType LocalHeader = { + 0x4001, 0xc800, 0, 0, 0, 0, // width, height, x, y, planes, mask + 1, 0, 0xFF00, 5, 6, // compress, pad1, transcolor, xasptect, yaspect + 0x4001, 0xC800 }; // pagewidth, pageheight + +#define BM_HEADER_SIZE (((sizeof(BitMapHeaderType) + 1) & 0xFFFE) + 8L) + +/*=========================================================================*/ +/* The following static functions are in this file: */ +/*=========================================================================*/ + +static long CCWrite_BMHD(CCFileClass &lbmhandle, short bitplanes); +static long CCWrite_CMAP(CCFileClass &lbmhandle, unsigned char * palette, short bitplanes); +static long CCWrite_BODY(CCFileClass &lbmhandle, BufferClass& buff, short bitplanes); +static long CCWrite_Row(CCFileClass &lbmhandle, unsigned char *buffer); + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * CCWrite_LBM_File -- Writes out a file in LBM format * + * * + * INPUT: short lbmhandle -- lbm file handle already opened by caller * + * BufferClass buff -- buff where MCGA picture is * + * short bitplane -- number of bitplanes to convert to * + * char *palette -- pointer to palette for buff * + * * + * OUTPUT: Returns bool -- successfull or not * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/18/1991 SB : Created. * + *=========================================================================*/ +bool CCWrite_LBM_File(CCFileClass &lbmhandle, BufferClass& buff, short bitplanes, unsigned char *palette) +{ + long filesize; + + + lbmhandle.Seek(0L, SEEK_SET); // goto beginning of file + + lbmhandle.Write("FORM????ILBM", 12L); // First 12 bytes of all .lbm files + // size is unkown so write ???? + filesize = 12L; // 4 bytes for "ILBM" + + filesize += CCWrite_BMHD(lbmhandle, bitplanes); // write out BMHD (fixed size) + filesize += CCWrite_CMAP(lbmhandle, palette, bitplanes); // write out CMAP + + // Write out the body, or compressed picture image. This size will depend + // on the compression, but the value passed back is what the compressor + // assumed was written to file + + filesize += CCWrite_BODY(lbmhandle, buff, bitplanes); + + // Verify that we were able to write out the file without running out of space + if (lbmhandle.Seek(0L, SEEK_END) != filesize) { + return(false); + } + + lbmhandle.Seek(4L, SEEK_SET); // goto beginning of file + filesize = Reverse_LONG(filesize - 8L); // - 8 because of "FORM" + short (size) + lbmhandle.Write( (char *) &filesize, 4L); // patch in filesize + + return(true); +} + + +/*************************************************************************** + * CCWrite_BMHD -- writes out the bit map header (LocalHeader) * + * * + * INPUT: short lbmhandle -- file handle for lbm file * + * short pitplanes -- number of bitplanes to write out * + * * + * OUTPUT: LONG number of bytes hopefully written out to .LBM file * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/19/1991 SB : Created. * + *=========================================================================*/ +static long CCWrite_BMHD(CCFileClass &lbmhandle, short bitplanes) +{ + long size; + + lbmhandle.Write("BMHD", 4L); // write out chunk title + size = Reverse_LONG(sizeof(LocalHeader)); // write out size of LocalHeader chunk + lbmhandle.Write((char *) &size, 4L); + + LocalHeader.planes = bitplanes; // only nonconstant value in LocalHeader + + // Make sure size is even. Return 8 = "BMHD" + size of the bitmap header structure + + return(lbmhandle.Write((char *) &LocalHeader, + (sizeof(LocalHeader) + 1) & 0xFFFE) + 8L); +} + + +/*************************************************************************** + * CCWrite_CMAP -- Writes out CMAP (palette) information * + * * + * * + * INPUT: short lbmhandle -- file handle of lbm file * + * char * palette -- pointer to paletter information * + * short bitplanes -- used to figure out size of palette * + * * + * OUTPUT: long number of bytes that should have been written out to .LBM. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/19/1991 SB : Created. * + *=========================================================================*/ +static long CCWrite_CMAP(CCFileClass &lbmhandle, unsigned char * palette, short bitplanes) +{ + short color, r, g, b, colors; + long size; + unsigned char *pal_ptr; + char rgb[3]; + + + lbmhandle.Write("CMAP", 4L); // write out palette info + colors = 1 << bitplanes; // colors = 2 to the bitplanes + size = Reverse_LONG(colors * 3L); // size = colors * 3 guns + + lbmhandle.Write((char *) &size, 4L); + + for (pal_ptr = palette, color = 0; color < colors; color++) { // for each color + + if ((r = *pal_ptr++) != 0) { // DPaint changes allows 0 - 100 for gun values + r = (r << 2) | 0x03; // this must be converted to 0 - 256 for LBM + } // so LBM_val = (DP_val * 4) | 3 if DP_val != 0 + if ((g = *pal_ptr++) != 0) { + g = (g << 2) | 0x03; + } + if ((b = *pal_ptr++) != 0) { + b = (b << 2) | 0x03; + } + rgb[0] = r; // assign gun values to an array to write out + rgb[1] = g; + rgb[2] = b; + + lbmhandle.Write(rgb, 3L); + } + // size = colors * 3 + return(((colors << 1) + colors) + 8L); // total size of CMAP 8 = "CMAP" + short (size) +} + + +/*************************************************************************** + * CCWrite_Body -- writes out compressed data in an LBM file * + * * + * INPUT: short lbmhandle -- file handle of lbm file * + * * + * OUTPUT: long - number of byte written * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/19/1991 SB : Created. * + *=========================================================================*/ +static long CCWrite_BODY(CCFileClass &lbmhandle, BufferClass& buff, short bitplanes) +{ + long bodysize = 0; + long actualsize; + long size; + short planebit; + short line, plane; + unsigned char buffer[40]; + unsigned char *buffptr; + + lbmhandle.Write("BODY????", 8L); // BODY chunk ID, ???? reserved for chuncksize + + buffptr = (unsigned char *) buff.Get_Buffer(); // point to beginning of buff + + for (line = 0; line < 200; line++) { + planebit = 1; // start with bit 1 set + + for (plane = 0; plane < bitplanes; plane++) { + Pack_2_Plane(buffer, buffptr, planebit); // convert to planar + bodysize += CCWrite_Row(lbmhandle, buffer); // write to to the BODY in the LBM + + planebit <<= 1; // set next bit + } + + buffptr += 320; // row size is 320 + } + + actualsize = bodysize + (bodysize&0x01); + + if (actualsize != bodysize) { + lbmhandle.Write(buffer, 1); // Padd the block. + } + + lbmhandle.Seek( -(actualsize + 4L), SEEK_CUR); // Patch in chunksize + size = Reverse_LONG(bodysize); + lbmhandle.Write( (char *) &size ,4L); + + return(actualsize + 8L); // total size of BODY, "BODY????" = 8 bytes +} + + +/*************************************************************************** + * CCWrite_Row -- compresses and writes a row plane to .lbm file * + * * + * INPUT: short lbmhandle -- lbm file handle * + * unsigned char *buffer -- pointer to buffer to be written out * + * * + * OUTPUT: long size of chunk that should have been written out * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/19/1991 SB : Created. * + *=========================================================================*/ +static long CCWrite_Row(CCFileClass &lbmhandle, unsigned char *buffer) +{ + short i; + short chunksize = 0; + short dataLength = 40; // 320 rows / 8 ( 1 plane per row) + unsigned char repCode, current, curr_plus_2; + unsigned char *buffptr; + + while (dataLength) { + + // If at least 2 more bytes and they are equal, then replicate + + if ((dataLength >= 2) && (buffer[0] == buffer[1])) { + buffptr = buffer; + for (i = 0; (i <= 128) && (i < (dataLength - 1)); i++) { + if (*buffptr != buffptr[1]) { + break; + } + buffptr++; + } + i++; + repCode = -i + 1; + lbmhandle.Write(&repCode, 1L); // Write count as -count+1 + lbmhandle.Write(buffer, 1L); // Write byte to replicate + buffer += i; + dataLength -= i; + chunksize += 2; + + } else { // Copy literally till 3 byte run or two 2 byte runs found + + for (i = 0; (i <= 128) && (i < dataLength); i++) { + current = buffer[i]; + curr_plus_2 = buffer[i + 2]; + + if (i == dataLength - 1) + continue; + if (current != buffer[i + 1]) + continue; + if (i == dataLength - 2) + continue; + if (current == curr_plus_2) + break; + if (i == dataLength - 3) + continue; + if (curr_plus_2 == buffer[i + 3]) + break; + } + repCode = i - 1; + lbmhandle.Write(&repCode, 1L); // Write count as count-1 + lbmhandle.Write(buffer, (long) i); // Write 'count' bytes + buffer += i; + dataLength -= i; + chunksize += i + 1; + } + } // end while + + return(chunksize); +} +#endif + diff --git a/SCORE.H b/SCORE.H new file mode 100644 index 0000000..dfeba99 --- /dev/null +++ b/SCORE.H @@ -0,0 +1,159 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\score.h_v 2.18 16 Oct 1995 16:46:18 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SCORE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 19, 1994 * + * * + * Last Update : April 19, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SCORE_H +#define SCORE_H + +#include "unit.h" +#include "building.h" + +class ScoreClass { + public: + int Score; + int NKilled; + int GKilled; + int CKilled; + int NBKilled; + int GBKilled; + int CBKilled; + int NHarvested; + int GHarvested; + int CHarvested; + unsigned long ElapsedTime; + + void Init(void) {memset(this, 0, sizeof(ScoreClass));}; + void Presentation(void); + + /* + ** File I/O. + */ + bool Load(FileClass & file); + bool Save(FileClass & file); + void Code_Pointers(void); + void Decode_Pointers(void); + + protected: + + private: + unsigned char *ChangingGun; + + void ScoreDelay(int ticks); + void Pulse_Bar_Graph(void); + void Print_Graph_Title(int,int); + void Print_Minutes(int minutes); + void Count_Up_Print(char *str, int percent, int max, int xpos, int ypos); + void Show_Credits(int house, char const pal[]); + void Do_GDI_Graph(void const * yellowptr, void const * redptr, int gdikilled, int nodkilled, int ypos); + void Do_Nod_Casualties_Graph(void); + void Do_Nod_Buildings_Graph(void); + void Input_Name(char str[], int xpos, int ypos, char const pal[]); +}; + +class ScoreAnimClass { + public: + ScoreAnimClass(int x, int y, void const * data); + int XPos; + int YPos; + CountDownTimerClass Timer; + void const * DataPtr; + virtual void Update(void) {} ; + virtual ~ScoreAnimClass(void) {} ; +}; + +class ScoreCredsClass : public ScoreAnimClass { + public: + int Stage; + int MaxStage; + int TimerReset; + void const * CashTurn; + void const * Clock1; + + virtual void Update(void); + ScoreCredsClass(int xpos, int ypos, void const * data, int max, int timer); + virtual ~ScoreCredsClass(void) {}; +}; + +class ScoreTimeClass : public ScoreAnimClass { + public: + int Stage; + int MaxStage; + int TimerReset; + virtual void Update(void); + ScoreTimeClass(int xpos, int ypos, void const * data, int max, int timer); + virtual ~ScoreTimeClass(void) {}; +}; + +class ScorePrintClass : public ScoreAnimClass { + public: + int Background; + int Stage; + void const * PrimaryPalette; + virtual void Update(void); + ScorePrintClass(void const * string, int xpos, int ypos, void const * palette, int background=TBLACK); + ScorePrintClass( int string, int xpos, int ypos, void const * palette, int background=TBLACK); + virtual ~ScorePrintClass(void) {}; +}; + + +class MultiStagePrintClass : public ScoreAnimClass { + public: + int Background; + int Stage; + void const * PrimaryPalette; + virtual void Update(void); + MultiStagePrintClass(void const * string, int xpos, int ypos, void const * palette, int background=TBLACK); + MultiStagePrintClass( int string, int xpos, int ypos, void const * palette, int background=TBLACK); + virtual ~MultiStagePrintClass(void) {}; +}; + + +class ScoreScaleClass : public ScoreAnimClass { + public: + int Stage; + char const * Palette; + virtual void Update(void); + ScoreScaleClass(void const * data, int xpos, int ypos, char const pal[]); + virtual ~ScoreScaleClass(void) {}; + +}; + +#define MAXSCOREOBJS 8 +extern ScoreAnimClass *ScoreObjs[MAXSCOREOBJS]; + +void Multi_Score_Presentation(void); + +#endif diff --git a/SCREEN.H b/SCREEN.H new file mode 100644 index 0000000..c948cbb --- /dev/null +++ b/SCREEN.H @@ -0,0 +1,68 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\screen.h_v 2.15 16 Oct 1995 16:47:38 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SCREEN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 2, 1994 * + * * + * Last Update : June 2, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SCREEN_H +#define SCREEN_H + + +class ScreenClass +{ + /* + ** The mouse shape is controlled by these variables. These + ** hold the current mouse shape (so resetting won't be needlessly performed) and + ** the normal default mouse shape (when arrow shapes are needed). + */ + MouseShapeType CurrentMouseShape; + MouseShapeType NormalMouseShape; + + public: + + ScreenClass(void) { + CurrentMouseShape = SHP_NONE; + NormalMouseShape = SHP_MOUSE; + }; + + + Init(void); + Set_Default_Mouse(MouseShapeType mouse); + Force_Mouse_Shape(MouseShapeType mouse); + + unsigned char *GamePalette; + unsigned char *BlackPalette; +}; + +#endif diff --git a/SCROLL.CPP b/SCROLL.CPP new file mode 100644 index 0000000..6fd0347 --- /dev/null +++ b/SCROLL.CPP @@ -0,0 +1,256 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\scroll.cpv 2.18 16 Oct 1995 16:49:08 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SCROLL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/08/95 * + * * + * Last Update : August 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ScrollClass::AI -- Handles scroll AI processing. * + * ScrollClass::ScrollClass -- Constructor for the scroll class object. * + * ScrollClass::Set_Autoscroll -- Turns autoscrolling on or off. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +#define SCROLL_DELAY 1 + +CountDownTimerClass ScrollClass::Counter; + + +/*********************************************************************************************** + * ScrollClass::ScrollClass -- Constructor for the scroll class object. * + * * + * This is the constructor for the scroll class object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +ScrollClass::ScrollClass(void) +{ + IsAutoScroll = true; + Counter.Set(SCROLL_DELAY); + Inertia = 0; + Counter.Start(); +} + + +/*********************************************************************************************** + * ScrollClass::AI -- Handles scroll AI processing. * + * * + * This routine is called every game frame for purposes of input processing. * + * * + * INPUT: input -- Reference to the keyboard/mouse event that just occurred. * + * * + * x,y -- The mouse coordinates. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + * 08/10/1995 JLB : Revamped for free smooth scrolling. * + * 08/25/1995 JLB : Handles new scrolling option. * + *=============================================================================================*/ +#define EVA_WIDTH 80 +void ScrollClass::AI(KeyNumType &input, int x, int y) +{ + static DirType direction; + bool player_scrolled=false; + + /* + ** If rubber band mode is in progress, then don't allow scrolling of the tactical map. + */ + if (!IsRubberBand /*&& !IsTentative*/) { + + /* + ** Special check to not scroll within the special no-scroll regions. + */ + bool noscroll = false; + if (Special.IsScrollMod && y == 0 && ((x > 3 && x < EVA_WIDTH) || (x > SeenBuff.Get_Width()-EVA_WIDTH && x < SeenBuff.Get_Width()-3))) { + noscroll = true; + } + + if (!noscroll) { + + /* + ** Verify that the mouse is over a scroll region. + */ + if (Inertia || y == 0 || x == 0 || x == (SeenBuff.Get_Width()-1) || y == (SeenBuff.Get_Height()-1)) { + + if (y == 0 || x == 0 || x == (SeenBuff.Get_Width()-1) || y == (SeenBuff.Get_Height()-1)) { + + player_scrolled=true; + /* + ** Adjust the mouse coordinates to emphasise the + ** cardinal directions over the diagonals. + */ + int altx = x; + if (altx < 50) altx -= (50-altx)*2; + altx = MAX(altx, 0); + if (altx > (SeenBuff.Get_Width()-50)) altx += (altx-(SeenBuff.Get_Width()-50))*2; + altx = MIN(altx, SeenBuff.Get_Width()); + if (altx > 50 && altx < (SeenBuff.Get_Width()-50)) { + altx += ((SeenBuff.Get_Width()/2)-altx)/2; + } + + int alty = y; + if (alty < 50) alty -= (50-alty); + alty = MAX(alty, 0); + if (alty > (SeenBuff.Get_Height()-50)) alty += ((alty-(SeenBuff.Get_Height()-50))); + alty = MIN(alty, SeenBuff.Get_Height()); + + direction = (DirType)Desired_Facing256((SeenBuff.Get_Width())/2, (SeenBuff.Get_Height())/2, altx, alty); + } + int control = Dir_Facing(direction); + + /* + ** The mouse is over a scroll region so set the mouse shape accordingly if the map + ** can be scrolled in the direction indicated. + */ + static int _rate[9] = { + 0x01C0, + 0x0180, + 0x0140, + 0x0100, + 0x00C0, + 0x0080, + 0x0040, + 0x0020, + 0x0010 + }; + + int rate = 8-Inertia; + + if (rate. +*/ + +/* $Header: F:\projects\c&c\vcs\code\scroll.h_v 2.16 16 Oct 1995 16:46:12 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SCROLL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/18/94 * + * * + * Last Update : November 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SCROLL_H +#define SCROLL_H + +#include "help.h" + + +class ScrollClass: public HelpClass +{ + /* + ** If map scrolling is automatic, then this flag is true. Automatic scrolling will + ** cause the map to scroll if the mouse is in the scroll region, regardless of + ** whether or not the mouse button is held down. + */ + unsigned IsAutoScroll:1; + + /* + ** Scroll speed is regulated by this count down timer. When this value reaches zero, + ** scroll the map in the direction required and reset this timer. + */ + static CountDownTimerClass Counter; + + /* + ** These are the delays used (as countdown timers) to regulate the scroll rate + ** of the map. These times are based on game ticks. + */ +// enum ScrollClassEnums { +// INITIAL_DELAY=8, // Delay before scrolling can start at all. +// SEQUENCE_DELAY=4 // Delay between scroll steps. +// }; + + int Inertia; + + + public: + ScrollClass(void); + + bool Set_Autoscroll(int control); + + virtual void AI(KeyNumType &input, int x, int y); + virtual void Init_IO(void) {Counter.Set(0);HelpClass::Init_IO();}; + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); +}; + +#endif diff --git a/SDATA.CPP b/SDATA.CPP new file mode 100644 index 0000000..c6c38c2 --- /dev/null +++ b/SDATA.CPP @@ -0,0 +1,473 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\sdata.cpv 2.17 16 Oct 1995 16:52:10 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 9, 1994 * + * * + * Last Update : August 12, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SmudgeTypeClass::Draw_It -- Renders the smudge image at the coordinate specified. * + * SmudgeTypeClass::Create_One_Of -- Creates a smudge object of this type. * + * SmudgeTypeClass::Create_And_Place -- Creates and places on map, a smudge object. * + * SmudgeTypeClass::Prep_For_Add -- Prepares the scenario editor for adding a smudge object. * + * SmudgeTypeClass::Init -- Performs theater specific initializations. * + * SmudgeTypeClass::Display -- Draws a generic version of this smudge type. * + * SmudgetypeClass::Occupy_List -- Determines occupation list for smudge object. * + * SmudgeTypeClass::From_Name -- Converts an ASCII name into a smudge type. * + * SmudgeTypeClass::SmudgeTypeClass -- Constructor for smudge type objects. * + * SmudgeTypeClass::One_Time -- Performs one-time initialization * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "type.h" + + +static SmudgeTypeClass const Crater1 ( + + SMUDGE_CRATER1, + "CR1", + TXT_CRATER, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + true // Is this a crater smudge? +); +static SmudgeTypeClass const Crater2 ( + SMUDGE_CRATER2, + "CR2", + TXT_CRATER, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + true // Is this a crater smudge? +); +static SmudgeTypeClass const Crater3 ( + SMUDGE_CRATER3, + "CR3", + TXT_CRATER, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + true // Is this a crater smudge? +); +static SmudgeTypeClass const Crater4 ( + SMUDGE_CRATER4, + "CR4", + TXT_CRATER, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + true // Is this a crater smudge? +); +static SmudgeTypeClass const Crater5 ( + SMUDGE_CRATER5, + "CR5", + TXT_CRATER, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + true // Is this a crater smudge? +); +static SmudgeTypeClass const Crater6 ( + SMUDGE_CRATER6, + "CR6", + TXT_CRATER, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + true // Is this a crater smudge? +); +static SmudgeTypeClass const Scorch1 ( + SMUDGE_SCORCH1, + "SC1", + TXT_SCORCH, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Scorch2 ( + SMUDGE_SCORCH2, + "SC2", + TXT_SCORCH, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Scorch3 ( + SMUDGE_SCORCH3, + "SC3", + TXT_SCORCH, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Scorch4 ( + SMUDGE_SCORCH4, + "SC4", + TXT_SCORCH, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Scorch5 ( + SMUDGE_SCORCH5, + "SC5", + TXT_SCORCH, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Scorch6 ( + SMUDGE_SCORCH6, + "SC6", + TXT_SCORCH, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + false // Is this a crater smudge? +); + +static SmudgeTypeClass const Bibx1 ( + SMUDGE_BIB1, + "BIB1", + TXT_BIB, + 4,2, // Width and height of smudge (in icons). + true, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Bibx2 ( + SMUDGE_BIB2, + "BIB2", + TXT_BIB, + 3,2, // Width and height of smudge (in icons). + true, // Is this a building bib? + false // Is this a crater smudge? +); + +/* +** The watcom code optimiser screws up the last constructor call. Making it 'volatile' reduces the +** level of optimisation enough for the problem not to manifest. +*/ +volatile SmudgeTypeClass const Bibx3 ( + SMUDGE_BIB3, + "BIB3", + TXT_BIB, + 2,2, // Width and height of smudge (in icons). + true, // Is this a building bib? + false // Is this a crater smudge? +); + +/* +** Working array to the smudge control objects types. This routine is +** used for quick conversion from a SmudgeType number into an actual +** smudge type object pointer. +*/ +SmudgeTypeClass const * const SmudgeTypeClass::Pointers[SMUDGE_COUNT] = { + &Crater1, // SMUDGE_CRATER1 + &Crater2, // SMUDGE_CRATER2 + &Crater3, // SMUDGE_CRATER3 + &Crater4, // SMUDGE_CRATER4 + &Crater5, // SMUDGE_CRATER5 + &Crater6, // SMUDGE_CRATER6 + &Scorch1, // SMUDGE_SCORCH1 + &Scorch2, // SMUDGE_SCORCH2 + &Scorch3, // SMUDGE_SCORCH3 + &Scorch4, // SMUDGE_SCORCH4 + &Scorch5, // SMUDGE_SCORCH5 + &Scorch6, // SMUDGE_SCORCH6 + &Bibx1, // SMUDGE_BIB1 + &Bibx2, // SMUDGE_BIB2 + (SmudgeTypeClass const * const)&Bibx3 // SMUDGE_BIB3 +}; + + +/*********************************************************************************************** + * SmudgeTypeClass::SmudgeTypeClass -- Constructor for smudge type objects. * + * * + * This constructor is used to create the smudge type objects. These type objects contain * + * static information about the various smudge types supported in the game. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +SmudgeTypeClass::SmudgeTypeClass(SmudgeType smudge, char const *ininame, int fullname, int width, int height, bool isbib, bool iscrater) : + ObjectTypeClass(false, false, false, true, false, false, true, true, fullname, ininame, ARMOR_NONE, 0) +{ + IsBib = isbib; + Width = width; + Height = height; + IsCrater = iscrater; + Type = smudge; +} + +/*********************************************************************************************** + * SmudgeTypeClass::From_Name -- Converts an ASCII name into a smudge type. * + * * + * This converts an ASCII name into a smudge type number. This is typically necessary * + * when processing scenario INI files and not used otherwise. * + * * + * INPUT: name -- Pointer to the name to convert. * + * * + * OUTPUT: Returns with the SmudgeType number that matches the name supplied. If no match * + * was found, then SMUDGE_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +SmudgeType SmudgeTypeClass::From_Name(char const *name) +{ + if (name) { + for (SmudgeType index = SMUDGE_FIRST; index < SMUDGE_COUNT; index++) { + if (stricmp(As_Reference(index).IniName, name) == 0) { + return(index); + } + } + } + return(SMUDGE_NONE); +} + + +/*********************************************************************************************** + * SmudgetypeClass::Occupy_List -- Determines occupation list for smudge object. * + * * + * Smudges are always only one icon in dimension, so this routine always returns a cell * + * occupation offset list of the center cell. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns occupation list specifying all the cells that the overlay occupies. This * + * is just the center cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +short const * SmudgeTypeClass::Occupy_List(bool) const +{ + static short _occupy[4*4]; + short * ptr = &_occupy[0]; + + for (int x = 0; x < Width; x++) { + for (int y = 0; y < Height; y++) { + *ptr++ = x + (y*MAP_CELL_W); + } + } + *ptr = REFRESH_EOL; + return(_occupy); +} + + +/*********************************************************************************************** + * SmudgeTypeClass::Init -- Performs theater specific initializations. * + * * + * Smudge object imagery varies between theaters. This routine will load the appropriate * + * imagery for the theater specified. * + * * + * INPUT: theater -- The theater to prepare for. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::Init(TheaterType theater) +{ + if (theater != LastTheater){ + for (SmudgeType index = SMUDGE_FIRST; index < SMUDGE_COUNT; index++) { + SmudgeTypeClass const & smudge = As_Reference(index); + char fullname[_MAX_FNAME+_MAX_EXT]; // Fully constructed smudge data set name. + + _makepath(fullname, NULL, NULL, smudge.IniName, Theaters[theater].Suffix); + ((void const *&)smudge.ImageData) = MixFileClass::Retrieve(fullname); + } + } +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * SmudgeTypeClass::Display -- Draws a generic version of this smudge type. * + * * + * The scenario object editor will call this routine to display a typical imagery of this * + * smudge object for graphical identification purposes. * + * * + * INPUT: x,y -- Coordinate to render the smudge at. * + * * + * window-- The window to base the coordinate rendering upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::Display(int x, int y, WindowNumberType window, HousesType ) const +{ + void const *ptr = Get_Image_Data(); + + x += WindowList[window][WINDOWX] << 3; + y += WindowList[window][WINDOWY]; + + if (ptr) { + for (int w = 0; w < Width; w++) { + for (int h = 0; h < Height; h++) { + CC_Draw_Shape(ptr, 0, x + w*ICON_PIXEL_W, y + h*ICON_PIXEL_H, WINDOW_TACTICAL, SHAPE_WIN_REL); + //LogicPage->Draw_Stamp(ptr, w + (h*Width), x + w*ICON_PIXEL_W, y + h*ICON_PIXEL_H, NULL, WINDOW_TACTICAL); + } + } + } +} + + +/*********************************************************************************************** + * SmudgeTypeClass::Prep_For_Add -- Prepares the scenario editor for adding a smudge object. * + * * + * This routine adds smudge objects to the list of objects that the scenario editor can * + * place upon the ground. It is only called from the scenario editor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::Prep_For_Add(void) +{ + for (SmudgeType index = SMUDGE_FIRST; index < SMUDGE_COUNT; index++) { + if (As_Reference(index).Get_Image_Data()) { + Map.Add_To_List(&As_Reference(index)); + } + } +} +#endif + + +/*********************************************************************************************** + * SmudgeTypeClass::Create_And_Place -- Creates and places on map, a smudge object. * + * * + * This routine will, in one motion, create a smudge object and place it upon the map. * + * Since placing a smudge on the map will destroy the object, this routine will leave the * + * smudge object count unchanged. Typically, this routine is used by the scenario editor * + * for creating smudges and placing them on the map. * + * * + * INPUT: cell -- The cell to place the smudge object. * + * * + * OUTPUT: bool; Was the placement successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +bool SmudgeTypeClass::Create_And_Place(CELL cell, HousesType ) const +{ + if (new SmudgeClass(Type, Cell_Coord(cell))) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SmudgeTypeClass::Create_One_Of -- Creates a smudge object of this type. * + * * + * This routine will create a smudge object of the appropriate type. Smudge objects are * + * transitory in nature. They exist only from the point of creation until they are given * + * a spot on the map to reside. At that time the map data is updated and the smudge * + * object is destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to a created smudge object. If none could be created, then * + * NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * SmudgeTypeClass::Create_One_Of(HouseClass *) const +{ + return(new SmudgeClass(Type, -1)); +} + + +/*********************************************************************************************** + * SmudgeTypeClass::Draw_It -- Renders the smudge image at the coordinate specified. * + * * + * This routine will draw the smudge overlay image at the coordinate (upper left) * + * specified. The underlying terrain icon is presumed to have already been rendered. * + * * + * INPUT: x,y -- Coordinate of the upper left corner of icon to render the smudge object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::Draw_It(int x, int y, int data) const +{ + void const * ptr = Get_Image_Data(); + if (ptr) { + IsTheaterShape = true; // Smudges are theater specific + CC_Draw_Shape(ptr, data, x, y, WINDOW_TACTICAL, SHAPE_WIN_REL); + IsTheaterShape = false; +// LogicPage->Draw_Stamp(ptr, data, x, y, NULL, WINDOW_TACTICAL); + } +} + + +/*********************************************************************************************** + * SmudgeTypeClass::One_Time -- Performs one-time initialization * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::One_Time(void) +{ +} + diff --git a/SEQCONN.CPP b/SEQCONN.CPP new file mode 100644 index 0000000..9526802 --- /dev/null +++ b/SEQCONN.CPP @@ -0,0 +1,561 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\seqconn.cpv 1.9 16 Oct 1995 16:51:30 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SEQCONN.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : April 9, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * SequencedConnClass::SequencedConnClass -- class constructor * + * SequencedConnClass::~SequencedConnClass -- class destructor * + * SequencedConnClass::Init -- Initializes connection queue to empty * + * SequencedConnClass::Send_Packet -- adds a packet to the send queue * + * SequencedConnClass::Receive_Packet -- adds packet to receive queue * + * SequencedConnClass::Get_Packet -- gets a packet from receive queue * + * SequencedConnClass::Service_Send_Queue -- services the send queue * + * SequencedConnClass::Service_Receive_Queue -- services recieve queue * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * SequencedConnClass::SequencedConnClass -- class constructor * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the recieve queue * + * maxlen max length of an application packet * + * magicnum the packet "magic number" for this connection * + * retry_delta the time to wait between sends * + * max_retries the max # of retries allowed for a packet * + * (-1 means retry forever, based on this parameter) * + * timeout the max amount of time before we give up on a packet * + * (-1 means retry forever, based on this parameter) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +SequencedConnClass::SequencedConnClass (int numsend, int numreceive, + int maxlen, unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout) : + ConnectionClass (maxlen, magicnum, retry_delta, max_retries, timeout) +{ + NumRecNoAck = 0; + NumRecAck = 0; + NumSendNoAck = 0; + NumSendAck = 0; + + /*------------------------------------------------------------------------ + Allocate the packet Queue. This will store incoming packets (which will + be placed there by the Connection Manager), and outgoing packets (which + are placed there by this class when it "sends" a packet). + ------------------------------------------------------------------------*/ + Queue = new CommQueueClass (numsend, numreceive, MaxPacketLen); + +} /* end of SequencedConnClass */ + + +/*************************************************************************** + * SequencedConnClass::~SequencedConnClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +SequencedConnClass::~SequencedConnClass () +{ + delete Queue; +} + + +/*************************************************************************** + * SequencedConnClass::Init -- Initializes connection queue to empty * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void SequencedConnClass::Init (void) +{ + Queue->Init(); + +} /* end of Init */ + + +/*************************************************************************** + * SequencedConnClass::Send_Packet -- adds a packet to the send queue * + * * + * This routine prefixes the given buffer with a CommHeaderType and * + * queues the resulting packet into the Send Queue. (It's actually the * + * Service() routine that handles the hardware-dependent Send of the data).* + * The packet's MagicNumber, Code, and PacketID are set here. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * ack_req true = ACK is required for this packet; false = isn't * + * * + * OUTPUT: * + * 1 = packet was queue'd OK, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int SequencedConnClass::Send_Packet (void * buf, int buflen, int ack_req) +{ + /*........................................................................ + Set the magic # for the packet + ........................................................................*/ + ((CommHeaderType *)PacketBuf)->MagicNumber = MagicNum; + + /*........................................................................ + Set the packet Code: DATA_ACK if it requires an ACK, NOACK if it doesn't + Set the packet ID to the appropriate counter value. + ........................................................................*/ + if (ack_req) { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_ACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendAck; + } else { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_NOACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendNoAck; + } + + /*........................................................................ + Now build the packet + ........................................................................*/ + memcpy(PacketBuf + sizeof(CommHeaderType), buf, buflen); + + /*........................................................................ + Add it to the queue. + ........................................................................*/ + if (Queue->Queue_Send(PacketBuf,buflen + sizeof(CommHeaderType))) { + if (ack_req) { + NumSendAck++; + } else { + NumSendNoAck++; + } + return(true); + + } else { + return(false); + } +} + + +/*************************************************************************** + * SequencedConnClass::Receive_Packet -- adds packet to the receive queue * + * * + * INPUT: * + * buf buffer to process (already includes CommHeaderType) * + * buflen length of buffer to process * + * * + * OUTPUT: * + * 1 = packet was processed OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int SequencedConnClass::Receive_Packet (void * buf, int buflen) +{ + CommHeaderType *packet; // ptr to this packet + SendQueueType *send_entry; // ptr to send entry header + ReceiveQueueType *rec_entry; // ptr to receive entry header + CommHeaderType *entry_data; // ptr to queue entry data + int save_packet = 1; // 0 = this is a resend, or + // out-of-order packet + + /* + --------------------------- Check the magic # ---------------------------- + */ + packet = (CommHeaderType *)buf; + if (packet->MagicNumber!=MagicNum) + return(false); + + /*------------------------------------------------------------------------ + Process the packet based on its Code + ------------------------------------------------------------------------*/ + switch (packet->Code) { + /*..................................................................... + DATA: If this is a No-Ack packet, always save it. Otherwise, only + save it if it's received in the proper sequence. + .....................................................................*/ + case PACKET_DATA_ACK: + case PACKET_DATA_NOACK: + if (packet->Code == PACKET_DATA_NOACK) { +#ifdef DEBUG_SEQ +printf("PACKET_DATA_NOACK received (%d)\n",packet->PacketID); +#endif + save_packet = 1; + } else { +#ifdef DEBUG_SEQ +printf("PACKET_DATA_ACK received (%d)\n",packet->PacketID); +#endif + if ((packet->PacketID == NumRecAck)) { + save_packet = 1; + } else { + save_packet = 0; + /*............................................................... + If this is a resend of our next-available received message, it + means the other app didn't get our ACK, so mark it as + non-acknowledged to tell Service_Receive_Queue to send an ACK. + ...............................................................*/ + rec_entry = Queue->Next_Receive(); + if (rec_entry) { + entry_data = (CommHeaderType *)rec_entry->Buffer; + if (entry_data->PacketID==packet->PacketID && + entry_data->Code == PACKET_DATA_ACK) { + rec_entry->IsACK = 0; +#ifdef DEBUG_SEQ +printf("(Resend)\n"); +#endif + } + } + } + } + + /* + ...................... queue this packet ........................ + */ + if (save_packet) { + if (!Queue->Queue_Receive(buf, buflen)) { + return(false); + } + if (packet->Code == PACKET_DATA_ACK) { + NumRecAck++; + } else { + NumRecNoAck++; + } + } + break; + + /*..................................................................... + ACK: If this ACK is for the latest-sent packet, mark that packet as + acknowledged, then throw this packet away. Otherwise, ignore the ACK + (if we re-sent before we received the other system's first ACK, this + ACK will be a leftover) + .....................................................................*/ + case PACKET_ACK: +#ifdef DEBUG_SEQ +printf("ACK received (%d)\n",packet->PacketID); +#endif + /* + ....................... Get queue entry ptr ........................ + */ + send_entry = Queue->Next_Send(); + /* + ............... If ptr is valid, get ptr to its data ............... + */ + if (send_entry!=NULL) { + entry_data = (CommHeaderType *)send_entry->Buffer; + + /* + .............. If ACK is for this entry, mark it ................ + */ + if (packet->PacketID==entry_data->PacketID && + entry_data->Code == PACKET_DATA_ACK) { + send_entry->IsACK = 1; + } + } + break; + + /*..................................................................... + Default: ignore the packet + .....................................................................*/ + default: + break; + + } /* end of switch */ + + return(true); +} + + +/*************************************************************************** + * SequencedConnClass::Get_Packet -- gets a packet from the receive queue * + * * + * INPUT: * + * buf location to store buffer * + * buflen filled in with length of 'buf' * + * * + * OUTPUT: * + * 1 = packet was read, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int SequencedConnClass::Get_Packet (void * buf, int *buflen) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + int packetlen; // size of received packet + + /* + ------------------ Get ptr to the next available entry ------------------- + */ + rec_entry = Queue->Next_Receive(); + + /* + ------------------------ Read it if it's un-read ------------------------- + */ + if (rec_entry!=NULL && rec_entry->IsRead==0) { + /* + ........................... Mark as read .............................. + */ + rec_entry->IsRead = 1; + + /* + .......................... Copy data packet ........................... + */ + packetlen = rec_entry->BufLen - sizeof(CommHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType), packetlen); + } + (*buflen) = packetlen; + return(true); + } + + return(false); +} + + +/*************************************************************************** + * SequencedConnClass::Service_Send_Queue -- services the send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int SequencedConnClass::Service_Send_Queue (void) +{ + SendQueueType *send_entry; // ptr to send queue entry + CommHeaderType *packet_hdr; // packet header + unsigned long curtime; // current time + + /*------------------------------------------------------------------------ + - If the next packet is ACK'd remove it from the queue + - If the next packet isn't ACK'd, [re-]send it + ------------------------------------------------------------------------*/ + /* + ......................... Get ptr to data to send ........................ + */ + send_entry = Queue->Next_Send(); + if (send_entry==NULL) + return(true); + + /* + ------------------ If ACK has been received, unqueue it ------------------ + */ + if (send_entry->IsACK) { + + /* + .................. Update this queue's response time .................. + */ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_ACK) { + Queue->Add_Delay(Time() - send_entry->FirstTime); + } + + /* + ......................... unqueue the packet .......................... + */ +#ifdef DEBUG_SEQ +printf(">>>Unqueueing Receive packet #%d<<<\n",packet_hdr->PacketID); +#endif + Queue->UnQueue_Send(NULL,NULL); + } else { + + /* + ----------------- ACK not received yet, [re-]send packet ----------------- + */ + /*..................................................................... + Only send the message if time has elapsed. (The message's Time + fields are init'd to 0 when a message is queue'd or unqueue'd, so the + first time through, the delta time will appear large.) + .....................................................................*/ + curtime = Time(); + if (curtime - send_entry->LastTime > RetryDelta) { + /* + ......................... Send the message ......................... + */ + Send (send_entry->Buffer, send_entry->BufLen); + /* + ....................... Fill in Time fields ........................ + */ + send_entry->LastTime = curtime; + if (send_entry->SendCount==0) { + send_entry->FirstTime = curtime; + /*............................................................... + If this is the 1st time we're sending this packet, and it doesn't + require an ACK, mark it as ACK'd; then, the next time through, + it will just be removed from the queue. + ...............................................................*/ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_NOACK) + send_entry->IsACK = 1; + } + +#ifdef DEBUG_SEQ +packet_hdr = (CommHeaderType *)send_entry->Buffer; +if (packet_hdr->Code == PACKET_DATA_NOACK) { + printf("Sending PACKET_DATA_NOACK (%d)\n",packet_hdr->PacketID); +} else { + printf("Sending PACKET_DATA_ACK (%d)\n",packet_hdr->PacketID); +} +#endif + /* + ......................... Update SendCount ......................... + */ + send_entry->SendCount++; + /*.................................................................. + Perform error detection, based on either MaxRetries or Timeout + ..................................................................*/ + if (MaxRetries != -1 && send_entry->SendCount > MaxRetries) + return(false); + if (Timeout != -1 && send_entry->LastTime - + send_entry->FirstTime > Timeout) + return(false); + + } + } + + return(true); +} + + +/*************************************************************************** + * SequencedConnClass::Service_Receive_Queue -- services the recieve queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int SequencedConnClass::Service_Receive_Queue (void) +{ + CommHeaderType ackpacket; // ACK packet to send + ReceiveQueueType *rec_entry; // ptr to receive entry header + CommHeaderType *packet_hdr; // packet header + + /*------------------------------------------------------------------------ + Get a pointer to the next received entry + ------------------------------------------------------------------------*/ + rec_entry = Queue->Next_Receive(); + if (rec_entry==NULL) + return(true); + + /*------------------------------------------------------------------------ + If this packet doesn't require an ACK, mark it as ACK'd. + ------------------------------------------------------------------------*/ + packet_hdr = (CommHeaderType *)(rec_entry->Buffer); + if (packet_hdr->Code==PACKET_DATA_NOACK) + rec_entry->IsACK = 1; + + /*------------------------------------------------------------------------ + If this packet hasn't been ACK'd, send an ACK: + - Fill in the MagicNum & the Code + - Set the PacketID to the same ID that the sending system used, so the + sending system knows which packet the ACK is for + ------------------------------------------------------------------------*/ + if (rec_entry->IsACK==0) { +#ifdef DEBUG_SEQ +printf("Sending ACK (%d)\n",packet_hdr->PacketID); +#endif + ackpacket.MagicNumber = MagicNum; + ackpacket.Code = PACKET_ACK; + ackpacket.PacketID = packet_hdr->PacketID; + + Send((char *)&ackpacket, sizeof(CommHeaderType)); + + rec_entry->IsACK = 1; + } + + /*------------------------------------------------------------------------ + If this packet has been read by the application, and has been ACK'd, and + there is another packet in the queue behind this one, it means the other + system got the ACK we sent for this packet; remove this packet from the + queue. + ------------------------------------------------------------------------*/ + if (rec_entry!=NULL && rec_entry->IsRead && rec_entry->IsACK && + Queue->Num_Receive() > 1) { +#ifdef DEBUG_SEQ +printf(">>>Unqueueing Send packet #%d<<<\n",packet_hdr->PacketID); +#endif + Queue->UnQueue_Receive(NULL,NULL); + } + + return(true); +} + diff --git a/SEQCONN.H b/SEQCONN.H new file mode 100644 index 0000000..0883933 --- /dev/null +++ b/SEQCONN.H @@ -0,0 +1,111 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\seqconn.h_v 1.12 16 Oct 1995 16:47:58 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SEQCONN.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 9, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This class provides a "Sequenced" ACK/Retry approach to packet * + * transmission. It waits until the last packet has been ACK'd before * + * sending another packet. Thus, it guarantees order of delivery of * + * packets, but its performance will be slower than the Non-Sequenced * + * approach. * + * * + * A derived class must provide: * + * - Init: Initialization of any hardware-specific values. * + * - Send: a hardware-dependent send routine. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SEQCONN_H +#define SEQCONN_H + +#include "connect.h" +#include "comqueue.h" + + +/* +***************************** Class Declaration ***************************** +*/ +class SequencedConnClass : public ConnectionClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + SequencedConnClass (int numsend, int numrecieve, int maxlen, + unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout); + virtual ~SequencedConnClass (); + + /*..................................................................... + Initialization. + .....................................................................*/ + virtual void Init (void); + + /*..................................................................... + Send/Receive routines. + .....................................................................*/ + virtual int Send_Packet (void * buf, int buflen, int ack_req); + virtual int Receive_Packet (void * buf, int buflen); + virtual int Get_Packet (void * buf, int *buflen); + + /*..................................................................... + The packet queue. + .....................................................................*/ + CommQueueClass *Queue; + + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + /*..................................................................... + Routines to service the Send & Receive queues. + .....................................................................*/ + virtual int Service_Send_Queue (void); + virtual int Service_Receive_Queue (void); + + /*..................................................................... + Running totals of # of packets we send & receive which require an ACK, + and those that don't. + .....................................................................*/ + unsigned long NumRecNoAck; + unsigned long NumRecAck; + unsigned long NumSendNoAck; + unsigned long NumSendAck; + +}; + +#endif +/*************************** end of seqconn.h ******************************/ diff --git a/SESSION.H b/SESSION.H new file mode 100644 index 0000000..f4b27c2 --- /dev/null +++ b/SESSION.H @@ -0,0 +1,553 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SESSION.H * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 11/30/95 * + * * + * Last Update : November 30, 1995 [BRR] * + * * + * The purpose of this class is to contain those variables & routines * + * specifically related to a multiplayer game. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef SESSION_H +#define SESSION_H + +#include "ipxaddr.h" +#include "msglist.h" +#include "connect.h" + +//--------------------------------------------------------------------------- +// Forward declarations +//--------------------------------------------------------------------------- +class AircraftClass; +class AnimClass; +class BuildingClass; +class BulletClass; +class InfantryClass; +class UnitClass; +class PhoneEntryClass; +class CellClass; + +//--------------------------------------------------------------------------- +// Defines +//--------------------------------------------------------------------------- +//........................................................................... +// Various limiting values +//........................................................................... +#define MAX_PLAYERS 6 // max # of players we can have +#define MPLAYER_BUILD_LEVEL_MAX 7 // max build level in multiplay +#define MAX_MPLAYER_COLORS 6 // max # of colors + +//........................................................................... +// Max sizes of packets we want to send +// The IPX packet's size is IPX's max size (546), rounded down to accommodate +// the max number of events possible. +//........................................................................... +#define MAX_IPX_PACKET_SIZE (((546 - sizeof(CommHeaderType)) / \ + sizeof(EventClass) ) * sizeof(EventClass)) +#define MAX_SERIAL_PACKET_SIZE 200 + +//........................................................................... +// Max length of player names fields; attempt to use the constant for the +// HouseClass, if it's been defined; otherwise, define it myself. +//........................................................................... +#ifdef HOUSE_NAME_MAX +#define MPLAYER_NAME_MAX HOUSE_NAME_MAX +#else +#define MPLAYER_NAME_MAX 12 // max length of a player's name +#endif + +//........................................................................... +// Values to control the multiplayer score screen +//........................................................................... +#define MAX_MULTI_NAMES 8 // max # names (rows) on the score screen +#define MAX_MULTI_GAMES 4 // max # games (columns) on the score screen + +//........................................................................... +// Min value for MaxAhead, for both net & modem; only applies for +// COMM_PROTOCOL_MULTI_E_COMP. +//........................................................................... +#define MODEM_MIN_MAX_AHEAD 5 +#define NETWORK_MIN_MAX_AHEAD 2 + +//........................................................................... +// Send period (in frames) for COMM_PROTOCOL_MULTI_E_COMP and above +//........................................................................... +#define DEFAULT_FRAME_SEND_RATE 3 + +//........................................................................... +// Modem-specific constants +//........................................................................... +#define PORTBUF_MAX 5 // dialog field sizes +#define IRQBUF_MAX 3 +#define BAUDBUF_MAX 7 +#define INITSTRBUF_MAX 41 +#define CWAITSTRBUF_MAX 16 +#define CREDITSBUF_MAX 5 +#define PACKET_TIMING_TIMEOUT 40 // ticks b/w sending a timing packet + +//--------------------------------------------------------------------------- +// Enums +//--------------------------------------------------------------------------- +//........................................................................... +// Types of games; used to tell which protocol we're using +//........................................................................... +typedef enum GameEnum { + GAME_NORMAL, // not multiplayer + GAME_MODEM, // modem game + GAME_NULL_MODEM, // NULL-modem + GAME_IPX, // IPX Network game + GAME_INTERNET // Winsock game +} GameType; + +//........................................................................... +// Various Modem-specific enums +//........................................................................... +typedef enum DetectPortType { + PORT_VALID = 0, + PORT_INVALID, + PORT_IRQ_INUSE +} DetectPortType; + +typedef enum DialStatusType { + DIAL_CONNECTED = 0, + DIAL_NO_CARRIER, + DIAL_BUSY, + DIAL_ERROR, + DIAL_CANCELED +} DialStatusType; + +typedef enum DialMethodType { + DIAL_TOUCH_TONE = 0, + DIAL_PULSE, + DIAL_METHODS +} DialMethodType; + +typedef enum CallWaitStringType { + CALL_WAIT_TONE_1 = 0, + CALL_WAIT_TONE_2, + CALL_WAIT_PULSE, + CALL_WAIT_CUSTOM, + CALL_WAIT_STRINGS_NUM +} CallWaitStringType; + +typedef enum ModemGameType { + MODEM_NULL_HOST = 0, + MODEM_NULL_JOIN, + MODEM_DIALER, + MODEM_ANSWERER, + INTERNET_HOST = MODEM_NULL_HOST, + INTERNET_JOIN = MODEM_NULL_JOIN +} ModemGameType; + +//........................................................................... +// Commands sent over the serial Global Channel +//........................................................................... +typedef enum SerialCommandType { + SERIAL_CONNECT = 100, // Are you there? Hello? McFly? + SERIAL_GAME_OPTIONS = 101, // Hey, dudes, here's some new game options + SERIAL_SIGN_OFF = 102, // Bogus, dudes, my boss is coming; I'm outta here! + SERIAL_GO = 103, // OK, dudes, jump into the game loop! + SERIAL_MESSAGE = 104, // Here's a message + SERIAL_TIMING = 105, // timimg packet + SERIAL_SCORE_SCREEN = 106, // player at score screen + SERIAL_LOADGAME = 107, // Start the game, loading a saved game first + SERIAL_LAST_COMMAND // last command +} SerialCommandType; + +//........................................................................... +// Commands sent over the network Global Channel +//........................................................................... +typedef enum NetCommandType { + NET_QUERY_GAME, // Hey, what games are out there? + NET_ANSWER_GAME, // Yo, Here's my game's name! + NET_QUERY_PLAYER, // Hey, what players are in this game? + NET_ANSWER_PLAYER, // Yo, I'm in that game! + NET_CHAT_ANNOUNCE, // I'm at the chat screen + NET_CHAT_REQUEST, // Respond with a CHAT_ANNOUNCE, please. + NET_QUERY_JOIN, // Hey guys, can I play too? + NET_CONFIRM_JOIN, // Well, OK, if you really want to. + NET_REJECT_JOIN, // No, you can't join; sorry, dude. + NET_GAME_OPTIONS, // Hey, dudes, here's some new game options + NET_SIGN_OFF, // Bogus, dudes, my boss is coming; I'm outta here! + NET_GO, // OK, jump into the game loop! + NET_MESSAGE, // Here's a message + NET_PING, // I'm pinging you to take a time measurement + NET_LOADGAME, // start a game by loading a saved game +} NetCommandType; + +//--------------------------------------------------------------------------- +// Structures +//--------------------------------------------------------------------------- +//........................................................................... +// An entry on the score screen is defined by this structure +//........................................................................... +typedef struct { + char Name[MPLAYER_NAME_MAX]; + int Wins; + int Kills[MAX_MULTI_GAMES]; + int Color; +} MPlayerScoreType; + +//........................................................................... +// Settings for the serial port +//........................................................................... +typedef struct { + int Port; + int IRQ; + int Baud; + DialMethodType DialMethod; + int InitStringIndex; + int CallWaitStringIndex; + char CallWaitString[ CWAITSTRBUF_MAX ]; +} SerialSettingsType; + +//........................................................................... +// This is a "node", used for the lists of available games & players. The +// 'Game' structure is used for games; the 'Player' structure for players. +//........................................................................... +typedef struct NodeNameTag { + char Name[MPLAYER_NAME_MAX]; // player or game name + IPXAddressClass Address; + union { + struct { + unsigned char IsOpen; // is the game open? + unsigned long LastTime; // last time we heard from this guy + } Game; + struct { + HousesType House; // "ActLike" House of this player + unsigned char Color; // Color of this player + HousesType ID; // Actual House of this player + } Player; + struct { + unsigned long LastTime; // last time we heard from this guy + unsigned char LastChance; // we're about to remove him from the list + unsigned char Color; // chat player's color + } Chat; + }; +} NodeNameType; + +//........................................................................... +// Packet sent over the serial Global Channel +//........................................................................... +typedef struct { + SerialCommandType Command; // One of the enum's defined above + char Name[MPLAYER_NAME_MAX]; // Player or Game Name + unsigned long MinVersion; // min version this game supports + unsigned long MaxVersion; // max version this game supports + HousesType House; // player's House + unsigned char Color; // player's color or SIGNOFF ID + unsigned char Scenario; // Scenario # + unsigned int Credits; // player's credits + unsigned int IsBases : 1; // 1 = bases are allowed + unsigned int IsTiberium : 1; // 1 = tiberium is allowed + unsigned int IsGoodies : 1; // 1 = goodies are allowed + unsigned int IsGhosties : 1; // 1 = ghosts are allowed + unsigned char BuildLevel; // buildable level + unsigned char UnitCount; // max # units + int Seed; // random number seed + SpecialClass Special; // command-line options + unsigned int GameSpeed; // Game Speed + unsigned long ResponseTime; // packet response time + char Message[COMPAT_MESSAGE_LENGTH]; // inter-player message + unsigned char ID; // unique ID of sender of message +} SerialPacketType; + +//........................................................................... +// Packet sent over the network Global Channel +//........................................................................... +typedef struct { + NetCommandType Command; // One of the enum's defined above + char Name[MPLAYER_NAME_MAX]; // Player or Game Name + union { + struct { + unsigned int IsOpen : 1; // 1 = game is open for joining + } GameInfo; + struct { + HousesType House; // player's House + unsigned int Color; // player's color + unsigned long NameCRC; // CRC of player's game's name + unsigned long MinVersion; // game's min supported version + unsigned long MaxVersion; // game's max supported version + } PlayerInfo; + struct { + unsigned char Scenario; // Scenario # + unsigned int Credits; // player's credits + unsigned int IsBases : 1; // 1 = bases are allowed + unsigned int IsTiberium : 1; // 1 = tiberium is allowed + unsigned int IsGoodies : 1; // 1 = goodies are allowed + unsigned int IsGhosties : 1; // 1 = ghosts are allowed + unsigned char BuildLevel; // buildable level + unsigned char UnitCount; // max # units + int Seed; // random number seed + SpecialClass Special; // command-line options + unsigned int GameSpeed; // Game Speed + unsigned long Version; // version # common to all players + } ScenarioInfo; + struct { + char Buf[COMPAT_MESSAGE_LENGTH]; // inter-user message + unsigned char Color; // color of sender of message + unsigned long NameCRC; // CRC of sender's Game Name + } Message; + struct { + int OneWay; // one-way response time + } ResponseTime; + struct { + int Why; // why were we rejected from the game? + } Reject; + struct { + unsigned long ID; // unique ID for this chat node + unsigned char Color; // my color + } Chat; + }; +} GlobalPacketType; + +//........................................................................... +// For finding sync bugs; filled in by the engine when certain conditions +// are met; the pointers allow examination of objects in the debugger. +//........................................................................... +typedef struct { + union { + AircraftClass *Aircraft; + AnimClass *Anim; + BuildingClass *Building; + BulletClass *Bullet; + InfantryClass *Infantry; + UnitClass *Unit; + void *All; + } Ptr; +} TrapObjectType; + +typedef struct { + int ScenarioIndex; + int Bases; + int Credits; + int Tiberium; + int Goodies; + int Ghosts; + int UnitCount; +} GameOptionsType; + +//--------------------------------------------------------------------------- +// Class Definition +//--------------------------------------------------------------------------- +class SessionClass +{ + //------------------------------------------------------------------------ + // Public interface + //------------------------------------------------------------------------ + public: + //..................................................................... + // Constructor/Destructor + //..................................................................... + SessionClass(void); + ~SessionClass(void); + + //..................................................................... + // Initialization + //..................................................................... + void One_Time(void); + void Init(void); + + //..................................................................... + // Reads/writes to the INI file + //..................................................................... + void Read_MultiPlayer_Settings (void); + void Write_MultiPlayer_Settings (void); + void Read_Scenario_Descriptions (void); + void Free_Scenario_Descriptions(void); + + //..................................................................... + // Utility functions + //..................................................................... + int Create_Connections(void); + bool Am_I_Master(void); + unsigned long Compute_Unique_ID(void); + + //..................................................................... + // File I/O + //..................................................................... + int Save(FileClass &file); + int Load(FileClass &file); + + //..................................................................... + // Debugging / Sync Bugs + //..................................................................... + void Trap_Object(void); + + //--------------------------------------------------------------------- + // Public Data + //--------------------------------------------------------------------- + //..................................................................... + // The type of session being played + //..................................................................... + GameType Type; + + //..................................................................... + // The current communications protocol + //..................................................................... + CommProtocolType CommProtocol; + + //..................................................................... + // Game options + //..................................................................... + GameOptionsType Options; + + //..................................................................... + // Unique workstation ID, for detecting my own packets + //..................................................................... + unsigned long UniqueID; + + //..................................................................... + // Player's local options + //..................................................................... + char Handle[MPLAYER_NAME_MAX]; // player name + int PrefColor; // preferred color index + int ColorIdx; // actual color index + HousesType House; // GDI / NOD + int Blitz; // 1 = AI blitzes + int ObiWan; // 1 = player can see all + int Solo; // 1 = player can play alone + + //..................................................................... + // Max allowable # of players & actual # of (human) players + //..................................................................... + int MaxPlayers; + int NumPlayers; + + //..................................................................... + // Frame-sync'ing timing variables + // 'MaxAhead' is the number of frames ahead of this one to execute + // a given packet. It's set by the RESPONSE_TIME event. + // 'FrameSendRate' is the # frames between data packets + // 'FrameRateDelay' is the time ticks to wait between frames, for + // smoothing. + //..................................................................... + unsigned long MaxAhead; + unsigned long FrameSendRate; + unsigned long FrameRateDelay; + + //..................................................................... + // This flag is set when we've loaded a multiplayer game. + //..................................................................... + int LoadGame; + + //..................................................................... + // This flag is set when the modem game saves the game due to a lost + // connection. + //..................................................................... + int EmergencySave; + + //..................................................................... + // List of scenarios & their file numbers + //..................................................................... + DynamicVectorClass Scenarios; + DynamicVectorClass Filenum; + + //..................................................................... + // This is the multiplayer messaging system + //..................................................................... + MessageListClass Messages; + IPXAddressClass MessageAddress; + char LastMessage[MAX_MESSAGE_LENGTH]; + int WWChat : 1; // 1 = go into special WW Chat mode + + //..................................................................... + // This is the multiplayer scorekeeping system + //..................................................................... + MPlayerScoreType Score[MAX_MULTI_NAMES]; + int GamesPlayed; // # games played this run + int NumScores; // # active entries in MPlayerScore + int Winner; // index of winner of last game + int CurGame; // index of current game being played + + //..................................................................... + // Static arrays + //..................................................................... + static int GColors[]; + static int TColors[]; + static char Descriptions[100][40]; + static int CountMin[2]; + static int CountMax[2]; + static char * GlobalPacketNames[]; + static char * SerialPacketNames[]; + + //..................................................................... + // For Recording & Playing back a file + //..................................................................... + CCFileClass RecordFile; + int Record : 1; + int Play : 1; + int Attract : 1; + + //..................................................................... + // IPX-specific variables + //..................................................................... + int IsBridge; // 1 = we're crossing a bridge + IPXAddressClass BridgeNet; // address of bridge + bool NetStealth; // makes us invisible + bool NetProtect; // keeps others from messaging us + bool NetOpen; // 1 = game is open for joining + char GameName[MPLAYER_NAME_MAX]; // game's name + GlobalPacketType GPacket; // global packet + int GPacketlen; // global packet length + IPXAddressClass GAddress; // address of sender + unsigned short GProductID; // product ID of sender + char MetaPacket[MAX_IPX_PACKET_SIZE]; // packet building buffer + int MetaSize; // size of MetaPacket + DynamicVectorClass Games; // list of games + DynamicVectorClass Players; // list of players + DynamicVectorClass Chat; // list of chat nodes + + //..................................................................... + // Modem-specific variables + //..................................................................... + bool ModemService : 1; // 1 = service modem in Call_Back + int CurPhoneIdx; // phone listing index + SerialSettingsType SerialDefaults; // default serial settings + ModemGameType ModemType; // caller or answerer? + + DynamicVectorClass PhoneBook; + DynamicVectorClass InitStrings; + static char * DialMethodCheck[ DIAL_METHODS ]; + static char * CallWaitStrings[ CALL_WAIT_STRINGS_NUM ]; + + //..................................................................... + // For finding Sync Bugs + //..................................................................... + long TrapFrame; + RTTIType TrapObjType; + TrapObjectType TrapObject; + COORD TrapCoord; + void * TrapThis; + CellClass * TrapCell; + int TrapCheckHeap; + +}; + +#endif // SESSION_H + +/*************************** end of session.h ******************************/ diff --git a/SHAPEBTN.CPP b/SHAPEBTN.CPP new file mode 100644 index 0000000..a2e2264 --- /dev/null +++ b/SHAPEBTN.CPP @@ -0,0 +1,166 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\shapebtn.cpv 2.17 16 Oct 1995 16:52:00 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SHAPEBTN.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ShapeButtonClass::ShapeButtonClass -- Constructor for a shape type button. * + * ShapeButtonClass::Draw_Me -- Renders the shape button's imagery. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "shapebtn.h" + + +/*********************************************************************************************** + * ShapeButtonClass::ShapeButtonClass -- Default Constructor for a shape type button. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: You must call Set_Shape() before using a button constructed with this function, * + * and you must set X & Y, and ID. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +ShapeButtonClass::ShapeButtonClass(void) : + ToggleClass(0, 0, 0, 0, 0) +{ + ReflectButtonState = false; +} + + +/*********************************************************************************************** + * ShapeButtonClass::ShapeButtonClass -- Constructor for a shape type button. * + * * + * This is the normal constructor for a shape type button. Shape buttons are ones that * + * have their imagery controlled by a shape file. The various states of the button are * + * given a visual form as one of these shapes. Button dimensions are controlled by the * + * first shape. * + * * + * INPUT: id -- The button ID. * + * * + * shape -- Pointer to the shape file that controls the button's display. * + * * + * x,y -- The pixel coordinate of the upper left corner of the button. * + * * + * OUTPUT: none * + * * + * WARNINGS: The width and height of the shape is controlled by the first shape in the * + * shape file provided. This means that all the shapes in the shape file must be * + * the same size. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +ShapeButtonClass::ShapeButtonClass(unsigned id, void const * shape, int x, int y) : + ToggleClass(id, x, y, 0, 0) +{ + Width = 0; + Height = 0; + ReflectButtonState = false; + Set_Shape(shape); +} + + +void ShapeButtonClass::Set_Shape(void const * data) +{ + ShapeData = data; + if (ShapeData) { + Width = Get_Build_Frame_Width(ShapeData); + Height = Get_Build_Frame_Height(ShapeData); + } +} + + +/*********************************************************************************************** + * ShapeButtonClass::Draw_Me -- Renders the shape button's imagery. * + * * + * This function is called when the button detects that it must be redrawn. The actual * + * shape to use is controled by the button's state and the shape file provided when then * + * button was constructed. * + * * + * INPUT: forced -- Should the button be redrawn regardless of the redraw flag? * + * * + * OUTPUT: bool; Was the shape redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int ShapeButtonClass::Draw_Me(int forced) +{ + if (ControlClass::Draw_Me(forced) && ShapeData) { + + /* + ** Hide the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width-1, Y+Height-1); + } + + /* + ** Draw the body & set text color. + */ + int shapenum = 0; + if (IsDisabled) { + shapenum = DISABLED_SHAPE; + } else { + + if (!ReflectButtonState){ + if (IsPressed) { + shapenum = DOWN_SHAPE; + } else { + shapenum = UP_SHAPE; + } + }else{ + shapenum = IsOn; + } + } + CC_Draw_Shape(ShapeData, shapenum, X, Y, WINDOW_MAIN, SHAPE_NORMAL); + + /* + ** Display the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + + + diff --git a/SHAPEBTN.H b/SHAPEBTN.H new file mode 100644 index 0000000..e44c8e3 --- /dev/null +++ b/SHAPEBTN.H @@ -0,0 +1,67 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\shapebtn.h_v 2.18 16 Oct 1995 16:46:30 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SHAPEBTN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SHAPEBTN_H +#define SHAPEBTN_H + +#include "toggle.h" + +class ShapeButtonClass : public ToggleClass +{ + public: + ShapeButtonClass(void); + ShapeButtonClass(unsigned id, void const * shapes, int x, int y); + virtual int Draw_Me(int forced=false); + virtual void Set_Shape(void const * data); + + enum ShapeButtonClassEnums { + UP_SHAPE, // Shape to use when button is "up". + DOWN_SHAPE, // Shape to use when button is "down". + DISABLED_SHAPE, // Shape to use when button is disabled. + }; + + unsigned ReflectButtonState:1; + + protected: + + /* + ** This points to the shape data file. This file contains the appropriate shapes + ** for this button in the offsets specified above. + */ + void const * ShapeData; +}; +#endif diff --git a/SIDEBAR.CPP b/SIDEBAR.CPP new file mode 100644 index 0000000..e6a7490 --- /dev/null +++ b/SIDEBAR.CPP @@ -0,0 +1,2535 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\sidebar.cpv 2.13 02 Aug 1995 17:03:22 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SIDEBAR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 20, 1994 * + * * + * Last Update : January 25, 1996 [] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SidebarClass::Abandon_Production -- Stops production of the object specified. * + * SidebarClass::Activate -- Controls the sidebar activation. * + * SidebarClass::Activate_Demolish -- Controls the demolish button on the sidebar. * + * SidebarClass::Activate_Repair -- Controls the repair button on the sidebar. * + * SidebarClass::Activate_Upgrade -- Controls the upgrade button on the sidebar. * + * SidebarClass::Add -- Adds a game object to the sidebar list. * + * SidebarClass::AI -- Handles player clicking on sidebar area. * + * SidebarClass::Draw_It -- Renders the sidebar display. * + * SidebarClass::Factory_Link -- Links a factory to a sidebar strip. * + * SidebarClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * + * SidebarClass::Init_IO -- Adds buttons to the button list * + * SidebarClass::Init_Theater -- Performs theater-specific initialization * + * SidebarClass::One_Time -- Handles the one time game initializations. * + * SidebarClass::One_Time -- Handles the one time game initializations. * + * SidebarClass::Recalc -- Examines the sidebar data and updates it as necessary. * + * SidebarClass::Refresh_Cells -- Intercepts the refresh, looking for sidebar controls. * + * SidebarClass::SBGadgetClass::Action -- Special function that controls the mouse over the s* + * SidebarClass::Scroll -- Handles scrolling the sidebar object strip. * + * SidebarClass::Set_Current -- Sets a specified object that controls the sidebar display. * + * SidebarClass::SidebarClass -- Default constructor for the sidebar. * + * SidebarClass::StripClass::Abandon_Produ -- Abandons production associated with sidebar. * + * SidebarClass::StripClass::Activate -- Adds the strip buttons to the input system. * + * SidebarClass::StripClass::Add -- Add an object to the side strip. * + * SidebarClass::StripClass::AI -- Input and AI processing for the side strip. * + * SidebarClass::StripClass::Deactivate -- Removes the side strip buttons from the input syst* + * SidebarClass::StripClass::Draw_It -- Render the sidebar display. * + * SidebarClass::StripClass::Factory_Link -- Links a factory to a sidebar button. * + * SidebarClass::StripClass::Flag_To_Redra -- Flags the sidebar strip to be redrawn. * + * SidebarClass::StripClass::Get_Special_Cameo -- Fetches the special event cameo shape. * + * SidebarClass::StripClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * + * SidebarClass::StripClass::Init_IO -- Adds buttons to the button list * + * SidebarClass::StripClass::Init_Theater -- Performs theater-specific initialization * + * SidebarClass::StripClass::One_Time -- Performs one time actions necessary for the side str* + * SidebarClass::StripClass::Recalc -- Revalidates the current sidebar list of objects. * + * SidebarClass::StripClass::Scroll -- Causes the side strip to scroll. * + * SidebarClass::StripClass::SelectClass:: -- Action function when buildable cameo is selecte* + * SidebarClass::StripClass::SelectClass:: -- Assigns special values to a buildable select bu* + * SidebarClass::StripClass::SelectClass::SelectClass -- Default constructor. * + * SidebarClass::StripClass::StripClass -- Default constructor for the side strip class. * + * SidebarClass::Which_Column -- Determines which column a given type should appear. * + * sortfunc -- Utility routine that handles 'qsort' the strip buttons. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** Define "_RETRIEVE" if the palette morphing tables are part of the loaded data. If this +** is undefined, then the files will be created. +*/ +#define _RETRIEVE + + +/*************************************************************************** +** This holds the translucent table for use with the construction clock +** animation. +*/ +char SidebarClass::StripClass::ClockTranslucentTable[(1+1)*256]; + + +/*************************************************************************** +** This points to the main sidebar shapes. These include the upgrade and +** repair buttons. +*/ +TheaterType SidebarClass::StripClass::LastTheater = THEATER_NONE; + +typedef enum ButtonNumberType { + BUTTON_RADAR = 100, + BUTTON_REPAIR, + BUTTON_DEMOLISH, + BUTTON_UPGRADE, + BUTTON_SELECT, + BUTTON_ZOOM +} ButtonNumberType; + +/* +** Sidebar buttons +*/ +SidebarClass::SBGadgetClass SidebarClass::Background; +ShapeButtonClass SidebarClass::Repair; +ShapeButtonClass SidebarClass::Upgrade; +ShapeButtonClass SidebarClass::Zoom; +ShapeButtonClass SidebarClass::StripClass::UpButton[COLUMNS]; +ShapeButtonClass SidebarClass::StripClass::DownButton[COLUMNS]; +SidebarClass::StripClass::SelectClass +SidebarClass::StripClass::SelectButton[COLUMNS][MAX_VISIBLE]; + +/* +** Shape data pointers +*/ +void const * SidebarClass::StripClass::LogoShapes; +void const * SidebarClass::StripClass::ClockShapes; +void const * SidebarClass::StripClass::SpecialShapes[3]; + +void const * SidebarClass::SidebarShape1; +void const * SidebarClass::SidebarShape2; + + +/*********************************************************************************************** + * SidebarClass::SidebarClass -- Default constructor for the sidebar. * + * * + * Constructor for the sidebar handler. It basically sets up the sidebar to the empty * + * condition. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/17/1994 JLB : Created. * + *=============================================================================================*/ +SidebarClass::SidebarClass(void) +{ + IsSidebarActive = false; + IsRepairActive = false; + IsUpgradeActive = false; + IsDemolishActive = false; + IsToRedraw = true; + +} + + +/*********************************************************************************************** + * SidebarClass::One_Time -- Handles the one time game initializations. * + * * + * This routine is used to load the graphic data that is needed by the sidebar display. It * + * should only be called ONCE. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once when the game first starts. * + * * + * HISTORY: * + * 10/28/94 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::One_Time(void) +{ + PowerClass::One_Time(); + /* + ** Set up the pixel offsets and widths and heights used to render the + ** sidebar. They are now variables because we need to change them for + ** variable resolutions. + */ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + SideBarWidth = SIDEBARWIDTH * factor; + SideX = SeenBuff.Get_Width() - SideBarWidth; + SideY = Map.RadY + Map.RadHeight + 1; + SideWidth = SeenBuff.Get_Width() - SideX; + SideHeight = SeenBuff.Get_Height() - SideY; + MaxVisible = 4; + ButtonHeight = 9 * factor; + TopHeight = ButtonHeight + (4 * factor); + + Background.X = SideX+8 * factor; + Background.Y = SideY, + Background.Width = SideWidth-1; + Background.Height = SideHeight-1; + /* + ** This sets up the clipping window. This window is used by the shape drawing + ** code so that as the sidebar buildable buttons scroll, they get properly + ** clipped at the top and bottom edges. + */ + WindowList[WINDOW_SIDEBAR][WINDOWX] = (SideX+PowWidth) >> 3; + WindowList[WINDOW_SIDEBAR][WINDOWY] = SideY + 1 + TopHeight; + WindowList[WINDOW_SIDEBAR][WINDOWWIDTH] = SideWidth>>3; + WindowList[WINDOW_SIDEBAR][WINDOWHEIGHT] = (MaxVisible * (StripClass::OBJECT_HEIGHT * factor)) - 1; + + /* + ** Set up the coordinates for the sidebar strips. These coordinates are for + ** the upper left corner. + */ + int width = (SideWidth - PowWidth) - (((StripClass::STRIP_WIDTH ) * factor) << 1); + int spacing = width / 3; + + Column[0].X = SideX + PowWidth + spacing; + Column[0].Y = SideY + TopHeight + 1; + Column[1].X = Column[0].X + (StripClass::STRIP_WIDTH * factor) + spacing -1; + Column[1].Y = SideY + TopHeight + 1; + + Column[0].One_Time(0); + Column[1].One_Time(1); + + SidebarShape1 = Hires_Retrieve("SIDE1.SHP"); + SidebarShape2 = Hires_Retrieve("SIDE2.SHP"); + + +} + + +/*********************************************************************************************** + * SidebarClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Init_Clear(void) +{ + PowerClass::Init_Clear(); + + IsToRedraw = true; + IsRepairActive = false; + IsUpgradeActive = false; + IsDemolishActive = false; + + Column[0].Init_Clear(); + Column[1].Init_Clear(); + + Activate(false); +} + + +/*********************************************************************************************** + * SidebarClass::Init_IO -- Adds buttons to the button list * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Init_IO(void) +{ + void *oldfont; + int oldx; + PowerClass::Init_IO(); + + /* + ** Add the sidebar's buttons only if we're not in editor mode. + */ + int buttonspacing = (SideBarWidth - (ButtonOneWidth + ButtonTwoWidth + ButtonThreeWidth)) / 4; + + + if (!Debug_Map) { + /* + ** Set the button widths based on the string that goes in them. + */ + oldfont = Set_Font(Font6Ptr); + oldx = FontXSpacing; + FontXSpacing = -1; + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_6POINT | TPF_NOSHADOW); + + int maxwidth = String_Pixel_Width(Text_String(TXT_REPAIR_BUTTON)) + 8; + maxwidth = MAX(maxwidth, String_Pixel_Width(Text_String(TXT_BUTTON_SELL)) + 8); + maxwidth = MAX(maxwidth, String_Pixel_Width(Text_String(TXT_MAP)) + 8); + Repair.Width = maxwidth; + Upgrade.Width = maxwidth; + Zoom.Width = maxwidth; +// Repair.Width = String_Pixel_Width(Text_String(TXT_REPAIR_BUTTON)) + 8; +// Upgrade.Width = String_Pixel_Width(Text_String(TXT_BUTTON_SELL)) + 8; +// Zoom.Width = String_Pixel_Width(Text_String(TXT_MAP)) + 8; + /* + ** find the spacing between buttons by getting remaining width + ** and dividing it between the buttons. + */ + int buttonspacing = (SideBarWidth - (Repair.Width + Upgrade.Width + Zoom.Width)) / 4; + + Repair.IsSticky = true; + Repair.ID = BUTTON_REPAIR; + Repair.X = 484; + Repair.Y = 160; + Repair.IsPressed = false; + Repair.IsToggleType = true; + Repair.ReflectButtonState = true; +#if (FRENCH) + Repair.Set_Shape(Hires_Retrieve("REPAIRF.SHP")); +#else +#if (GERMAN) + Repair.Set_Shape(Hires_Retrieve("REPAIRG.SHP")); +#else + Repair.Set_Shape(Hires_Retrieve("REPAIR.SHP")); +#endif +#endif + + Upgrade.IsSticky = true; + Upgrade.ID = BUTTON_UPGRADE; + Upgrade.X = 480+57; + Upgrade.Y = 160; + Upgrade.IsPressed = false; + Upgrade.IsToggleType = true; + Upgrade.ReflectButtonState = true; +#if (FRENCH) + Upgrade.Set_Shape(Hires_Retrieve("SELLF.SHP")); +#else +#if (GERMAN) + Upgrade.Set_Shape(Hires_Retrieve("SELLG.SHP")); +#else + Upgrade.Set_Shape(Hires_Retrieve("SELL.SHP")); +#endif +#endif + + Zoom.IsSticky = true; + Zoom.ID = BUTTON_ZOOM; + Zoom.X = 480 + 110; + Zoom.Y = 160; + Zoom.IsPressed = false; +#if (FRENCH) + Zoom.Set_Shape(Hires_Retrieve("MAPF.SHP")); +#else +#if (GERMAN) + Zoom.Set_Shape(Hires_Retrieve("MAPG.SHP")); +#else + Zoom.Set_Shape(Hires_Retrieve("MAP.SHP")); +#endif +#endif + + if (IsRadarActive || GameToPlay!=GAME_NORMAL) { + Zoom.Enable(); + } else { + Zoom.Disable(); + } + + Set_Font(oldfont); + FontXSpacing = oldx; + FontXSpacing = -1; + + + Column[0].Init_IO(0); + Column[1].Init_IO(1); + + /* + ** If a game was loaded & the sidebar was enabled, pop it up now + */ + if (IsSidebarActive) { + IsSidebarActive = false; + Activate(1); +// Background.Zap(); +// Add_A_Button(Background); + } + } +} + + +/*********************************************************************************************** + * SidebarClass::Init_Theater -- Performs theater-specific initialization * + * * + * INPUT: theater -- The theater that is being initialized. Sometimes this has an effect on * + * the data that is loaded. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Init_Theater(TheaterType theater) +{ + PowerClass::Init_Theater(theater); + + Column[0].Init_Theater(theater); + Column[1].Init_Theater(theater); +} + + +/*********************************************************************************************** + * SidebarClass::Which_Column -- Determines which column a given type should appear. * + * * + * Use this function to resolve what column the specified object type should be placed * + * into. * + * * + * INPUT: otype -- Pointer to the object type class of the object in question. * + * * + * OUTPUT: Returns with the column number that the object should be placed in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + *=============================================================================================*/ +int SidebarClass::Which_Column(RTTIType type) +{ + if (type == RTTI_BUILDINGTYPE || type == RTTI_BUILDING) { + return(0); + } + return(1); +} + + +/*********************************************************************************************** + * SidebarClass::Factory_Link -- Links a factory to a sidebar strip. * + * * + * This routine will link the specified factory to the sidebar strip. A factory must be * + * linked to the sidebar so that as the factory production progresses, the sidebar will * + * show the production progress. * + * * + * INPUT: factory -- The factory number to attach. * + * * + * type -- The object type number. * + * * + * id -- The object sub-type number. * + * * + * OUTPUT: Was the factory successfully attached to the sidebar strip? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1995 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Factory_Link(int factory, RTTIType type, int id) +{ + return(Column[Which_Column(type)].Factory_Link(factory, type, id)); +} + + +/*********************************************************************************************** + * SidebarClass::Refresh_Cells -- Intercepts the refresh, looking for sidebar controls. * + * * + * This routine intercepts the Refresh_Cells call in order to see if the sidebar needs * + * to be refreshed as well. If the special code to refresh the sidebar was found, it * + * flags the sidebar to be redrawn and then removes the code from the list. * + * * + * INPUT: cell -- The cell to base the refresh list on. * + * * + * list -- Pointer to the cell offset list that elaborates all the cells that * + * need to be flagged for redraw. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Refresh_Cells(CELL cell, short const *list) +{ + if (*list == REFRESH_SIDEBAR) { + IsToRedraw = true; + Column[0].IsToRedraw = true; + Column[1].IsToRedraw = true; + Flag_To_Redraw(false); + } + PowerClass::Refresh_Cells(cell, list); +} + + +/*********************************************************************************************** + * SidebarClass::Activate_Repair -- Controls the repair button on the sidebar. * + * * + * Use this routine to turn the repair sidebar button on and off. Typically, the button * + * is enabled when the currently selected structure is friendly and damaged. * + * * + * INPUT: control -- The controls how the button is to be activated or deactivated; * + * 0 -- Turn button off. * + * 1 -- Turn button on. * + * -1 -- Toggle button state. * + * * + * OUTPUT: bool; Was the button previously activated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Activate_Repair(int control) +{ + bool old = IsRepairActive; + + if (control == -1) { + control = IsRepairActive ? 0 : 1; + } + switch (control) { + case 1: + IsRepairActive = true; + break; + + default: + case 0: + IsRepairActive = false; + break; + } + if (old != IsRepairActive) { + Flag_To_Redraw(false); + IsToRedraw = true; + + if (!IsRepairActive) { + Help_Text(TXT_NONE); + Set_Default_Mouse(MOUSE_NORMAL, false); + } + } + return(old); +} + + +/*********************************************************************************************** + * SidebarClass::Activate_Upgrade -- Controls the upgrade button on the sidebar. * + * * + * Use this routine to turn the upgrade sidebar button on and off. Typically, the button * + * is enabled when the currently selected structure can be upgraded and disabled otherwise. * + * * + * INPUT: control -- The controls how the button is to be activated or deactivated; * + * 0 -- Turn button off. * + * 1 -- Turn button on. * + * -1 -- Toggle button state. * + * * + * OUTPUT: bool; Was the button previously activated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Activate_Upgrade(int control) +{ + bool old = IsUpgradeActive; + if (control == -1) { + control = IsUpgradeActive ? 0 : 1; + } + switch (control) { + case 1: + IsUpgradeActive = true; + break; + + default: + case 0: + IsUpgradeActive = false; + break; + } + if (old != IsUpgradeActive) { + Flag_To_Redraw(false); + IsToRedraw = true; + if (!IsUpgradeActive) { + Set_Default_Mouse(MOUSE_NORMAL, false); + } + } + return(old); +} + + +/*********************************************************************************************** + * SidebarClass::Activate_Demolish -- Controls the demolish button on the sidebar. * + * * + * Use this routine to turn the demolish/dismantle sidebar button on and off. Typically, * + * the button is enabled when a friendly building is selected and disabled otherwise. * + * * + * INPUT: control -- The controls how the button is to be activated or deactivated; * + * 0 -- Turn button off. * + * 1 -- Turn button on. * + * -1 -- Toggle button state. * + * * + * OUTPUT: bool; Was the button previously activated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Activate_Demolish(int control) +{ + bool old = IsDemolishActive; + + if (control == -1) { + control = IsDemolishActive ? 0 : 1; + } + switch (control) { + case 1: + IsDemolishActive = true; + break; + + default: + case 0: + IsDemolishActive = false; + break; + } + if (old != IsDemolishActive) { + Flag_To_Redraw(false); + IsToRedraw = true; + if (!IsDemolishActive) { + Set_Default_Mouse(MOUSE_NORMAL, false); + } + } + return(old); +} + + +/*********************************************************************************************** + * SidebarClass::Add -- Adds a game object to the sidebar list. * + * * + * This routine is used to add a game object to the sidebar. Call this routine when a * + * factory type building is created. It handles the case of adding an item that has already * + * been added -- it just ignores it. * + * * + * INPUT: object -- Pointer to the object that is being added. * + * * + * OUTPUT: bool; Was the object added to the sidebar? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/17/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Add(RTTIType type, int id) +{ + int column; + + /* + ** Add the sidebar only if we're not in editor mode. + */ + if (!Debug_Map) { + column = Which_Column(type); + + if (Column[column].Add(type, id)) { + Activate(1); + IsToRedraw = true; + Flag_To_Redraw(false); + return(true); + } + return(false); + } + + return(false); +} + + +/*********************************************************************************************** + * SidebarClass::Scroll -- Handles scrolling the sidebar object strip. * + * * + * This routine is used to scroll the sidebar strip of objects. The strip appears whenever * + * a building is selected that can produce units. If the number of units to produce is * + * greater than what the sidebar can hold, this routine is used to scroll the other object * + * into view so they can be selected. * + * * + * INPUT: up -- Should the scroll be upwards? Upward scrolling reveals object that are * + * later in the list of objects. * + * * + * OUTPUT: bool; Did scrolling occur? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/28/94 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Scroll(bool up, int column) +{ + if (Column[column].Scroll(up)) { + IsToRedraw = true; + Flag_To_Redraw(false); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SidebarClass::Draw_It -- Renders the sidebar display. * + * * + * This routine performs the actual drawing of the sidebar display. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the sidebar imagery changed at all? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/28/94 JLB : Created. * + * 12/31/1994 JLB : Split rendering off into the sidebar strip class. * + *=============================================================================================*/ +void SidebarClass::Draw_It(bool complete) +{ + PowerClass::Draw_It(complete); + + if (IsSidebarActive && (IsToRedraw || complete) && !Debug_Map) { + IsToRedraw = false; + + if (LogicPage->Lock()){ + /* + ** Draw the outline box around the sidebar buttons. + */ + //CC_Draw_Shape(SidebarShape1, (int)complete, SideX, 158, WINDOW_MAIN, SHAPE_WIN_REL); + //CC_Draw_Shape(SidebarShape2, (int)complete, SideX, 158+118, WINDOW_MAIN, SHAPE_WIN_REL); + LogicPage->Draw_Line(SideX, 157, SeenBuff.Get_Width()-1, 157, 0); + CC_Draw_Shape(SidebarShape1, 0, SideX, 158, WINDOW_MAIN, SHAPE_WIN_REL); + CC_Draw_Shape(SidebarShape2, 0, SideX, 158+118, WINDOW_MAIN, SHAPE_WIN_REL); + + #if (0) + if ( complete ) { + LogicPage->Fill_Rect(SideX+Map.PowWidth, SideY, SideX+SideWidth-1, SideY+SideHeight-1, LTGREY); + } + LogicPage->Fill_Rect(SideX, SideY, SideX+SideWidth-1, SideY+TopHeight-1, LTGREY); + Draw_Box(SideX+Map.PowWidth, SideY+TopHeight, SideWidth-Map.PowWidth, SideHeight-TopHeight, BOXSTYLE_RAISED, false); + #endif //(0) + //Repair.Draw_Me(true); + //Upgrade.Draw_Me(true); + //Zoom.Draw_Me(true); + // } else { + // if (IsToRedraw || complete) { + // LogicPage->Fill_Rect(TacPixelX + Lepton_To_Pixel(TacLeptonWidth), SIDE_Y, 319, SIDE_Y+TOP_HEIGHT, BLACK); + // } + + LogicPage->Unlock(); + } + + } + /* + ** Draw the side strip elements by calling their respective draw functions. + */ + if (IsSidebarActive){ + Column[0].Draw_It(complete); + Column[1].Draw_It(complete); + Repair.Draw_Me(true); + Upgrade.Draw_Me(true); + Zoom.Draw_Me(true); + } + + IsToRedraw = false; +} + + +/*********************************************************************************************** + * SidebarClass::AI -- Handles player clicking on sidebar area. * + * * + * This routine handles the processing necessary when the player clicks on the sidebar. * + * Typically, this is selection of the item to build. * + * * + * INPUT: input -- Reference to the keyboard input value. * + * * + * x,y -- Mouse coordinates at time of input. * + * * + * OUTPUT: bool; Was the click handled? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/28/94 JLB : Created. * + * 11/11/1994 JLB : Processes input directly. * + * 12/26/1994 JLB : Uses factory manager class for construction handling. * + * 12/31/1994 JLB : Simplified to use the sidebar strip class handlers. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + * 06/27/1995 JLB : key toggles sidebar. * + *=============================================================================================*/ +void SidebarClass::AI(KeyNumType & input, int x, int y) +{ + bool redraw = false; + + /* + ** Toggle the sidebar in and out with the key. + */ + if (input == KN_TAB) { + Activate(-1); + } + + if (!Debug_Map) { + Column[0].AI(input, x, y); + Column[1].AI(input, x, y); + } + + if (IsSidebarActive && !Debug_Map) { + + if (input == KN_DOWN) { + redraw |= Column[0].Scroll(false); + redraw |= Column[1].Scroll(false); + input = KN_NONE; + } + if (input == KN_UP) { + redraw |= Column[0].Scroll(true); + redraw |= Column[1].Scroll(true); + input = KN_NONE; + } + } + + if (IsSidebarActive) { + + /* + ** If there are any buildings in the payer's inventory, then allow the repair + ** option. + */ + if (PlayerPtr->BScan) { + Activate_Repair(true); + } else { + Activate_Repair(false); + } + + if (input == (BUTTON_REPAIR|KN_BUTTON)) { + Repair_Mode_Control(-1); + } + + if (input == (BUTTON_ZOOM|KN_BUTTON)) { + /* + ** If radar is active, cycle as follows: + ** Zoomed => not zoomed + ** not zoomed => player status (multiplayer only) + ** player status => zoomed + */ + if (IsRadarActive) { + if (Is_Zoomed() || GameToPlay==GAME_NORMAL) { + Zoom_Mode(Coord_Cell(TacticalCoord)); + } else { + if (!Is_Player_Names()) { + Player_Names(1); + } else { + Player_Names(0); + Zoom_Mode(Coord_Cell(TacticalCoord)); + } + } + } else { + if (GameToPlay!=GAME_NORMAL) { + Player_Names(Is_Player_Names()==0); + } + } + } + + if (input == (BUTTON_UPGRADE|KN_BUTTON)) { + Sell_Mode_Control(-1); + } + +#ifdef NEVER +// int index = -1; + if (index != -1) { + /* + ** Display help text if the mouse is over a sidebar button. + */ + switch (index) { + default: + case 2: + Map.Help_Text(TXT_UPGRADE, -1, -1, PlayerPtr->Class->Color); + break; + + case 1: + Map.Help_Text(PlayerPtr->Class->House == HOUSE_GOOD ? TXT_SELL : TXT_DEMOLISH, x, y, PlayerPtr->Class->Color); + break; + + case 0: + Map.Help_Text(TXT_REPAIR, x, y, PlayerPtr->Class->Color); + break; + } + } +#endif + + if (redraw) { + //IsToRedraw = true; + Column[0].Flag_To_Redraw(); + Column[1].Flag_To_Redraw(); + + Flag_To_Redraw(false); + } + } + + if ((!IsRepairMode) && Repair.IsOn){ + Repair.Turn_Off(); + } + + if ((!IsSellMode) && Upgrade.IsOn){ + Upgrade.Turn_Off(); + } + + PowerClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * SidebarClass::Recalc -- Examines the sidebar data and updates it as necessary. * + * * + * Occasionally a factory gets destroyed. This routine must be called in such a case * + * because it might be possible that sidebar object need to be removed. This routine will * + * examine all existing objects in the sidebar class and if no possible factory can * + * produce it, then it will be removed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is exhaustive and thus time consuming. Only call it when really * + * necessary. Such as when a factory is destroyed rather than when a non-factory * + * is destroyed. * + * * + * HISTORY: * + * 11/30/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Recalc(void) +{ + bool redraw = false; + + redraw |= Column[0].Recalc(); + redraw |= Column[1].Recalc(); + + if (redraw) { + IsToRedraw = true; + Flag_To_Redraw(false); + } +} + + +/*********************************************************************************************** + * SidebarClass::Activate -- Controls the sidebar activation. * + * * + * Use this routine to turn the sidebar on or off. This routine handles updating the * + * necessary flags. * + * * + * INPUT: control -- Tells what to do with the sidebar according to the following: * + * 0 = Turn sidebar off. * + * 1 = Turn sidebar on. * + * -1= Toggle sidebar on or off. * + * * + * OUTPUT: bool; Was the sidebar already on? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Activate(int control) +{ + bool old = IsSidebarActive; + + int sidex = SeenBuff.Get_Width() - SideBarWidth; + int sidey = Map.RadY + Map.RadHeight; + int topheight = 13; + int sidewidth = SeenBuff.Get_Width() - sidex; + int sideheight = SeenBuff.Get_Height() - sidey; + + if (PlaybackGame) + return (old); + + /* + ** Determine the new state of the sidebar. + */ + switch (control) { + case -1: + IsSidebarActive = IsSidebarActive == false; + break; + + case 1: + IsSidebarActive = true; + break; + + default: + case 0: + IsSidebarActive = false; + break; + } + + /* + ** Only if there is a change in the state of the sidebar will anything + ** be done to change it. + */ + if (IsSidebarActive != old) { + + /* + ** If the sidebar is activated but was on the right side of the screen, then + ** activate it on the left side of the screen. + */ + if (IsSidebarActive /*&& X*/) { + Set_View_Dimensions(0, Map.Get_Tab_Height(), SeenBuff.Get_Width() - sidewidth); + IsToRedraw = true; + Help_Text(TXT_NONE); + Repair.Zap(); + Add_A_Button(Repair); + Upgrade.Zap(); + Add_A_Button(Upgrade); + Zoom.Zap(); + Add_A_Button(Zoom); + Column[0].Activate(); + Column[1].Activate(); + Background.Zap(); + Add_A_Button(Background); + Map.RadarButton.Zap(); + Add_A_Button(Map.RadarButton); + Map.PowerButton.Zap(); + Add_A_Button(Map.PowerButton); + } else { + Help_Text(TXT_NONE); + Set_View_Dimensions(0, Map.Get_Tab_Height()); + Remove_A_Button(Repair); + Remove_A_Button(Upgrade); + Remove_A_Button(Zoom); + Remove_A_Button(Background); + Column[0].Deactivate(); + Column[1].Deactivate(); + Remove_A_Button(Map.RadarButton); + Remove_A_Button(Map.PowerButton); + } + + /* + ** Since the sidebar status has changed, update the map so that the graphics + ** will be rendered correctly. + */ + Flag_To_Redraw(true); + } + + return(old); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::StripClass -- Default constructor for the side strip class. * + * * + * This constructor is used to reset the side strip to default empty state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + *=============================================================================================*/ +SidebarClass::StripClass::StripClass(void) +{ + IsScrollingDown = false; + IsScrolling = false; + IsBuilding = false; TopIndex = 0; + Slid = 0; + BuildableCount = 0; + for (int index = 0; index < MAX_BUILDABLES; index++) { + Buildables[index].BuildableID = 0; + Buildables[index].BuildableType = RTTI_NONE; + Buildables[index].Factory = -1; + } +} + + + +/*********************************************************************************************** + * SidebarClass::StripClass::One_Time -- Performs one time actions necessary for the side stri * + * * + * Call this routine ONCE at the beginning of the game. It handles retrieving pointers to * + * the shape files it needs for rendering. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::One_Time(int ) +{ + static char *_file[3] = { + "ION", + "ATOM", + "BOMB" + }; + int factor = Get_Resolution_Factor(); + + ObjectWidth = OBJECT_WIDTH << factor; + ObjectHeight = OBJECT_HEIGHT << factor; + StripWidth = STRIP_WIDTH << factor; + LeftEdgeOffset = (StripWidth - ObjectWidth) >> 1; + ButtonSpacingOffset = (StripWidth - ((BUTTON_WIDTH << factor) << 1)) / 3; + + LogoShapes = Hires_Retrieve("STRIP.SHP"); + ClockShapes = Hires_Retrieve("CLOCK.SHP"); + + char fullname[_MAX_FNAME+_MAX_EXT]; + char buffer[_MAX_FNAME]; + + for (int lp = 0; lp < 3; lp++) { + if ( Get_Resolution_Factor() ) { + sprintf(buffer, "%sICNH", _file[lp]); + } else { + sprintf(buffer, "%sICON", _file[lp]); + } + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + SpecialShapes[lp] = MixFileClass::Retrieve(fullname); + } + + +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Get_Special_Cameo -- Fetches the special event cameo shape. * + * * + * This routine will return with a pointer to the cameo data for the special objects that * + * can appear on the sidebar (e.g., nuclear bomb). * + * * + * INPUT: type -- The special type to fetch the cameo imagery for. * + * * + * OUTPUT: Returns with a pointer to the cameo imagery for the specified special object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1995 JLB : commented * + *=============================================================================================*/ +void const * SidebarClass::StripClass::Get_Special_Cameo(int type) +{ + return(SpecialShapes[type]); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Init_Clear(void) +{ + IsScrollingDown = false; + IsScrolling = false; + IsBuilding = false; + Flasher = -1; + TopIndex = 0; + Slid = 0; + BuildableCount = 0; + + /* + ** Since we're resetting the strips, clear out all the buildables & factory pointers. + */ + for (int index = 0; index < MAX_BUILDABLES; index++) { + Buildables[index].BuildableID = 0; + Buildables[index].BuildableType = RTTI_NONE; + Buildables[index].Factory = -1; + } +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Init_IO -- Initializes the strip's buttons * + * * + * This routine doesn't actually add any buttons to the list; + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Init_IO(int id) +{ + ID = id; + + UpButton[ID].IsSticky = true; + UpButton[ID].ID = BUTTON_UP+id; + UpButton[ID].X = X+ButtonSpacingOffset+1; + UpButton[ID].Y = Y+MAX_VISIBLE*ObjectHeight-1; + + UpButton[ID].Set_Shape(Hires_Retrieve("STRIPUP.SHP")); + + DownButton[ID].IsSticky = true; + DownButton[ID].ID = BUTTON_DOWN+id; + DownButton[ID].X = UpButton[ID].X + UpButton[ID].Width + ButtonSpacingOffset-2; + DownButton[ID].Y = Y+MAX_VISIBLE*ObjectHeight-1; + + DownButton[ID].Set_Shape(Hires_Retrieve("STRIPDN.SHP")); + + for (int index = 0; index < MAX_VISIBLE; index++) { + SelectClass & g = SelectButton[ID][index]; + g.ID = BUTTON_SELECT; + g.X = X; + g.Y = Y + (ObjectHeight*index); + g.Width = ObjectWidth; + g.Height = ObjectHeight; + g.Set_Owner(*this, index); + } + +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Init_Theater -- Performs theater-specific initialization * + * * + * INPUT: theater * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Init_Theater(TheaterType theater) +{ + //if (theater != LastTheater) { + + + static char *_file[3] = { + "ION", + "ATOM", + "BOMB" + }; + int factor = Get_Resolution_Factor(); + char fullname[_MAX_FNAME+_MAX_EXT]; + char buffer[_MAX_FNAME]; + void const * cameo_ptr; + + for (int lp = 0; lp < 3; lp++) { + if ( Get_Resolution_Factor() ) { + sprintf(buffer, "%sICNH", _file[lp]); + } else { + sprintf(buffer, "%sICON", _file[lp]); + } + _makepath(fullname, NULL, NULL, buffer, Theaters[theater].Suffix); + cameo_ptr = MixFileClass::Retrieve(fullname); + if (cameo_ptr){ + SpecialShapes[lp] = cameo_ptr; + } + } + + +#ifndef _RETRIEVE + static TLucentType const ClockCols[1] = { +// {LTGREEN, BLACK, 0, 0}, + {GREEN, LTGREY, 180, 0} + }; + + /* + ** Make sure that remapping doesn't occur on the colors that cycle. + */ + Mem_Copy(GamePalette, OriginalPalette, 768); + memset(&GamePalette[CYCLE_COLOR_START*3], 0x3f, CYCLE_COLOR_COUNT*3); + + /* + ** Create the translucent table used for the sidebar. + */ + Build_Translucent_Table(GamePalette, &ClockCols[0], 1, (void*)ClockTranslucentTable); + CCFileClass(Fading_Table_Name("CLOCK", theater)).Write(ClockTranslucentTable, sizeof(ClockTranslucentTable)); + Mem_Copy(OriginalPalette, GamePalette, 768); +#else + CCFileClass(Fading_Table_Name("CLOCK", theater)).Read(ClockTranslucentTable, sizeof(ClockTranslucentTable)); +#endif + LastTheater = theater; + //} +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Activate -- Adds the strip buttons to the input system. * + * * + * This routine will add the side strip buttons to the map's input system. This routine * + * should be called once when the sidebar activates. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Never call this routine a second time without first calling Deactivate(). * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Activate(void) +{ + UpButton[ID].Zap(); + Map.Add_A_Button(UpButton[ID]); + + DownButton[ID].Zap(); + Map.Add_A_Button(DownButton[ID]); + + for (int index = 0; index < MAX_VISIBLE; index++) { + SelectButton[ID][index].Zap(); + Map.Add_A_Button(SelectButton[ID][index]); + } +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Deactivate -- Removes the side strip buttons from the input syste * + * * + * Call this routine to remove all the buttons on the side strip from the map's input * + * system. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Never call this routine unless the Activate() function was prevously called. * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Deactivate(void) +{ + Map.Remove_A_Button(UpButton[ID]); + Map.Remove_A_Button(DownButton[ID]); + for (int index = 0; index < MAX_VISIBLE; index++) { + Map.Remove_A_Button(SelectButton[ID][index]); + } +} + + +#ifdef NEVER +/*********************************************************************************************** + * sortfunc -- Utility routine that handles 'qsort' the strip buttons. * + * * + * This routine is called by qsort() in order to sort the sidebar buttons. This sorting * + * forces the sidebar buttons to always occur in the order that they can be built in, * + * rather than the order that they were added to the sidebar list. * + * * + * INPUT: ptr1 -- Pointer to the first sidebar class object. * + * * + * ptr2 -- Pointer to the second sidebar class object. * + * * + * OUTPUT: Returns <0 if the first object can be produced before the second. It returns * + * >0 if the reverse is true. It returns exactly 0 if the production scneario for * + * both objects is the same. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +static int sortfunc(void const * ptr1, void const * ptr2) +{ + SidebarClass::StripClass::BuildType * b1 = (SidebarClass::StripClass::BuildType *)ptr1; + SidebarClass::StripClass::BuildType * b2 = (SidebarClass::StripClass::BuildType *)ptr2; + + TechnoTypeClass const * p1 = Fetch_Techno_Type(b1->BuildableType, b1->BuildableID); + TechnoTypeClass const * p2 = Fetch_Techno_Type(b2->BuildableType, b2->BuildableID); + + int i1 = 0; + int i2 = 0; + + if (p1) i1 = p1->What_Am_I()*2; + if (p2) i2 = p2->What_Am_I()*2; + + /* + ** Walls should be sorted after the regular buildings. + */ + if (p1 && p1->What_Am_I() == RTTI_BUILDINGTYPE && ((BuildingTypeClass * const)p1)->IsWall) { + i1++; + } + if (p2 && p2->What_Am_I() == RTTI_BUILDINGTYPE && ((BuildingTypeClass * const)p2)->IsWall) { + i2++; + } + + /* + ** If the object types are identical, then sort by scenario available. + */ + if (i1 == i2) { + + /* + ** In the case of walls (can tell if there is an odd value), then sort + ** by cost. + */ + if (i1 & 0x01) { + i1 = p1->Cost; + i2 = p2->Cost; + } else { + i1 = p1->Scenario; + i2 = p2->Scenario; + } + } + + return(i1 - i2); +} +#endif + + +/*********************************************************************************************** + * SidebarClass::StripClass::Add -- Add an object to the side strip. * + * * + * Use this routine to add a buildable object to the side strip. * + * * + * INPUT: object -- Pointer to the object type that can be built and is to be added to * + * the side strip. * + * * + * OUTPUT: bool; Was the object successfully added to the side strip? Failure could be the * + * result of running out of room in the side strip array or the object might * + * already be in the list. * + * * + * WARNINGS: none. * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::StripClass::Add(RTTIType type, int id) +{ + if (BuildableCount <= MAX_BUILDABLES) { + for (int index = 0; index < BuildableCount; index++) { + if (Buildables[index].BuildableType == type && Buildables[index].BuildableID == id) { + return(false); + } + } + if (!ScenarioInit && type != RTTI_SPECIAL) { + Speak(VOX_NEW_CONSTRUCT); + } + Buildables[BuildableCount].BuildableType = type; + Buildables[BuildableCount].BuildableID = id; + BuildableCount++; + IsToRedraw = true; +#ifdef OBSOLETE + if (GameToPlay == GAME_NORMAL) { + qsort(&Buildables[0], BuildableCount, sizeof(Buildables[0]), sortfunc); + } +#endif + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Scroll -- Causes the side strip to scroll. * + * * + * Use this routine to flag the side strip to scroll. The direction scrolled is controlled * + * by the parameter. Scrolling is merely initiated by this routine. Subsequent calls to * + * the AI function and the Draw_It function are required to properly give the appearence * + * of scrolling. * + * * + * INPUT: bool; Should the side strip scroll UP? If it is to scroll down then pass false. * + * * + * OUTPUT: bool; Was the side strip started to scroll in the desired direction? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + * 07/29/1995 JLB : Simplified scrolling logic. * + *=============================================================================================*/ +bool SidebarClass::StripClass::Scroll(bool up) +{ + if (up) { + Scroller--; + } else { + Scroller++; + } +#ifdef NEVER + if (BuildableCount <= MAX_VISIBLE) return(false); + + /* + ** Top of list is moving toward lower ordered entries in the object list. It looks like + ** the "window" to the object list is moving up even though the actual object images are + ** scrolling downward. + */ + if (up) { + if (!TopIndex) return(false); + + TopIndex--; + Slid = 0; + } else { + if (TopIndex+MAX_VISIBLE >= BuildableCount) return(false); + + Slid = ObjectHeight; + } + IsScrollingDown = !up; + IsScrolling = true; +#endif + return(true); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Flag_To_Redra -- Flags the sidebar strip to be redrawn. * + * * + * This utility routine is called when something changes on the sidebar and it must be * + * reflected the next time drawing is performed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Flag_To_Redraw(void) +{ + IsToRedraw = true; + //Map.SidebarClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::AI -- Input and AI processing for the side strip. * + * * + * The side strip AI processing is performed by this function. This function not only * + * checks for player input, but also handles any graphic logic updating necessary as a * + * result of flashing or construction animation. * + * * + * INPUT: input -- The player input code. * + * * + * x,y -- Mouse coordinate to use. * + * * + * OUTPUT: bool; Did the AI detect that it will need a rendering change? If this routine * + * returns true, then the Draw_It function should be called at the * + * earliest opportunity. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + *=============================================================================================*/ +bool SidebarClass::StripClass::AI(KeyNumType & input, int , int ) +{ + bool redraw = false; + + /* + ** If this is scroll button for this side strip, then scroll the strip as + ** indicated. + */ + if (input == (UpButton[ID].ID|KN_BUTTON)) { // && !IsScrolling + UpButton[ID].IsPressed = false; + Scroll(true); + } + if (input == (DownButton[ID].ID|KN_BUTTON)) { // && !IsScrolling + DownButton[ID].IsPressed = false; + Scroll(false); + } + + /* + ** Reflect the scroll desired direction/value into the scroll + ** logic handler. This might result in up or down scrolling. + */ + if (!IsScrolling && Scroller) { + if (BuildableCount <= MAX_VISIBLE) { + Scroller = 0; + } else { + + /* + ** Top of list is moving toward lower ordered entries in the object list. It looks like + ** the "window" to the object list is moving up even though the actual object images are + ** scrolling downward. + */ + if (Scroller < 0) { + if (!TopIndex) { + Scroller = 0; + } else { + Scroller++; + IsScrollingDown = false; + IsScrolling = true; + TopIndex--; + Slid = 0; + } + + } else { + if (TopIndex+MAX_VISIBLE >= BuildableCount) { + Scroller = 0; + } else { + Scroller--; + Slid = ObjectHeight; + IsScrollingDown = true; + IsScrolling = true; + } + } + } + } + + /* + ** Scroll logic is handled here. + */ + if (IsScrolling) { + if (IsScrollingDown) { + Slid -= SCROLL_RATE; + if (Slid <= 0) { + IsScrolling = false; + Slid = 0; + TopIndex++; + } + } else { + Slid += SCROLL_RATE; + if (Slid >= ObjectHeight) { + IsScrolling = false; + Slid = 0; + } + } + redraw = true; + } + + /* + ** Handle any flashing logic. Flashing occurs when the player selects an object + ** and provides the visual feedback of a recognized and legal selection. + */ + if (Flasher != -1) { + if (Graphic_Logic()) { + redraw = true; + if (Fetch_Stage() >= 7) { + Set_Rate(0); + Set_Stage(0); + Flasher = -1; + } + } + } + + /* + ** Handle any building clock animation logic. + */ + if (IsBuilding) { + for (int index = 0; index < BuildableCount; index++) { + int factoryid = Buildables[index].Factory; + + if (factoryid != -1) { + FactoryClass * factory = Factories.Raw_Ptr(factoryid); + + if (factory && factory->Has_Changed()) { + redraw = true; + if (factory->Has_Completed()) { + + /* + ** Construction has been completed. Announce this fact to the player and + ** try to get the object to automatically leave the factory. Buildings are + ** the main exception to the ability to leave the factory under their own + ** power. + */ + TechnoClass * pending = factory->Get_Object(); + if (pending) { + switch (pending->What_Am_I()) { + case RTTI_UNIT: + case RTTI_AIRCRAFT: + OutList.Add(EventClass(EventClass::PLACE, pending->What_Am_I(), -1)); + // Fall into next case. + + case RTTI_BUILDING: + Speak(VOX_CONSTRUCTION); + break; + + case RTTI_INFANTRY: + OutList.Add(EventClass(EventClass::PLACE, pending->What_Am_I(), -1)); + Speak(VOX_UNIT_READY); + break; + } + } + } + } + } + } + } + + /* + ** If any of the logic determined that this side strip needs to be redrawn, then + ** set the redraw flag for this side strip. + */ + if (redraw) { + Flag_To_Redraw(); + } + return(redraw); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Draw_It -- Render the sidebar display. * + * * + * Use this routine to render the sidebar display. It checks to see if it needs to be * + * redrawn and only redraw if necessary. If the "complete" parameter is true, then it * + * will force redraw the entire strip. * + * * + * INPUT: complete -- Should the redraw be forced? A force redraw will ignore the redraw * + * flag. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + * 08/06/1995 JLB : Handles multi factory tracking in same strip. * + *=============================================================================================*/ +void SidebarClass::StripClass::Draw_It(bool complete) +{ + if (IsToRedraw || complete) { + IsToRedraw = false; + + /* + ** Fills the background to the side strip. We shouldnt need to do this if the strip + ** has a full complement of icons. ST - 10/7/96 6:03PM + */ + if (BuildableCount < MAX_VISIBLE){ + CC_Draw_Shape(LogoShapes, ID, X+3, Y-1, WINDOW_MAIN, SHAPE_WIN_REL|SHAPE_NORMAL, 0); + } + + /* + ** Redraw the scroll buttons. + */ + UpButton[ID].Draw_Me(true); + DownButton[ID].Draw_Me(true); + + /* + ** Loop through all the buildable objects that are visible in the strip and render + ** them. Their Y offset may be adjusted if the strip is in the process of scrolling. + */ + for (int i = 0; i < MAX_VISIBLE + (IsScrolling ? 1 : 0); i++) { + bool production; + bool completed; + int stage; + bool darken = false; + void const * shapefile = 0; + int shapenum = 0; + void const * remapper = 0; + FactoryClass * factory = 0; + int index = i+TopIndex; + int x = X; + int y = Y + i*ObjectHeight; + y--; + + /* + ** If the strip is scrolling, then the offset is adjusted accordingly. + */ + if (IsScrolling) { + y -= ObjectHeight - Slid; + } + + /* + ** Fetch the shape number for the object type located at this current working + ** slot. This shape pointer is used to draw the underlying graphic there. + */ + if ((unsigned)index < BuildableCount) { + ObjectTypeClass const * obj = NULL; + int spc = 0; + + if (Buildables[index].BuildableType != RTTI_SPECIAL) { + + obj = Fetch_Techno_Type(Buildables[index].BuildableType, Buildables[index].BuildableID); + if (obj) { + bool isbusy = false; + switch (Buildables[index].BuildableType) { + case RTTI_INFANTRYTYPE: + isbusy = (PlayerPtr->InfantryFactory != -1); + break; + + case RTTI_BUILDINGTYPE: + isbusy = (PlayerPtr->BuildingFactory != -1); + if (!BuildingTypeClass::As_Reference((StructType)Buildables[index].BuildableID).IsWall) { + remapper = PlayerPtr->Remap_Table(false, false); + } + break; + + case RTTI_UNITTYPE: + isbusy = (PlayerPtr->UnitFactory != -1); + switch (Buildables[index].BuildableID) { + case UNIT_MCV: + case UNIT_HARVESTER: + remapper = PlayerPtr->Remap_Table(false, false); + break; + + default: + remapper = PlayerPtr->Remap_Table(false, true); + break; + } + break; + + case RTTI_AIRCRAFTTYPE: + isbusy = (PlayerPtr->AircraftFactory != -1); + remapper = PlayerPtr->Remap_Table(false, true); + break; + } + shapefile = obj->Get_Cameo_Data(); + shapenum = 0; + if (Buildables[index].Factory != -1) { + factory = Factories.Raw_Ptr(Buildables[index].Factory); + production = true; + completed = factory->Has_Completed(); + stage = factory->Completion(); + darken = false; + } else { + production = false; +// darken = IsBuilding; + + /* + ** Darken the imagery if a factory of a matching type is + ** already busy. + */ + darken = isbusy; + } + } + + } else { + + spc = Buildables[index].BuildableID; + shapefile = Get_Special_Cameo(spc - 1); + shapenum = 0; + + switch (spc) { + case SPC_ION_CANNON: + production = true; + completed = PlayerPtr->IonCannon.Is_Ready(); + stage = PlayerPtr->IonCannon.Anim_Stage(); + darken = false; + break; + + case SPC_AIR_STRIKE: + production = true; + completed = PlayerPtr->AirStrike.Is_Ready(); + stage = PlayerPtr->AirStrike.Anim_Stage(); + darken = false; + break; + + case SPC_NUCLEAR_BOMB: + production = true; + completed = PlayerPtr->NukeStrike.Is_Ready(); + stage = PlayerPtr->NukeStrike.Anim_Stage(); + darken = false; + break; + } + } + + if (obj || spc) { + /* + ** If this item is flashing then take care of it. + ** + */ + if (Flasher == index && (Fetch_Stage() & 0x01)) { + remapper = Map.FadingLight; + } + + } else { + shapefile = LogoShapes; + shapenum = SB_BLANK; + } + } else { + shapefile = LogoShapes; + shapenum = SB_BLANK; + production = false; + } + + remapper = 0; + + /* + ** Now that the shape of the object at the current working slot has been found, + ** draw it and any graphic overlays as necessary. + ** + ** Dont draw blank shapes over the new 640x400 sidebar art - ST 5/1/96 6:01PM + */ + if (shapenum != SB_BLANK || shapefile != LogoShapes){ + IsTheaterShape = true; // This shape is theater specific + CC_Draw_Shape(shapefile, shapenum, + x-(WindowList[WINDOW_SIDEBAR][WINDOWX]*8)+LeftEdgeOffset, + y-WindowList[WINDOW_SIDEBAR][WINDOWY], + WINDOW_SIDEBAR, + SHAPE_NORMAL|SHAPE_WIN_REL| (remapper ? SHAPE_FADING : SHAPE_NORMAL), + remapper); + IsTheaterShape = false; + + + /* + ** Darken this object because it cannot be produced or is otherwise + ** unavailable. + */ + if (darken) { + CC_Draw_Shape(ClockShapes, 0, + x-(WindowList[WINDOW_SIDEBAR][WINDOWX]*8)+LeftEdgeOffset, + y-WindowList[WINDOW_SIDEBAR][WINDOWY], + WINDOW_SIDEBAR, + SHAPE_NORMAL|SHAPE_WIN_REL|SHAPE_GHOST, + NULL, ClockTranslucentTable); + } + } + + /* + ** Draw the overlapping clock shape if this is object is being constructed. + ** If the object is completed, then display "Ready" with no clock shape. + */ + if (production) { + if (completed) { + + /* + ** Display text showing that the object is ready to place. + */ + CC_Draw_Shape(ObjectTypeClass::PipShapes, PIP_READY, + (x-(WindowList[WINDOW_SIDEBAR][WINDOWX]*8))+LeftEdgeOffset+(ObjectWidth >> 1), + (y-WindowList[WINDOW_SIDEBAR][WINDOWY])+ObjectHeight-Get_Build_Frame_Height(ObjectTypeClass::PipShapes) -8, + WINDOW_SIDEBAR, SHAPE_CENTER); +// Fancy_Text_Print(TXT_READY, x+TEXT_X_OFFSET, y+TEXT_Y_OFFSET, TEXT_COLOR, TBLACK, TPF_6POINT|TPF_CENTER|TPF_NOSHADOW); + } else { + CC_Draw_Shape(ClockShapes, stage+1, + x-(WindowList[WINDOW_SIDEBAR][WINDOWX]*8)+LeftEdgeOffset, + y-WindowList[WINDOW_SIDEBAR][WINDOWY], + WINDOW_SIDEBAR, + SHAPE_NORMAL|SHAPE_WIN_REL|SHAPE_GHOST, + NULL, ClockTranslucentTable); + /* + ** Display text showing that the construction is temporarily on hold. + */ + if (factory && !factory->Is_Building()) { + CC_Draw_Shape(ObjectTypeClass::PipShapes, PIP_HOLDING, + (x-(WindowList[WINDOW_SIDEBAR][WINDOWX]*8))+LeftEdgeOffset+(ObjectWidth >> 1), + (y-WindowList[WINDOW_SIDEBAR][WINDOWY])+ObjectHeight-Get_Build_Frame_Height(ObjectTypeClass::PipShapes) - 8, // Moved up now that icons have names on them + WINDOW_SIDEBAR, SHAPE_CENTER); +// Fancy_Text_Print(TXT_HOLDING, x+TEXT_X_OFFSET, y+TEXT_Y_OFFSET, TEXT_COLOR, TBLACK, TPF_6POINT|TPF_CENTER|TPF_NOSHADOW); + } + } + } + } + } +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Recalc -- Revalidates the current sidebar list of objects. * + * * + * This routine will revalidate all the buildable objects in the sidebar. This routine * + * comes in handy when a factory has been destroyed, and the sidebar needs to reflect any * + * change that this requires. It checks every object to see if there is a factory available * + * that could produce it. If none can be found, then the object is removed from the * + * sidebar. * + * * + * INPUT: none * + * * + * OUTPUT: bool; The sidebar has changed as a result of this call? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 06/26/1995 JLB : Doesn't collapse sidebar when buildables removed. * + *=============================================================================================*/ +bool SidebarClass::StripClass::Recalc(void) +{ + int ok; + + if (Debug_Map || !BuildableCount) { + return(false); + } + + /* + ** Sweep through all objects listed in the sidebar. If any of those object can + ** not be created -- even in theory -- then they must be removed form the sidebar and + ** any current production must be abandoned. + */ + bool redraw = false; + for (int index = 0; index < BuildableCount; index++) { + TechnoTypeClass const * tech = Fetch_Techno_Type(Buildables[index].BuildableType, Buildables[index].BuildableID); + if (tech) { + ok = tech->Who_Can_Build_Me(true, false, PlayerPtr->Class->House) != NULL; + } else { + switch (Buildables[index].BuildableID) { + case SPC_ION_CANNON: + ok = PlayerPtr->IonCannon.Is_Present(); + break; + + case SPC_NUCLEAR_BOMB: + ok = PlayerPtr->NukeStrike.Is_Present(); + break; + + case SPC_AIR_STRIKE: + ok = PlayerPtr->AirStrike.Is_Present(); + break; + + default: + ok = false; + break; + } + +#ifdef OBSOLETE + } else { + switch (Buildables[index].BuildableID) { + case SPC_ION_CANNON: + ok = (PlayerPtr->BScan & STRUCTF_EYE) != 0 || PlayerPtr->IonOneTimeFlag; + if (!ok) { + PlayerPtr->Remove_Ion_Cannon(); + } + break; + + case SPC_NUCLEAR_BOMB: + ok = (PlayerPtr->BScan & STRUCTF_TEMPLE) != 0 && PlayerPtr->Has_Nuke_Device(); + ok = ok || PlayerPtr->NukeOneTimeFlag; + if (!ok) { + PlayerPtr->Remove_Nuke_Bomb(); + } + break; + + case SPC_AIR_STRIKE: +// ok = (PlayerPtr->BScan & STRUCTF_SAM) == 0; +// ok = !PlayerPtr->Does_Enemy_Building_Exist(STRUCT_SAM); + ok = (PlayerPtr->AirPresent /*&& !PlayerPtr->Does_Enemy_Building_Exist(STRUCT_SAM)*/) || PlayerPtr->AirOneTimeFlag; + if (!ok) { + PlayerPtr->Remove_Air_Strike(); + } + break; + + default: + ok = false; + break; + } +#endif + } + + if (!ok) { + + /* + ** If there was something in production, then abandon it before deleting the + ** factory manager. + */ + //if (Buildables[index].Factory != -1) { + //FactoryClass * factory = Factories.Raw_Ptr(Buildables[index].Factory); + //factory->Abandon(); + //delete factory; + //Buildables[index].Factory = -1; + //} +// Buildables[index].Factory = -1; + + /* + ** Removes this entry from the list. + */ + if (BuildableCount > 1 && index < BuildableCount-1) { + memcpy(&Buildables[index], &Buildables[index+1], sizeof(Buildables[0])*((BuildableCount-index)-1)); + } + TopIndex = 0; + IsToRedraw = true; + redraw = true; + BuildableCount--; + index--; + } + } + +#ifdef NEVER + /* + ** If there are no more buildable objects to display, make the sidebar go away. + */ + if (!BuildableCount) { + Map.SidebarClass::Activate(0); + } +#endif + return(redraw); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::SelectClass::SelectClass -- Default constructor. * + * * + * This is the default constructor for the button that controls the buildable cameos on * + * the sidebar strip. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The coordinates are set to zero by this routine. They must be set to the * + * correct values before this button will function. * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +SidebarClass::StripClass::SelectClass::SelectClass(void) : + ControlClass(0, 0, 0, 0, 0, LEFTPRESS|RIGHTPRESS|LEFTUP) +{ + int factor = Get_Resolution_Factor(); + + Strip = 0; + Index = 0; + Width = StripClass::OBJECT_WIDTH << factor; + Height = StripClass::OBJECT_HEIGHT << factor; +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::SelectClass:: -- Assigns special values to a buildable select but * + * * + * Use this routine to set custom buildable vars for this particular select button. It * + * uses this information to properly know what buildable object to start or stop production * + * on. * + * * + * INPUT: strip -- Reference to the strip that owns this buildable button. * + * * + * index -- The index (0 .. MAX_VISIBLE-1) of this button. This is used to let * + * the owning strip know what index this button refers to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::SelectClass::Set_Owner(StripClass & strip, int index) +{ + int factor = Get_Resolution_Factor(); + Strip = &strip; + Index = index; + X = strip.X; + Y = strip.Y + (index * (StripClass::OBJECT_HEIGHT << factor)); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::SelectClass:: -- Action function when buildable cameo is selected * + * * + * This function is called when the buildable icon (cameo) is clicked on. It handles * + * starting and stopping production as indicated. * + * * + * INPUT: flags -- The input event that triggered the call. * + * * + * key -- The keyboard value at the time of the input. * + * * + * OUTPUT: Returns with whether the input list should be scanned further. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +int SidebarClass::StripClass::SelectClass::Action(unsigned flags, KeyNumType & key) +{ + int index = Strip->TopIndex + Index; + RTTIType otype = Strip->Buildables[index].BuildableType; + int oid = Strip->Buildables[index].BuildableID; + int fnumber = Strip->Buildables[index].Factory; + + FactoryClass * factory = NULL; + ObjectTypeClass const * choice = NULL; + int spc = 0; + + /* + ** Determine the factory number that would apply to objects of the type + ** the mouse is currently addressing. This doesn't mean that the factory number + ** fetched is actually producing the indicated object, merely that that particular + ** kind of factory is specified by the "genfactory" value. This can be used to see + ** if the factory type is currently busy or not. + */ + int genfactory = -1; + switch (otype) { + case RTTI_INFANTRYTYPE: + genfactory = PlayerPtr->InfantryFactory; + break; + + case RTTI_UNITTYPE: + genfactory = PlayerPtr->UnitFactory; + break; + + case RTTI_AIRCRAFTTYPE: + genfactory = PlayerPtr->AircraftFactory; + break; + + case RTTI_BUILDINGTYPE: + genfactory = PlayerPtr->BuildingFactory; + break; + + default: + genfactory = -1; + break; + } + + Map.Override_Mouse_Shape(MOUSE_NORMAL); + + if (index < Strip->BuildableCount) { + if (otype != RTTI_SPECIAL) { + choice = Fetch_Techno_Type(otype, oid); + } else { + spc = oid; + } + + if (fnumber != -1) { + factory = Factories.Raw_Ptr(fnumber); + } + + } else { + Map.Help_Text(TXT_NONE); + } + + if (spc) { + /* + ** Display the help text if the mouse is over the button. + */ + if (flags & LEFTUP) { + switch (spc) { + case SPC_ION_CANNON: + Map.Help_Text(TXT_ION_CANNON, X, Y, CC_GREEN, true); + break; + + case SPC_NUCLEAR_BOMB: + Map.Help_Text(TXT_NUKE_STRIKE, X, Y, CC_GREEN, true); + break; + + case SPC_AIR_STRIKE: + Map.Help_Text(TXT_AIR_STRIKE, X, Y, CC_GREEN, true); + break; + } + flags &= ~LEFTUP; + } + + /* + ** A right mouse button signals "cancel". If we are in targetting + ** mode then we don't want to be any more. + */ + if (flags & RIGHTPRESS) { + Map.IsTargettingMode = false; + } + /* + ** A left mouse press signal "activate". If our weapon type is + ** available then we should activate it. + */ + if (flags & LEFTPRESS) { + switch (spc) { + case SPC_ION_CANNON: + if (PlayerPtr->IonCannon.Is_Ready()) { + Map.IsTargettingMode = spc; + Unselect_All(); + Speak(VOX_SELECT_TARGET); + } else { + PlayerPtr->IonCannon.Impatient_Click(); + } + break; + + case SPC_AIR_STRIKE: + if (PlayerPtr->AirStrike.Is_Ready()) { + Map.IsTargettingMode = spc; + Unselect_All(); + Speak(VOX_SELECT_TARGET); + } else { + PlayerPtr->AirStrike.Impatient_Click(); + } + break; + + case SPC_NUCLEAR_BOMB: + if (PlayerPtr->NukeStrike.Is_Ready()) { + Map.IsTargettingMode = spc; + Unselect_All(); + Speak(VOX_SELECT_TARGET); + } else { + PlayerPtr->NukeStrike.Impatient_Click(); + } + break; + } + } + + } else { + + if (choice) { + + /* + ** Display the help text if the mouse is over the button. + */ + if (flags & LEFTUP) { + Map.Help_Text(choice->Full_Name(), X, Y, CC_GREEN, true, choice->Cost_Of()); + flags &= ~LEFTUP; + } + + /* + ** A right mouse button signals "cancel". + */ + if (flags & RIGHTPRESS) { + + /* + ** If production is in progress, put it on hold. If production is already + ** on hold, then abandon it. Money will be refunded, the factory + ** manager deleted, and the object under construction is returned to + ** the free pool. + */ + if (factory) { + + /* + ** Cancels placement mode if the sidebar factory is abandoned or + ** suspended. + */ + if (Map.PendingObjectPtr && Map.PendingObjectPtr->Is_Techno()) { + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + Map.Set_Cursor_Shape(0); + } + + if (!factory->Is_Building()) { + Speak(VOX_CANCELED); + OutList.Add(EventClass(EventClass::ABANDON, otype, oid)); + } else { + Speak(VOX_SUSPENDED); + OutList.Add(EventClass(EventClass::SUSPEND, otype, oid)); + } + } + } + + if (flags & LEFTPRESS) { + + /* + ** If there is already a factory attached to this strip but the player didn't click + ** on the icon that has the attached factory, then say that the factory is busy and + ** ignore the click. + */ + if (fnumber == -1 && genfactory != -1) { + Speak(VOX_NO_FACTORY); + ControlClass::Action(flags, key); + return(true); + } + + if (factory) { + + /* + ** If this object is currently being built, then give a scold sound and text and then + ** bail. + */ + if (factory->Is_Building()) { + Speak(VOX_NO_FACTORY); + } else { + + /* + ** If production has completed, then attempt to have the object exit + ** the factory or go into placement mode. + */ + if (factory->Has_Completed()) { + + TechnoClass * pending = factory->Get_Object(); + if (!pending && factory->Get_Special_Item()) { + Map.IsTargettingMode = true; + } else { + BuildingClass * builder = pending->Who_Can_Build_Me(false, false); + if (!builder) { + OutList.Add(EventClass(EventClass::ABANDON, otype, oid)); + Speak(VOX_NO_FACTORY); + } else { + + /* + ** If the completed object is a building, then change the + ** game state into building placement mode. This fact is + ** not transmitted to any linked computers until the moment + ** the building is actually placed down. + */ + if (pending->What_Am_I() == RTTI_BUILDING) { + PlayerPtr->Manual_Place(builder, (BuildingClass *)pending); + } else { + + /* + ** For objects that can leave the factory under their own + ** power, queue this event and process through normal house + ** production channels. + */ + OutList.Add(EventClass(EventClass::PLACE, otype, -1)); + } + } + } + } else { + + /* + ** The factory must have been in a suspended state. Resume construction + ** normally. + */ + Speak(VOX_BUILDING); + OutList.Add(EventClass(EventClass::PRODUCE, Strip->Buildables[index].BuildableType, Strip->Buildables[index].BuildableID)); + } + } + + } else { + + /* + ** If this side strip is already busy with production, then ignore the + ** input and announce this fact. + */ +// if (Strip->IsBuilding) { +// Speak(VOX_NO_FACTORY); +// } else { + Speak(VOX_BUILDING); + OutList.Add(EventClass(EventClass::PRODUCE, Strip->Buildables[index].BuildableType, Strip->Buildables[index].BuildableID)); +// } + } + } + } else { + flags = 0; + } + } + + ControlClass::Action(flags, key); + return(true); +} + + +/*********************************************************************************************** + * SidebarClass::SBGadgetClass::Action -- Special function that controls the mouse over the si * + * * + * This routine is called whenever the mouse is over the sidebar. It makes sure that the * + * mouse is always the normal shape while over the sidebar. * + * * + * INPUT: flags -- The event flags that resuled in this routine being called. * + * * + * key -- Reference the keyboard code that may be present. * + * * + * OUTPUT: Returns that no further keyboard processing is necessary. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/28/1995 JLB : Created. * + *=============================================================================================*/ +int SidebarClass::SBGadgetClass::Action(unsigned , KeyNumType & ) +{ + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + return(true); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Factory_Link -- Links a factory to a sidebar button. * + * * + * This routine will link the specified factory to this sidebar strip. The exact button to * + * link to is determined from the object type and id specified. A linked button is one that * + * will show appropriate construction animation (clock shape) that matches the state of * + * the factory. * + * * + * INPUT: factory -- The factory number to link to the sidebar. * + * * + * type -- The object type that this factory refers to. * + * * + * id -- The object sub-type that this factory refers to. * + * * + * OUTPUT: Was the factory successfully attached? Failure would indicate that there is no * + * object of the specified type and sub-type in the sidebar list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::StripClass::Factory_Link(int factory, RTTIType type, int id) +{ + for (int index = 0; index < BuildableCount; index++) { + if (Buildables[index].BuildableType == type && Buildables[index].BuildableID == id) { + Buildables[index].Factory = factory; + IsBuilding = true; + + /* + ** Flag that all the icons on this strip need to be redrawn + */ + Flag_To_Redraw(); + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * SidebarClass::Abandon_Production -- Stops production of the object specified. * + * * + * This routine is used to abandon production of the object specified. The factory will * + * be completely disabled by this call. * + * * + * INPUT: type -- The object type that is to be abandoned. The sub-type is not needed * + * since it is presumed there can be only one type in production at any * + * one time. * + * * + * factory -- The factory number that is doing the production. * + * * + * OUTPUT: Was the factory successfully abandoned? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Abandon_Production(RTTIType type, int factory) +{ + return(Column[Which_Column(type)].Abandon_Production(factory)); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Abandon_Produ -- Abandons production associated with sidebar. * + * * + * Production of the object associated with this sidebar is abandoned when this routine is * + * called. * + * * + * INPUT: factory -- The factory index that is to be suspended. * + * * + * OUTPUT: Was the production abandonment successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + * 08/06/1995 JLB : More intelligent abandon logic for multiple factories. * + *=============================================================================================*/ +bool SidebarClass::StripClass::Abandon_Production(int factory) +{ + bool noprod = true; + bool abandon = false; + for (int index = 0; index < BuildableCount; index++) { + if (Buildables[index].Factory == factory) { + Factories.Raw_Ptr(factory)->Abandon(); + Buildables[index].Factory = -1; + abandon = true; + } else { + if (Buildables[index].Factory != -1) { + noprod = false; + } + } + } + + /* + ** If there was a change to the strip, then flag the strip to be redrawn. + */ + if (abandon) { + Flag_To_Redraw(); + } + + /* + ** If there is no production whatsoever on this strip, then flag it so. + */ + if (noprod) { + IsBuilding = false; + } + return(abandon); +} + + diff --git a/SIDEBAR.H b/SIDEBAR.H new file mode 100644 index 0000000..cb7ccd9 --- /dev/null +++ b/SIDEBAR.H @@ -0,0 +1,397 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\sidebar.h_v 2.18 16 Oct 1995 16:45:24 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SIDEBAR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 20, 1994 * + * * + * Last Update : October 20, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SIDEBAR_H +#define SIDEBAR_H + +#include "function.h" +#include "power.h" +#include "factory.h" + +class SidebarClass: public PowerClass +{ + public: + /* + ** These constants are used to control the sidebar rendering. They are instantiated + ** as enumerations since C++ cannot use "const" in this context. + */ + int SideX; // x position for side bar + int SideY; // y position for side bar + int SideBarWidth; // width of the sidebar + int SideWidth; // width of the sidebar + int SideHeight; // height of the sidebar + int TopHeight; // height of top of sidebar + int MaxVisible; // max production icons visible + int ButtonOneWidth; // Button width. + int ButtonTwoWidth; // Button width. + int ButtonThreeWidth; // Button width. + int ButtonHeight; // Button width. + + enum SideBarClassEnums { + BUTTON_ACTIVATOR=100, // Button ID for the activator. + SIDEBARWIDTH = 80, +#if 0 + SIDE_X=RADAR_X, // The X position of sidebar upper left corner. + SIDE_Y=RADAR_Y+RADAR_HEIGHT, // The Y position of sidebar upper left corner. + SIDE_WIDTH=320-SIDE_X, // Width of the entire sidebar (in pixels). + SIDE_HEIGHT=200-SIDE_Y, // Height of the entire sidebar (in pixels). + TOP_HEIGHT=13, // Height of top section (with repair/sell buttons). + COLUMN_ONE_X=SIDE_X+8, // Sidestrip upper left coordinates... + COLUMN_ONE_Y=SIDE_Y+TOP_HEIGHT, + COLUMN_TWO_X=COLUMN_ONE_X+((SIDE_WIDTH-16)/2)+3, + COLUMN_TWO_Y=SIDE_Y+TOP_HEIGHT, +#if (GERMAN | FRENCH) +//BGA: changes to all buttons + BUTTON_ONE_WIDTH=20, // Button width. + BUTTON_TWO_WIDTH=27, // Button width. + BUTTON_THREE_WIDTH=26, // Button width. + BUTTON_HEIGHT=9, // Button height. + BUTTON_ONE_X=SIDE_X+2, // Left button X coordinate. + BUTTON_ONE_Y=SIDE_Y+2, // Left button Y coordinate. + BUTTON_TWO_X=SIDE_X+24, // Right button X coordinate. + BUTTON_TWO_Y=SIDE_Y+2, // Right button Y coordinate. + BUTTON_THREE_X=SIDE_X+53, // Right button X coordinate. + BUTTON_THREE_Y=SIDE_Y+2, // Right button Y coordinate. +#else + BUTTON_ONE_WIDTH=32, // Button width. + BUTTON_TWO_WIDTH=20, // Button width. + BUTTON_THREE_WIDTH=20, // Button width. + BUTTON_HEIGHT=9, // Button height. + BUTTON_ONE_X=SIDE_X+2, // Left button X coordinate. + BUTTON_ONE_Y=SIDE_Y+2, // Left button Y coordinate. + BUTTON_TWO_X=SIDE_X+36, // Right button X coordinate. + BUTTON_TWO_Y=SIDE_Y+2, // Right button Y coordinate. + BUTTON_THREE_X=SIDE_X+58, // Right button X coordinate. + BUTTON_THREE_Y=SIDE_Y+2, // Right button Y coordinate. +#endif + BUTTON_ONE_WIDTH=32, // Button width. + BUTTON_TWO_WIDTH=20, // Button width. + BUTTON_THREE_WIDTH=20, // Button width. + BUTTON_HEIGHT=9, // Button height. +#endif + COLUMNS=2, // Number of side strips on sidebar. + }; + + SidebarClass(void); + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time inits + virtual void Init_Clear(void); // Clears all to known state + virtual void Init_IO(void); // Inits button list + virtual void Init_Theater(TheaterType theater); // Theater-specific inits + + virtual void AI(KeyNumType & input, int x, int y); + virtual void Draw_It(bool complete); + virtual void Refresh_Cells(CELL cell, short const *list); + + bool Abandon_Production(RTTIType type, int factory); + bool Activate(int control); + bool Add(RTTIType type, int ID); + bool Sidebar_Click(KeyNumType & input, int x, int y); + void Recalc(void); + bool Factory_Link(int factory, RTTIType type, int id); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Each side strip is managed by this class. It handles all strip specific + ** actions. + */ + class StripClass : public StageClass + { + class SelectClass : public ControlClass + { + public: + SelectClass(void); + + void Set_Owner(StripClass & strip, int index); + StripClass * Strip; + int Index; + + protected: + virtual int Action(unsigned flags, KeyNumType & key); + }; + + public: + int ObjectWidth; + int ObjectHeight; + int StripWidth; + int LeftEdgeOffset; + int ButtonSpacingOffset; + + + StripClass(void); + bool Add(RTTIType type, int ID); + bool Abandon_Production(int factory); + bool Scroll(bool up); + bool AI(KeyNumType & input, int x, int y); + void Draw_It(bool complete); + void One_Time(int id); + void Init_Clear(void); + void Init_IO(int id); + void Init_Theater(TheaterType theater); + bool Recalc(void); + void Activate(void); + void Deactivate(void); + void Flag_To_Redraw(void); + bool Factory_Link(int factory, RTTIType type, int id); + void const * Get_Special_Cameo(int type); + + /* + ** File I/O. + */ + bool Load(FileClass & file); + bool Save(FileClass & file); + void Code_Pointers(void); + void Decode_Pointers(void); + + /* + ** Working numbers used when rendering and processing the side strip. + */ + enum SideBarGeneralEnums { + BUTTON_UP=200, + BUTTON_DOWN=210, + BUTTON_SELECT=220, + MAX_BUILDABLES=30, // Maximum number of object types in sidebar. + OBJECT_HEIGHT=24, // Pixel height of each buildable object. + OBJECT_WIDTH=32, // Pixel width of each buildable object. + STRIP_WIDTH=35, // Width of strip (not counting border lines). + MAX_VISIBLE=4, // Number of object slots visible at any one time. + SCROLL_RATE=8, // The pixel jump while scrolling (larger is faster). + BUTTON_SPACING_OFFSET = 4, // spacing info for buttons + UP_X_OFFSET=2, // Scroll up arrow coordinates. + UP_Y_OFFSET=MAX_VISIBLE*OBJECT_HEIGHT+1, + DOWN_X_OFFSET=18, // Scroll down arrow coordinates. + DOWN_Y_OFFSET=MAX_VISIBLE*OBJECT_HEIGHT+1, + BUTTON_WIDTH=16, // Width of the mini-scroll button. + BUTTON_HEIGHT=12, // Height of the mini-scroll button. + //LEFT_EDGE_OFFSET=2, // Offset from left edge for building shapes. + TEXT_X_OFFSET=18, // X offset to print "ready" text. + TEXT_Y_OFFSET=15, // Y offset to print "ready" text. + TEXT_COLOR=255, // Color to use for the "Ready" text. + //BUTTON_SPACING_OFFSET = 4, // spacing info for buttons + //LEFT_EDGE_OFFSET=0, // Offset from left edge for building shapes. + //BUTTON_SPACING_OFFSET = 0, // spacing info for buttons + + }; + + /* + ** This is the coordinate of the upper left corner that this side strip + ** uses for rendering. + */ + int X,Y; + + /* + ** This is a unique identifier for the sidebar strip. Using this identifier, + ** it is possible to differentiate the button messages that arrive from the + ** common input button list. It >MUST< be equal to the strip's index into + ** the Column[] array, because the strip uses it to access the stripclass + ** buttons. + */ + int ID; + + /* + ** Shape numbers for the shapes in the STRIP.SHP file. + */ + enum SideBarStipShapeEnums { + SB_BLANK, // The blank rectangle to use if there are no objects present. + SB_FRAME + }; + + /* + ** If this particular side strip needs to be redrawn, then this flag + ** will be true. + */ + unsigned IsToRedraw:1; + + /* + ** If construction is in progress (no other objects in this strip can + ** be started), then this flag will be true. It will be cleared when + ** the strip is free to start production again. + */ + unsigned IsBuilding:1; + + /* + ** This controls the sidebar slide direction. If this is true, then the sidebar + ** will scroll downward -- revealing previous objects. + */ + unsigned IsScrollingDown:1; + + /* + ** If the sidebar is scrolling, then this flag is true. Otherwise it is false. + */ + unsigned IsScrolling:1; + + /* + ** This is the object (sidebar slot) that is flashing. Only one slot can be flashing + ** at any one instant. This is usually the result of a click on the slot and construction + ** has commenced. + */ + int Flasher; + + /* + ** As the sidebar scrolls up and down, this variable holds the index for the topmost + ** visible sidebar slot. + */ + int TopIndex; + + /* + ** This is the queued scroll direction and amount. The sidebar + ** will scroll the number of slots indicated by this value. This + ** value is set according to the scroll buttons. + */ + int Scroller; + + /* + ** The sidebar has smooth scrolling. This is the number of pixels the sidebar + ** has slide down. Thus, if this value were 5, then there would be 5 pixels of + ** the TopIndex-1 sidebar object visible. When the Slid value reaches 24, then + ** the value resets to zero and the TopIndex is decremented. For sliding in the + ** opposite direction, change the IsScrollingDown flag. + */ + int Slid; + + /* + ** This is the count of the number of sidebar slots that are active. + */ + int BuildableCount; + + /* + ** This is the array of buildable object types. This array is sorted in the order + ** that it is to be displayed. This array keeps track of which objects are building + ** and ready to be placed. The very nature of this method precludes simultaneous + ** construction of the same object type. + */ + typedef struct BuildType { + int BuildableID; + RTTIType BuildableType; + int Factory; // Production manager. + } BuildType; + BuildType Buildables[MAX_BUILDABLES]; + + /* + ** Pointer to the shape data for small versions of the logos. These are used as + ** placeholder pieces on the side bar. + */ + static void const * LogoShapes; + + /* + ** This points to the animation sequence of frames used to mark the passage of time + ** as an object is undergoing construction. + */ + static void const * ClockShapes; + + /* + ** This points to the animation sequence which deals with special + ** shapes which handle non-production based icons. + */ + static void const * SpecialShapes[3]; + + /* + ** This is the last theater that the special palette remap table was loaded + ** for. If the current theater differs from this recorded value, then the + ** remap tables are reloaded. + */ + static TheaterType LastTheater; + + static ShapeButtonClass UpButton[COLUMNS]; + static ShapeButtonClass DownButton[COLUMNS]; + static SelectClass SelectButton[COLUMNS][MAX_VISIBLE]; + + /* + ** This points to the shapes that are used for the clock overlay. This displays + ** progress of construction. + */ + static char ClockTranslucentTable[(1+1)*256]; + + } Column[COLUMNS]; + + + /* + ** If the sidebar is active then this flag is true. + */ + unsigned IsSidebarActive:1; + + /* + ** This flag tells the rendering system that the sidebar needs to be redrawn. + */ + unsigned IsToRedraw:1; + + class SBGadgetClass: public GadgetClass { + public: +// SBGadgetClass(void) : GadgetClass(SIDE_X+8, SIDE_Y, SIDE_WIDTH-1, SIDE_HEIGHT-1, LEFTUP) {}; + SBGadgetClass(void) : GadgetClass(0,0,0,0,LEFTUP) {}; + + protected: + virtual int Action(unsigned flags, KeyNumType & key); + }; + + /* + ** This is the button that is used to collapse and expand the sidebar. + ** These buttons must be available to derived classes, for Save/Load. + */ + static ShapeButtonClass Repair; + static ShapeButtonClass Upgrade; + static ShapeButtonClass Zoom; + static SBGadgetClass Background; + + /* + ** Pointer to the shape data for the sidebar + */ + static void const * SidebarShape1; + static void const * SidebarShape2; + + + private: + bool Activate_Repair(int control); + bool Activate_Upgrade(int control); + bool Activate_Demolish(int control); + bool Scroll(bool up, int column); + int Which_Column(RTTIType type); + + unsigned IsRepairActive:1; + unsigned IsUpgradeActive:1; + unsigned IsDemolishActive:1; +}; + +#endif diff --git a/SLIDER.CPP b/SLIDER.CPP new file mode 100644 index 0000000..d6c68db --- /dev/null +++ b/SLIDER.CPP @@ -0,0 +1,408 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\slider.cpv 2.17 16 Oct 1995 16:51:54 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SLIDER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 16, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SliderClass::Action -- Handles input processing for the slider. * + * SliderClass::Bump -- Bumps the slider one "thumb size" up or down. * + * SliderClass::Recalc_Thumb -- Recalculates the thumb pixel size and starting offset. * + * SliderClass::Set_Maximum -- Sets the maximum value for this slider. * + * SliderClass::Set_Thumb_Size -- Sets the size fo the thumb in "slider units". * + * SliderClass::Set_Value -- Sets the current thumb position for the slider. * + * SliderClass::SliderClass -- Normal constructor for a slider (with thumb) gadget. * + * SliderClass::Step -- Steps the slider one value up or down. * + * SliderClass::Draw_Thumb -- Draws the "thumb" for this slider. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "slider.h" + + +/*********************************************************************************************** + * SliderClass::SliderClass -- Normal constructor for a slider (with thumb) gadget. * + * * + * This is the normal constructor for the slider gadget. * + * * + * INPUT: id -- The ID number to assign to this gadget. * + * x,y -- The pixel coordinate of the upper left corner for this gadget. * + * w,h -- The width and height of the slider gadget. The slider automatically * + * adapts for horizontal or vertical operation depending on which of these * + * dimensions is greater. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +SliderClass::SliderClass(unsigned id, int x, int y, int w, int h, int belong_to_list) + : GaugeClass(id, x, y, w, h) +{ + BelongToList = belong_to_list ? true : false; + + PlusGadget = 0; + MinusGadget = 0; + if (!BelongToList) { + PlusGadget = new ShapeButtonClass(id, MixFileClass::Retrieve("BTN-PLUS.SHP"), X+Width+2, Y); + MinusGadget = new ShapeButtonClass(id, MixFileClass::Retrieve("BTN-MINS.SHP"), X-6, Y); + + if (PlusGadget) { + PlusGadget->Make_Peer(*this); + PlusGadget->Add(*this); + PlusGadget->Flag_To_Redraw(); + } + if (MinusGadget) { + MinusGadget->Make_Peer(*this); + MinusGadget->Add(*this); + MinusGadget->Flag_To_Redraw(); + } + } + Set_Thumb_Size(1); + Recalc_Thumb(); + + /* + ** Gauges have at least 2 colors, but sliders should only have one. + */ + IsColorized = 0; +} + + +virtual SliderClass::~SliderClass(void) +{ + if (PlusGadget) { + delete PlusGadget; + PlusGadget = 0; + } + if (MinusGadget) { + delete MinusGadget; + MinusGadget = 0; + } +} + + +/*********************************************************************************************** + * SliderClass::Set_Maximum -- Sets the maximum value for this slider. * + * * + * This sets the maximum value that the slider can be set at. The maximum value controls * + * the size of the thumb and the resolution of the thumb's movement. * + * * + * INPUT: value -- The value to set for the slider's maximum. * + * OUTPUT: bool; Was the maximum value changed? A false indicates a set to the value it * + * is currently set to already. * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Set_Maximum(int value) +{ + if (GaugeClass::Set_Maximum(value)) { + Recalc_Thumb(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SliderClass::Set_Thumb_Size -- Sets the size fo the thumb in "slider units". * + * * + * This routine will set the size of the thumb as it relates to the maximum value the * + * slider can achieve. This serves to display a proportionally sized thumb as well as * + * control how the slider "bumps" up or down. * + * * + * INPUT: value -- The new value of the thumb. It should never be larger than the slider * + * maximum. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void SliderClass::Set_Thumb_Size(int value) +{ + Thumb = MIN(value, MaxValue); + Thumb = MAX(Thumb, 1); + Flag_To_Redraw(); + Recalc_Thumb(); +} + + +/*********************************************************************************************** + * SliderClass::Set_Value -- Sets the current thumb position for the slider. * + * * + * This routine will set the thumb position for the slider. * + * * + * INPUT: value -- The position to set the slider. This position is relative to the maximum * + * value for the slider. * + * * + * OUTPUT: bool; Was the slider thumb position changed at all? * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Set_Value(int value) +{ + value = MIN(value, MaxValue-Thumb); + + if (GaugeClass::Set_Value(value)) { + Recalc_Thumb(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SliderClass::Recalc_Thumb -- Recalculates the thumb pixel size and starting offset. * + * * + * This takes the current thumb logical size and starting value and calculates the pixel * + * size and starting offset accordingly. This function should be called whenever one of * + * these elements has changed. * + * * + * INPUT: none * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void SliderClass::Recalc_Thumb(void) +{ + int length = IsHorizontal ? Width : Height; + int size = Fixed_To_Cardinal(length, Cardinal_To_Fixed(MaxValue, Thumb)); + ThumbSize = MAX(size, 4); + int start = Fixed_To_Cardinal(length, Cardinal_To_Fixed(MaxValue, CurValue)); + ThumbStart = MIN(start, length-ThumbSize); +} + + +/*********************************************************************************************** + * SliderClass::Action -- Handles input processing for the slider. * + * * + * This routine is called when a qualifying input event has occured. This routine will * + * process that event and make any adjustments to the slider as necessary. * + * * + * INPUT: flags -- Flag bits that tell the input event that caused this function to * + * be called. * + * key -- Reference to the key that caused the input event. * + * OUTPUT: bool; Was the event consumed and further processing of the gadget list should be * + * aborted? * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Action(unsigned flags, KeyNumType &key) +{ + /* + ** Handle the mouse click in a special way. If the click was not on the thumb, then + ** jump the thumb position one "step" in the appropriate direction. Otherwise, let normal + ** processing take place -- the slider then "sticks" and the thumb moves according to + ** mouse position. + */ + if (flags & LEFTPRESS) { + int mouse; // Mouse pixel position. + int edge; // Edge of slider. + + if (IsHorizontal) { + mouse = Get_Mouse_X(); + edge = X; + } else { + mouse = Get_Mouse_Y(); + edge = Y; + } + edge += 1; + + /* + ** Clicking outside the thumb: invoke parent's Action to process flags etc, + ** but turn off the event & return true so processing stops at this button. + */ + if (mouse < edge+ThumbStart) { + Bump(true); + GaugeClass::Action(0, key); + key = KN_NONE; + return(true); + } else { + if (mouse > edge+ThumbStart+ThumbSize) { + Bump(false); + GaugeClass::Action(0, key); + key = KN_NONE; + return(true); + } else { + GaugeClass::Action(flags, key); + key = KN_NONE; + return(true); + } + } + } + + /* + ** CHANGE GAUGECLASS::ACTION -- REMOVE (LEFTRELEASE) FROM IF STMT + */ + return(GaugeClass::Action(flags, key)); +} + + +/*********************************************************************************************** + * SliderClass::Bump -- Bumps the slider one "thumb size" up or down. * + * * + * This support function will bump the slider one "step" or the size of the thumb up or * + * down as specified. It is typically called when the slider is clicked outside of the * + * thumb region but still inside of the slider. * + * * + * INPUT: up -- Should the bump be to increase the current position? * + * OUTPUT: bool; Was the slider changed at all? A false indicates that the slider is already * + * at one end or the other. * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Bump(int up) +{ + if (up) { + return(Set_Value(CurValue - Thumb)); + } + return(Set_Value(CurValue + Thumb)); +} + + +/*********************************************************************************************** + * SliderClass::Step -- Steps the slider one value up or down. * + * * + * This routine will move the slider thumb one step in the direction specified. * + * * + * INPUT: up -- Should the step be up (i.e., forward)? * + * OUTPUT: bool; Was the slider changed at all? A false indicates that the slider is already * + * at one end or the other. * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Step(int up) +{ + if (up) { + return(Set_Value(CurValue - 1)); + } + return(Set_Value(CurValue + 1)); +} + + +/*********************************************************************************************** + * SliderClass::Draw_Thumb -- Draws the "thumb" for this slider. * + * * + * This will draw the thumb graphic for this slider. Sometimes the thumb requires special * + * drawing, thus the need for this function separate from the normal Draw_Me function. * + * * + * INPUT: none * + * OUTPUT: none * + * WARNINGS: The mouse is guaranteed to be hidden when this routine is called. * + * HISTORY: 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void SliderClass::Draw_Thumb(void) +{ + if (IsHorizontal) { + Draw_Box(X+ThumbStart, Y, ThumbSize, Height, BOXSTYLE_GREEN_RAISED, true); + } else { + Draw_Box(X, Y+ThumbStart, Width, ThumbSize, BOXSTYLE_GREEN_RAISED, true); + } +} + + +/*********************************************************************************************** + * SliderClass::Draw_Me -- Draws the body of the gauge. * + * * + * This routine will draw the body of the gauge if necessary. * + * * + * INPUT: forced -- Should the gauge be redrawn regardless of the current redraw flag? * + * OUTPUT: bool; Was the gauge redrawn? * + * WARNINGS: none * + * HISTORY: 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Draw_Me(int forced) +{ + if (BelongToList) { + if (ControlClass::Draw_Me(forced)) { + + /* + ===================== Hide the mouse ===================== + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + /* + =========== Draw the body & set text color =============== + */ + Draw_Box (X, Y, Width, Height, BOXSTYLE_GREEN_DOWN, true); +// if (IsHorizontal) { +// LogicPage->Fill_Rect(X, Y+1, X+Width-1, Y+Height-2, 141); +// LogicPage->Draw_Line(X, Y, X+Width-1, Y, 140); // top +// LogicPage->Draw_Line(X, Y+Height, X+Width, Y+Height, 159); // bottom +// } else { +// LogicPage->Fill_Rect(X+1, Y, X+Width-2, Y+Height-1, 141); +// LogicPage->Draw_Line(X, Y, X, Y+Height, 140); // left +// LogicPage->Draw_Line(X+Width-1, Y, X+Width-1, Y+Height, 159); // right +// } + Draw_Thumb(); + + /* + =================== Display the mouse =================== + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + } + + /* + ** If it does not belong to a listbox... + */ + return(GaugeClass::Draw_Me(forced)); +} + + +/*********************************************************************************************** + * SliderClass::Peer_To_Peer -- A peer gadget was touched -- make adjustments. * + * * + * This routine is called when one of the peer gadgets (the scroll arrows or the slider) * + * was touched in some fashion. This routine will sort out whom and why and then make * + * any necessary adjustments to the list box. * + * * + * INPUT: flags -- The event flags that affected the peer gadget. * + * key -- The key value at the time of the event. * + * whom -- Which gadget is being touched. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void SliderClass::Peer_To_Peer(unsigned flags, KeyNumType & , ControlClass & whom) +{ + if (flags & LEFTRELEASE) { + if (&whom == PlusGadget) { + Step(false); + } + if (&whom == MinusGadget) { + Step(true); + } + } +} + + diff --git a/SLIDER.H b/SLIDER.H new file mode 100644 index 0000000..5a9f995 --- /dev/null +++ b/SLIDER.H @@ -0,0 +1,110 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\slider.h_v 2.16 16 Oct 1995 16:45:38 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SLIDER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SLIDER_H +#define SLIDER_H + +#include "gauge.h" +#include "shapebtn.h" + +/*************************************************************************** + * SliderClass -- Like a Windows ListBox structure * + * * + * INPUT: int id-- id of gadget * + * int x -- x position of gadget * + * int y -- y position of gadget * + * int w -- width of gadget * + * int h -- height of gadget * + * int belong_to_list -- does this slider go with a listclass? * + * * + * OUTPUT: none. * + * WARNINGS: * + * HISTORY: 01/03/1995 MML : Created. * + *=========================================================================*/ +class SliderClass : public GaugeClass +{ + public: + SliderClass(unsigned id, int x, int y, int w, int h, int belong_to_list=false); + virtual ~SliderClass(void); +// static SliderClass * Create_One_Of(unsigned id, int x, int y, int w, int h, int belong_to_list=false); + + virtual void Set_Thumb_Size(int value); + virtual int Set_Maximum(int value); + virtual int Set_Value(int); + virtual int Bump(int up); + virtual int Step(int up); + virtual int Draw_Me(int forced); + virtual void Peer_To_Peer(unsigned flags, KeyNumType & key, ControlClass & whom); + + virtual int Thumb_Pixels(void) { return (ThumbSize);} + + protected: + + /* + ** If the slider bar has been created, these point to the respective gadgets + ** that it is composed of. + */ + ShapeButtonClass * PlusGadget; + ShapeButtonClass * MinusGadget; + + /* + ** If I belong to a listbox, I have to draw myself differently... + **/ + unsigned BelongToList:1; + + /* + ** This is the logical size of the thumb. This value is used when drawing + ** the thumb imagery. It is also the amount that is bumped when the + ** Bump() function is called. (This value is in application units.) + */ + int Thumb; + + /* + ** This is the current thumb pixel size and starting offset from beginning + ** of slider region. (These values are in pixels.) + */ + int ThumbSize; + int ThumbStart; // x or y position for the thumb + + virtual int Action(unsigned flags, KeyNumType &key); + virtual void Draw_Thumb(void); + + private: + void Recalc_Thumb(void); +}; + +#endif diff --git a/SMUDGE.CPP b/SMUDGE.CPP new file mode 100644 index 0000000..e3f1110 --- /dev/null +++ b/SMUDGE.CPP @@ -0,0 +1,406 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\smudge.cpv 2.18 16 Oct 1995 16:52:06 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SMUDGE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 9, 1994 * + * * + * Last Update : July 24, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SmudgeClass::Init -- Initialize the smudge tracking system. * + * SmudgeClass::SmudgeClass -- Constructor for smudge objects. * + * SmudgeClass::operator delete -- Deletes the smudge from the tracking system. * + * SmudgeClass::operator new -- Creator of smudge objects. * + * SmudgeClass::Mark -- Marks a smudge down on the map. * + * SmudgeClass::Read_INI -- Reads smudge data from an INI file. * + * SmudgeClass::Write_INI -- Writes the smudge data to an INI file. * + * SmudgeClass::Disown -- Disowns (removes) a building bib piece. * + * SmudgeClass::Validate -- validates smudge pointer * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "smudge.h" + + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * SmudgeClass::VTable; + +HousesType SmudgeClass::ToOwn = HOUSE_NONE; + + +/*********************************************************************************************** + * SmudgeClass::Validate -- validates smudge pointer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int SmudgeClass::Validate(void) const +{ + int num; + + num = Smudges.ID(this); + if (num < 0 || num >= SMUDGE_MAX) { + Validate_Error("SMUDGE"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * SmudgeClass::operator new -- Creator of smudge objects. * + * * + * This routine will allocate a smudge object from the smudge tracking pool. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to a newly allocated smudge object. If one couldn't be * + * found, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void * SmudgeClass::operator new(size_t ) +{ + void * ptr = Smudges.Allocate(); + if (ptr) { + ((SmudgeClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * SmudgeClass::operator delete -- Deletes the smudge from the tracking system. * + * * + * This routine is used to remove the smudge from the tracking system. * + * * + * INPUT: ptr -- Pointer to the smudge to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeClass::operator delete(void *ptr) +{ + if (ptr) { + ((SmudgeClass *)ptr)->IsActive = false; + } + Smudges.Free((SmudgeClass *)ptr); +} + + +/*********************************************************************************************** + * SmudgeClass::SmudgeClass -- Constructor for smudge objects. * + * * + * This is the typical constructor for smudge objects. If the position to place the * + * smudge is not given, then the smudge will be initialized in a limbo state. If the * + * smudge is placed on the map, then this operation causes the smudge object itself to be * + * deleted and special map values updated to reflect the presence of a smudge. * + * * + * INPUT: type -- The type of smudge to construct. * + * * + * pos -- The position to place the smudge. If -1, then the smudge is initialized * + * into a limbo state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +SmudgeClass::SmudgeClass(SmudgeType type, COORDINATE pos, HousesType house) : + Class(&SmudgeTypeClass::As_Reference(type)) +{ + if (pos != -1) { + ToOwn = house; + if (!Unlimbo(pos)) { + delete this; + } + ToOwn = HOUSE_NONE; + } +} + + +/*********************************************************************************************** + * SmudgeClass::Init -- Initialize the smudge tracking system. * + * * + * This routine is used during the scenario clearing process to initialize the smudge * + * object tracking system to a null state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeClass::Init(void) +{ + SmudgeClass *ptr; + + Smudges.Free_All(); + + ptr = new SmudgeClass(); + VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0]; + delete ptr; +} + + +/*********************************************************************************************** + * SmudgeClass::Mark -- Marks a smudge down on the map. * + * * + * This routine will place the smudge on the map. If the map cell allows. * + * * + * INPUT: mark -- The type of marking to perform. Only MARK_DOWN is supported. * + * * + * OUTPUT: bool; Was the smudge marked successfully? Failure occurs if the smudge isn't * + * marked DOWN. * + * * + * WARNINGS: The smudge object is DELETED by this routine. * + * * + * HISTORY: * + * 09/22/1994 JLB : Created. * + * 12/23/1994 JLB : Checks low level legality before proceeding. * + *=============================================================================================*/ +bool SmudgeClass::Mark(MarkType mark) +{ + Validate(); + if (ObjectClass::Mark(mark)) { + if (mark == MARK_DOWN) { + CELL origin = Coord_Cell(Coord); + + for (int w = 0; w < Class->Width; w++) { + for (int h = 0; h < Class->Height; h++) { + CELL newcell = origin + w + (h*MAP_CELL_W); + if (Map.In_Radar(newcell)) { + CellClass * cell = &Map[newcell]; + + if (Class->IsBib) { + cell->Smudge = Class->Type; + cell->SmudgeData = w + (h*Class->Width); + cell->Owner = ToOwn; + } else { + if (cell->Is_Generally_Clear()) { + if (Class->IsCrater && cell->Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference(cell->Smudge).IsCrater) { + cell->SmudgeData++; + cell->SmudgeData = (int)MIN((int)cell->SmudgeData, (int)4); + } + + if (cell->Smudge == SMUDGE_NONE) { + + /* + ** Special selection of a crater that starts as close to the + ** specified coordinate as possible. + */ + if (Class->IsCrater) { + cell->Smudge = (SmudgeType)(SMUDGE_CRATER1 + CellClass::Spot_Index(Coord)); + } else { + cell->Smudge = Class->Type; + } + cell->SmudgeData = 0; + } + } + } + + /* + ** Flag everything that might be overlapping this cell to redraw itself. + */ + cell->Redraw_Objects(); + } + } + } + + /* + ** Whether it was successful in placing, or not, delete the smudge object. It isn't + ** needed once the map has been updated with the proper smudge data. + */ + delete this; + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * SmudgeClass::Read_INI -- Reads smudge data from an INI file. * + * * + * This routine is used by the scenario loader to read the smudge data in an INI file and * + * create the appropriate smudge objects on the map. * + * * + * INPUT: buffer -- Pointer to the INI file staging buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + * 07/24/1995 JLB : Sets the smudge data value as well. * + *=============================================================================================*/ +void SmudgeClass::Read_INI(char *buffer) +{ + char buf[128]; // Working string staging buffer. + + int len = strlen(buffer) + 2; + char * tbuffer = buffer + len; + + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); + while (*tbuffer != '\0') { + SmudgeType smudge; // Smudge type. + + WWGetPrivateProfileString(INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, buffer); + smudge = SmudgeTypeClass::From_Name(strtok(buf, ",")); + if (smudge != SMUDGE_NONE) { + char * ptr = strtok(NULL, ","); + if (ptr) { + int data = 0; + CELL cell = atoi(ptr); + ptr = strtok(NULL, ","); + if (ptr) data = atoi(ptr); + new SmudgeClass(smudge, Cell_Coord(cell)); + if (Map[cell].Smudge == smudge && data) { + Map[cell].SmudgeData = data; + } + } + } + tbuffer += strlen(tbuffer)+1; + } +} + + +/*********************************************************************************************** + * SmudgeClass::Write_INI -- Writes the smudge data to an INI file. * + * * + * This routine is used by the scenario editor to write the smudge data to an INI file. * + * * + * INPUT: buffer -- Pointer to the INI file staging buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + * 07/24/1995 JLB : Records the smudge data as well. * + *=============================================================================================*/ +void SmudgeClass::Write_INI(char *buffer) +{ + char uname[10]; + char buf[127]; + char *tbuffer; // Accumulation buffer of unit IDs. + + /* + ** First, clear out all existing template data from the ini file. + */ + tbuffer = buffer + strlen(buffer) + 2; + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-strlen(buffer), buffer); + while (*tbuffer != '\0') { + WWWritePrivateProfileString(INI_Name(), tbuffer, NULL, buffer); + tbuffer += strlen(tbuffer)+1; + } + + /* + ** Find all templates and write them to the file. + */ + for (CELL index = 0; index < MAP_CELL_TOTAL; index++) { + CellClass * ptr; + + ptr = &Map[index]; + if (ptr->Smudge != SMUDGE_NONE) { + SmudgeTypeClass const * stype = &SmudgeTypeClass::As_Reference(ptr->Smudge); + if (!stype->IsBib) { + sprintf(uname, "%03d", index); + sprintf(buf, "%s,%d,%d", stype->IniName, index, ptr->SmudgeData); + WWWritePrivateProfileString(INI_Name(), uname, buf, buffer); + } + } + } +} + + +/*********************************************************************************************** + * SmudgeClass::Disown -- Disowns (removes) a building bib piece. * + * * + * This routine is used when a building is removed from the game. If there was any bib * + * attached, this routine will be called to disown the cells and remove the bib artwork. * + * * + * INPUT: cell -- The origin cell for this bib removal. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is actually working on a temporary bib object. It is created for the sole * + * purpose of calling this routine. It will be deleted immediately afterward. * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +void SmudgeClass::Disown(CELL cell) +{ + Validate(); + if (Class->IsBib) { + for (int w = 0; w < Class->Width; w++) { + for (int h = 0; h < Class->Height; h++) { + CellClass & cellptr = Map[cell + w + (h*MAP_CELL_W)]; + + if (cellptr.Overlay == OVERLAY_NONE || !OverlayTypeClass::As_Reference(cellptr.Overlay).IsWall) { + cellptr.Smudge = SMUDGE_NONE; + cellptr.SmudgeData = 0; + cellptr.Owner = HOUSE_NONE; + cellptr.Redraw_Objects(); + } + } + } + } +} diff --git a/SMUDGE.H b/SMUDGE.H new file mode 100644 index 0000000..0b87358 --- /dev/null +++ b/SMUDGE.H @@ -0,0 +1,104 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\smudge.h_v 2.16 16 Oct 1995 16:47:32 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SMUDGE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 9, 1994 * + * * + * Last Update : August 9, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SMUDGE_H +#define SMUDGE_H + +#include "object.h" +#include "type.h" + +/****************************************************************************** +** This is the transitory form for smudges. They exist as independent objects +** only in the transition stage from creation to placement upon the map. Once +** they are placed on the map, they merely become 'smudges' in the cell data. This +** object is then destroyed. +*/ +class SmudgeClass : public ObjectClass +{ + public: + /*------------------------------------------------------------------- + ** Constructors and destructors. + */ + static void * operator new(size_t size); + static void operator delete(void *ptr); + SmudgeClass(SmudgeType type, COORDINATE pos=-1, HousesType house = HOUSE_NONE); + SmudgeClass(void) : Class(0) {}; + operator SmudgeType(void) const {return Class->Type;}; + virtual ~SmudgeClass(void) {if (GameActive) SmudgeClass::Limbo();}; + virtual RTTIType What_Am_I(void) const {return RTTI_SMUDGE;}; + + static void Init(void); + + /* + ** File I/O. + */ + static void Read_INI(char *); + static void Write_INI(char *); + static char *INI_Name(void) {return "SMUDGE";}; + bool Load(FileClass & file); + bool Save(FileClass & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual bool Mark(MarkType); + virtual void Draw_It(int , int , WindowNumberType ) {}; + + void Disown(CELL cell); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + private: + + static HousesType ToOwn; + + /* + ** This is a pointer to the template object's class. + */ + SmudgeTypeClass const * const Class; + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; +}; + +#endif + diff --git a/SOUNDDLG.CPP b/SOUNDDLG.CPP new file mode 100644 index 0000000..a4e74d7 --- /dev/null +++ b/SOUNDDLG.CPP @@ -0,0 +1,434 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\sounddlg.cpv 2.17 16 Oct 1995 16:51:32 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SOUNDDLG.CPP * + * * + * Programmer : Maria del Mar McCready-Legg, Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : Jan 18, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SoundControlsClass::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "sounddlg.h" + +class MusicListClass : public ListClass +{ + public: + MusicListClass(int id, int x, int y, int w, int h) : + ListClass(id, x, y, w, h, TPF_6PT_GRAD|TPF_NOSHADOW, Hires_Retrieve("BTN-UP.SHP"), Hires_Retrieve("BTN-DN.SHP")) + {}; + virtual ~MusicListClass(void) {}; + + protected: + virtual void Draw_Entry(int index, int x, int y, int width, int selected); +}; +int SoundControlsClass::Init(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + Option_Width =292 * factor; + Option_Height =146 * factor; + + Option_X =((SeenBuff.Get_Width() - Option_Width) / 2); + Option_Y =(SeenBuff.Get_Height() - Option_Height) / 2; + + Listbox_X =1 * factor; + Listbox_Y =54 * factor; + Listbox_W =290 * factor; + Listbox_H =73 * factor; + + Button_Width =85 * factor; + Button_X =Option_Width-(Button_Width + (7 * factor)); + Button_Y =130 * factor; + + Stop_X =5 * factor; + Stop_Y =129 * factor; + + Play_X =23 * factor; + Play_Y =129 * factor; + + OnOff_Width =25 * factor; + #ifdef GERMAN + Shuffle_X =79 * factor; + #else + Shuffle_X =91 * factor; + #endif + + Shuffle_Y =130 * factor; + + Repeat_X =166 * factor; + Repeat_Y =130 * factor; + + MSlider_X =147 * factor; + MSlider_Y =28 * factor; + MSlider_W =108 * factor; + MSlider_Height =5 * factor; + + FXSlider_X =147 * factor; + FXSlider_Y =40 * factor; + FXSlider_W =108 * factor; + FXSlider_Height=5 * factor; + return(factor); +} + +/*********************************************************************************************** + * OptionsClass::Process -- Handles all the options graphic interface. * + * * + * This routine is the main control for the visual representation of the options * + * screen. It handles the visual overlay and the player input. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: 12/31/1994 MML : Created. * + *=============================================================================================*/ +void SoundControlsClass::Process(void) +{ +// ThemeType theme; + + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + Init(); + /* + ** List box that holds the score text strings. + */ + MusicListClass listbox(0, Option_X+Listbox_X, Option_Y+Listbox_Y, Listbox_W, Listbox_H); + + /* + ** Return to options menu button. + */ + TextButtonClass returnto(BUTTON_OPTIONS, TXT_OPTIONS_MENU, TPF_6PT_GRAD|TPF_NOSHADOW, +#ifdef FRENCH + Option_X+Button_X-8*2, Option_Y+Button_Y, Button_Width+11*2); +#else + Option_X+Button_X, Option_Y+Button_Y, Button_Width); +#endif + + /* + ** Stop playing button. + */ + char filename[30]; + if (factor == 1) + strcpy(filename,"BTN-ST.SHP"); + else + strcpy(filename,"BTN-STH.SHP"); + ShapeButtonClass stopbtn(BUTTON_STOP, MixFileClass::Retrieve(filename), + Option_X+Stop_X, Option_Y+Stop_Y); + + /* + ** Start playing button. + */ + if (factor == 1) + strcpy(filename,"BTN-PL.SHP"); + else + strcpy(filename,"BTN-PLH.SHP"); + + ShapeButtonClass playbtn(BUTTON_PLAY, MixFileClass::Retrieve(filename), + Option_X+Play_X, Option_Y+Play_Y); + + /* + ** Shuffle control. + */ + TextButtonClass shufflebtn(BUTTON_SHUFFLE, TXT_OFF, TPF_6PT_GRAD|TPF_NOSHADOW, + Option_X+Shuffle_X, Option_Y+Shuffle_Y, OnOff_Width); + + /* + ** Repeat control. + */ + TextButtonClass repeatbtn(BUTTON_REPEAT, TXT_OFF, TPF_6PT_GRAD|TPF_NOSHADOW, Option_X+Repeat_X, Option_Y+Repeat_Y, OnOff_Width); + + /* + ** Music volume slider. + */ + SliderClass music(SLIDER_MUSIC, Option_X+MSlider_X, Option_Y+MSlider_Y, MSlider_W, MSlider_Height); + + /* + ** Sound volume slider. + */ + SliderClass sound(SLIDER_SOUND, Option_X+FXSlider_X, Option_Y+FXSlider_Y, FXSlider_W, FXSlider_Height); + + /* + ** Causes left mouse clicks inside the dialog area, but not on any + ** particular button, to be ignored. + */ + GadgetClass area(Option_X, Option_Y, Option_Width, Option_Height, GadgetClass::LEFTPRESS); + + /* + ** Causes right clicks anywhere or left clicks outside of the dialog + ** box area to be the same a clicking the return to game options button. + */ + ControlClass ctrl(BUTTON_OPTIONS, 0, 0, SeenBuff.Get_Width(), SeenBuff.Get_Height(), GadgetClass::RIGHTPRESS|GadgetClass::LEFTPRESS); + + /* + ** The repeat and shuffle buttons are of the toggle type. They toggle + ** between saying "on" and "off". + */ + shufflebtn.IsToggleType = true; + if (Options.IsScoreShuffle) { + shufflebtn.Turn_On(); + } else { + shufflebtn.Turn_Off(); + } + shufflebtn.Set_Text(shufflebtn.IsOn ? TXT_ON : TXT_OFF); + + repeatbtn.IsToggleType = true; + if (Options.IsScoreRepeat) { + repeatbtn.Turn_On(); + } else { + repeatbtn.Turn_Off(); + } + repeatbtn.Set_Text(repeatbtn.IsOn ? TXT_ON : TXT_OFF); + + /* + ** Set the initial values of the sliders. + */ + music.Set_Maximum(255); + music.Set_Thumb_Size(16); + music.Set_Value(Options.ScoreVolume); + sound.Set_Maximum(255); + sound.Set_Thumb_Size(16); + sound.Set_Value(Options.Volume); + + /* + ** Set up the window. Window x-coords are in bytes not pixels. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Create Buttons. + */ + GadgetClass * optionsbtn = &returnto; + listbox.Add_Tail(*optionsbtn); + stopbtn.Add_Tail(*optionsbtn); + playbtn.Add_Tail(*optionsbtn); + shufflebtn.Add_Tail(*optionsbtn); + repeatbtn.Add_Tail(*optionsbtn); + music.Add_Tail(*optionsbtn); + sound.Add_Tail(*optionsbtn); + area.Add_Tail(*optionsbtn); + ctrl.Add_Tail(*optionsbtn); + + /* + ** Add all the themes to the list box. The list box entries are constructed + ** and then stored into allocated EMS memory blocks. + */ + for (ThemeType index = THEME_FIRST; index < Theme.Max_Themes(); index++) { + if (Theme.Is_Allowed(index)) { + char buffer[100]; + int length = Theme.Track_Length(index); + char const * fullname = Theme.Full_Name(index); + + void * ptr = new char [sizeof(buffer)]; + if (ptr) { + sprintf((char *)ptr, "%cTrack %d\t%d:%02d\t%s", index, listbox.Count()+1, length / 60, length % 60, fullname); + listbox.Add_Item((char const *)ptr); + } + + if (Theme.What_Is_Playing() == index) { + listbox.Set_Selected_Index(listbox.Count()-1); + } + } + } + static int _tabs[] = { + 55*factor, 72*factor, 90*factor + }; + listbox.Set_Tabs(_tabs); + + /* + ** Main Processing Loop. + */ + bool display = true; + bool process = true; + + while (process) { + + /* + ** Invoke game callback. + */ + if (GameToPlay == GAME_NORMAL) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + } + } + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=TRUE; + } + + /* + ** Refresh display if needed. + */ + if (display) { + + Hide_Mouse(); + + /* + ** Draw the background. + */ + Dialog_Box(Option_X, Option_Y, Option_Width, Option_Height); + + Draw_Caption(TXT_SOUND_CONTROLS, Option_X, Option_Y, Option_Width); + + /* + ** Draw the Music, Speech & Sound titles. + */ + Fancy_Text_Print(TXT_MUSIC_VOLUME, Option_X+MSlider_X-5, Option_Y+MSlider_Y-2, CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW|TPF_RIGHT); + Fancy_Text_Print(TXT_SOUND_VOLUME, Option_X+FXSlider_X-5, Option_Y+FXSlider_Y-2, CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW|TPF_RIGHT); + + Fancy_Text_Print(TXT_SHUFFLE, Option_X+Shuffle_X-5, Option_Y+Shuffle_Y+1, CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW|TPF_RIGHT); + Fancy_Text_Print(TXT_REPEAT, Option_X+Repeat_X-5, Option_Y+Repeat_Y+1, CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW|TPF_RIGHT); + + optionsbtn->Draw_All(); + Show_Mouse(); + display = false; + } + + /* + ** Get user input. + */ + KeyNumType input = optionsbtn->Input(); + + /* + ** Process Input. + */ + switch (input) { + + case KN_ESC: + case BUTTON_OPTIONS|KN_BUTTON: + process = false; + break; + + /* + ** Control music volume. + */ + case SLIDER_MUSIC|KN_BUTTON: + Options.Set_Score_Volume(music.Get_Value()); + break; + + /* + ** Control sound volume. + */ + case SLIDER_SOUND|KN_BUTTON: + Options.Set_Sound_Volume(sound.Get_Value(), true); + break; + + case BUTTON_LISTBOX|KN_BUTTON: +// Mono_Printf ("%d %s Listbox was pressed.\r",__LINE__, __FILE__); + break; + + /* + ** Stop all themes from playing. + */ + case BUTTON_STOP|KN_BUTTON: + Theme.Queue_Song(THEME_NONE); + break; + + /* + ** Start the currently selected theme to play. + */ + case KN_SPACE: + case BUTTON_PLAY|KN_BUTTON: + if (listbox.Count()) { + Theme.Queue_Song( (ThemeType)*((unsigned char *)listbox.Current_Item()) ); + } + break; + + /* + ** Toggle the shuffle button. + */ + case BUTTON_SHUFFLE|KN_BUTTON: + shufflebtn.Set_Text(shufflebtn.IsOn ? TXT_ON : TXT_OFF); + Options.Set_Shuffle(shufflebtn.IsOn); + break; + + /* + ** Toggle the repeat button. + */ + case BUTTON_REPEAT|KN_BUTTON: + repeatbtn.Set_Text(repeatbtn.IsOn ? TXT_ON : TXT_OFF); + Options.Set_Repeat(repeatbtn.IsOn); + break; + } + } + + /* + ** If the score volume was turned all the way down, then actually + ** stop the scores from being played. + */ + if (!Options.ScoreVolume) { + Theme.Stop(); + } + + /* + ** Save them settings - you know it makes sense + */ + Options.Save_Settings(); // save new value + + /* + ** Free the items from the list box. + */ + while (listbox.Count()) { + char const * ptr = listbox.Get_Item(0); + listbox.Remove_Item(ptr); + delete [] (void*)ptr; + } +} + + +void MusicListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + if (TextFlags & TPF_6PT_GRAD) { + TextPrintType flags = TextFlags; + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, CC_GREEN_SHADOW); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print((char *)Add_Long_To_Pointer(List[index], 1), x, y, CC_GREEN, TBLACK, flags, width, Tabs); + + } else { + Conquer_Clip_Text_Print((char *)Add_Long_To_Pointer(List[index], 1), x, y, (selected ? BLUE : WHITE), TBLACK, TextFlags, width, Tabs); + } +} diff --git a/SOUNDDLG.H b/SOUNDDLG.H new file mode 100644 index 0000000..a8757c9 --- /dev/null +++ b/SOUNDDLG.H @@ -0,0 +1,159 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\sounddlg.h_v 2.18 16 Oct 1995 16:46:48 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : June 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SOUNDDLG_H +#define SOUNDDLG_H + +#include "gadget.h" + +class SoundControlsClass +{ + enum SoundControlsClassEnums { +#ifdef FRENCH + OPTION_WIDTH=308, +#else + OPTION_WIDTH=292, +#endif + OPTION_HEIGHT=146, + + OPTION_X=((320 - OPTION_WIDTH) / 2), + OPTION_Y=(200 - OPTION_HEIGHT) / 2, + + LISTBOX_X=1, + LISTBOX_Y=54, + LISTBOX_W=290, + LISTBOX_H=72, + + BUTTON_WIDTH=85, + BUTTON_X=OPTION_WIDTH-(BUTTON_WIDTH+7), // Options button x pos + BUTTON_Y=130, // Options button y pos + + STOP_X=5, // Stop button X. + STOP_Y=129, // Stop button Y. + + PLAY_X=23, + PLAY_Y=129, + + ONOFF_WIDTH=25, +#ifdef GERMAN + SHUFFLE_X=79,//BGA:91, +#else +#ifdef FRENCH + SHUFFLE_X=99, +#else + SHUFFLE_X=91, +#endif +#endif + SHUFFLE_Y=130, + +#ifdef FRENCH + REPEAT_X=174, +#else + REPEAT_X=166, +#endif + REPEAT_Y=130, + + MSLIDER_X=147, + MSLIDER_Y=28, + MSLIDER_W=108, + MSLIDER_HEIGHT=5, + + FXSLIDER_X=147, + FXSLIDER_Y=40, + FXSLIDER_W=108, + FXSLIDER_HEIGHT=5, + + BUTTON_STOP = 605, + BUTTON_PLAY, + BUTTON_SHUFFLE, + BUTTON_REPEAT, + BUTTON_OPTIONS, + SLIDER_MUSIC, +// SLIDER_SPEECH, + SLIDER_SOUND, + BUTTON_LISTBOX, + }; + + public: + SoundControlsClass(void) {} + void Process(void); + int Init(void); + + private: + + int Option_Width; + int Option_Height; + + int Option_X; + int Option_Y; + + int Listbox_X; + int Listbox_Y; + int Listbox_W; + int Listbox_H; + + int Button_Width; + int Button_X; + int Button_Y; + + int Stop_X; + int Stop_Y; + + int Play_X; + int Play_Y; + + int OnOff_Width; + + int Shuffle_X; + int Shuffle_Y; + + int Repeat_X; + int Repeat_Y; + + int MSlider_X; + int MSlider_Y; + int MSlider_W; + int MSlider_Height; + + int FXSlider_X; + int FXSlider_Y; + int FXSlider_W; + int FXSlider_Height; + +}; + +#endif diff --git a/SPECIAL.CPP b/SPECIAL.CPP new file mode 100644 index 0000000..1be90b0 --- /dev/null +++ b/SPECIAL.CPP @@ -0,0 +1,274 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\special.cpv 1.4 16 Oct 1995 16:50:06 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SPECIAL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/27/95 * + * * + * Last Update : May 27, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#define OPTION_WIDTH 236 +#define OPTION_HEIGHT 162 +#define OPTION_X ((320 - OPTION_WIDTH) / 2) +#define OPTION_Y (200 - OPTION_HEIGHT) / 2 + +void Special_Dialog(void) +{ + SpecialClass oldspecial = Special; + GadgetClass * buttons = NULL; + static struct { + int Description; + int Setting; + CheckBoxClass * Button; + } _options[] = { +// {TXT_DEFENDER_ADVANTAGE, 0, 0}, + {TXT_SEPARATE_HELIPAD, 0, 0}, + {TXT_VISIBLE_TARGET, 0, 0}, + {TXT_TREE_TARGET, 0, 0}, + {TXT_MCV_DEPLOY, 0, 0}, + {TXT_SMART_DEFENCE, 0, 0}, + {TXT_THREE_POINT, 0, 0}, +// {TXT_TIBERIUM_GROWTH, 0, 0}, +// {TXT_TIBERIUM_SPREAD, 0, 0}, + {TXT_TIBERIUM_FAST, 0, 0}, + {TXT_ROAD_PIECES, 0, 0}, + {TXT_SCATTER, 0, 0}, + {TXT_SHOW_NAMES, 0, 0}, + }; + + TextButtonClass ok(200, TXT_OK, TPF_6PT_GRAD|TPF_NOSHADOW, OPTION_X+5, OPTION_Y+OPTION_HEIGHT-15); + TextButtonClass cancel(201, TXT_CANCEL, TPF_6PT_GRAD|TPF_NOSHADOW, OPTION_X+OPTION_WIDTH-50, OPTION_Y+OPTION_HEIGHT-15); + buttons = &ok; + cancel.Add(*buttons); + + for (int index = 0; index < sizeof(_options)/sizeof(_options[0]); index++) { + _options[index].Button = new CheckBoxClass(100+index, OPTION_X+7, OPTION_Y+20+(index*10)); + if (_options[index].Button) { + _options[index].Button->Add(*buttons); + + bool value = false; + switch (_options[index].Description) { + case TXT_SEPARATE_HELIPAD: + value = Special.IsSeparate; + break; + + case TXT_SHOW_NAMES: + value = Special.IsNamed; + break; + + case TXT_DEFENDER_ADVANTAGE: + value = Special.IsDefenderAdvantage; + break; + + case TXT_VISIBLE_TARGET: + value = Special.IsVisibleTarget; + break; + + case TXT_TREE_TARGET: + value = Special.IsTreeTarget; + break; + + case TXT_MCV_DEPLOY: + value = Special.IsMCVDeploy; + break; + + case TXT_SMART_DEFENCE: + value = Special.IsSmartDefense; + break; + + case TXT_THREE_POINT: + value = Special.IsThreePoint; + break; + + case TXT_TIBERIUM_GROWTH: + value = Special.IsTGrowth; + break; + + case TXT_TIBERIUM_SPREAD: + value = Special.IsTSpread; + break; + + case TXT_TIBERIUM_FAST: + value = Special.IsTFast; + break; + + case TXT_ROAD_PIECES: + value = Special.IsRoad; + break; + + case TXT_SCATTER: + value = Special.IsScatter; + break; + } + + _options[index].Setting = value; + if (value) { + _options[index].Button->Turn_On(); + } else { + _options[index].Button->Turn_Off(); + } + } + } + + Map.Override_Mouse_Shape(MOUSE_NORMAL); + Set_Logic_Page(SeenBuff); + bool recalc = true; + bool display = true; + bool process = true; + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=TRUE; + } + + if (GameToPlay == GAME_NORMAL) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + } + } + + if (display) { + display = false; + + Hide_Mouse(); + Dialog_Box(OPTION_X, OPTION_Y, OPTION_WIDTH, OPTION_HEIGHT); + Draw_Caption(TXT_SPECIAL_OPTIONS, OPTION_X, OPTION_Y, OPTION_WIDTH); + + for (index = 0; index < sizeof(_options)/sizeof(_options[0]); index++) { + Fancy_Text_Print(_options[index].Description, _options[index].Button->X+10, _options[index].Button->Y, CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + } + buttons->Draw_All(); + Show_Mouse(); + } + + KeyNumType input = buttons->Input(); + switch (input) { + case KN_ESC: + case 200|KN_BUTTON: + process = false; + for (index = 0; index < sizeof(_options)/sizeof(_options[0]); index++) { + switch (_options[index].Description) { + case TXT_SEPARATE_HELIPAD: + oldspecial.IsSeparate = _options[index].Setting; + break; + + case TXT_SHOW_NAMES: + oldspecial.IsNamed = _options[index].Setting; + break; + + case TXT_DEFENDER_ADVANTAGE: + oldspecial.IsDefenderAdvantage = _options[index].Setting; + break; + + case TXT_VISIBLE_TARGET: + oldspecial.IsVisibleTarget = _options[index].Setting; + break; + + case TXT_TREE_TARGET: + oldspecial.IsTreeTarget = _options[index].Setting; + break; + + case TXT_MCV_DEPLOY: + oldspecial.IsMCVDeploy = _options[index].Setting; + break; + + case TXT_SMART_DEFENCE: + oldspecial.IsSmartDefense = _options[index].Setting; + break; + + case TXT_THREE_POINT: + oldspecial.IsThreePoint = _options[index].Setting; + break; + + case TXT_TIBERIUM_GROWTH: + oldspecial.IsTGrowth = _options[index].Setting; + break; + + case TXT_TIBERIUM_SPREAD: + oldspecial.IsTSpread = _options[index].Setting; + break; + + case TXT_TIBERIUM_FAST: + oldspecial.IsTFast = _options[index].Setting; + break; + + case TXT_ROAD_PIECES: + oldspecial.IsRoad = _options[index].Setting; + break; + + case TXT_SCATTER: + oldspecial.IsScatter = _options[index].Setting; + break; + } + } + OutList.Add(EventClass(oldspecial)); + break; + + case 201|KN_BUTTON: + process = false; + break; + + case KN_NONE: + break; + + default: + index = (input & ~KN_BUTTON) - 100; + if ((unsigned)index < sizeof(_options)/sizeof(_options[0])) { + _options[index].Setting = (_options[index].Setting == false); + if (_options[index].Setting) { + _options[index].Button->Turn_On(); + } else { + _options[index].Button->Turn_Off(); + } + } + break; + } + } + + Map.Revert_Mouse_Shape(); + HiddenPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); +} + + + + + diff --git a/SPECIAL.H b/SPECIAL.H new file mode 100644 index 0000000..aaa7576 --- /dev/null +++ b/SPECIAL.H @@ -0,0 +1,241 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\special.h_v 2.15 16 Oct 1995 16:47:36 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SPECIAL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/27/95 * + * * + * Last Update : February 27, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SPECIAL_H +#define SPECIAL_H + +class SpecialClass +{ + public: + void Init(void) { + IsScrollMod = false; + IsGross = false; + IsHealthBar = true; + IsEasy = false; + IsDifficult = false; + IsSpeedBuild = false; + IsDefenderAdvantage = true; + IsVisibleTarget = false; + IsVariation = false; + IsJurassic = false; + IsJuvenile = false; + IsSmartDefense = false; + IsTreeTarget = false; + IsMCVDeploy = false; + IsMonoEnabled = false; + IsInert = false; + IsShowPath = false; + IsBarOn = false; + IsThreePoint = false; + IsTGrowth = true; + IsTSpread = true; + IsTFast = true; + IsRoad = false; + IsScatter = false; + IsCaptureTheFlag = false; + IsNamed = false; + IsFromInstall = false; + IsSeparate = false; + IsFromWChat = false; + } + + /* + ** Show the health bar on the enemy units? + */ + unsigned IsHealthBar:1; + + /* + ** Is the game flagged for easy mode? + */ + unsigned IsEasy:1; + + /* + ** Is the game flagged for difficult? + */ + unsigned IsDifficult:1; + + /* + ** Controls the speedy build option -- used for testing. + */ + unsigned IsSpeedBuild:1; + + /* + ** If the player can build the helipad separate from the helipad and + ** helicopter combo, then this flag will be true. + */ + unsigned IsSeparate:1; + + /* + ** If the defender has the advantage then this will be true. This flag + ** allows the defender to have a better advantage in combat than the + ** attacker. Moving units will not be able to dish out or take as much + ** damage when this flag is true. + */ + unsigned IsDefenderAdvantage:1; + + /* + ** If civilian structures are to have a name, then this flag will be + ** set to true. The default case is to just use generic names for + ** civilians. + */ + unsigned IsNamed:1; + + /* + ** If from install, then play the special installation movie and + ** skip asking them what type of game they want to play. + */ + unsigned IsFromInstall:1; + + /* + ** If capture the flag mode is on, this flag will be true. With this + ** flag enabled, then the flag is initially placed at the start of + ** the scenario. + */ + unsigned IsCaptureTheFlag:1; + + /* + ** Is target selecting by other human opponents visible to the player? + */ + unsigned IsVisibleTarget:1; + + /* + ** If human generated sound effects are to be used, then this + ** flag will be true. + */ + unsigned IsJuvenile:1; + + /* + ** If friendly units should return fire when fired upon, set this + ** flag to true. The default is only for the enemy units to do this. + */ + unsigned IsSmartDefense:1; + + /* + ** If targeting of trees is allowed, then this flag will be true. + */ + unsigned IsTreeTarget:1; + + /* + ** If this flag is true, then the construction yard can undeploy back into an MCV. + */ + unsigned IsMCVDeploy:1; + + /* + ** If the monochrome debugging output is enabled, then this flag will be true. + */ + unsigned IsMonoEnabled:1; + + /* + ** This flags controls whether weapons are inert. An inert weapon doesn't do any + ** damage. Effectively, if this is true, then the units never die. + */ + unsigned IsInert:1; + + /* + ** When this flag is true, the computer findpath algorithm reveals the route being + ** examined. This is used to trace findpath bugs. + */ + unsigned IsShowPath:1; + + /* + ** All units will display their health bargraph if this is true. + */ + unsigned IsBarOn:1; + + /* + ** If wheeled vehicles should do a 3-point turn when rotating in place, then + ** this flag is true. + */ + unsigned IsThreePoint:1; + + /* + ** If Tiberium is allowed to grow, then this flag will be true. + */ + unsigned IsTGrowth:1; + + /* + ** If Tiberium is allowed to spread, then this flag will be true. + */ + unsigned IsTSpread:1; + + /* + ** This controls whether Tiberium grows&spreads quickly or not. + */ + unsigned IsTFast:1; + + /* + ** This flag controls whether the road additional pieces are added to + ** the bottom of buildings. If true, then the roads are NOT added. + */ + unsigned IsRoad:1; + + /* + ** Controls whether units (especially infantry) will scatter when there + ** is an immediate threat. This gives infantry a "mind of their own" when + ** it comes to self preservation. If set to false, then units will not + ** scatter. + */ + unsigned IsScatter:1; + + /* + ** Special bonus scenario enabled. + */ + unsigned IsJurassic:1; + + /* + ** Are score variations allowed? + */ + unsigned IsVariation:1; + + /* + ** If the gross human splatter marks should be present. + */ + unsigned IsGross:1; + + /* + ** Disables scrolling over the "options" and "sidebar" tabs. + */ + unsigned IsScrollMod:1; + + /* + ** Flag that we were spawned from WChat. + */ + unsigned IsFromWChat:1; +}; + + +#endif diff --git a/STAGE.H b/STAGE.H new file mode 100644 index 0000000..650f982 --- /dev/null +++ b/STAGE.H @@ -0,0 +1,100 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\stage.h_v 2.15 16 Oct 1995 16:45:16 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : STAGE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 17, 1994 * + * * + * Last Update : June 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef STAGE_H +#define STAGE_H + +class StageClass { + + /* + ** This handles the animation stage of the object. This includes smoke, walking, + ** flapping, and rocket flames. + */ + unsigned short Stage; + + /* + ** This is the countdown timer for stage animation. When this counts down + ** to zero, then the stage increments by one and the time cycle starts + ** over again. + */ + unsigned char StageTimer; + + /* + ** This is the value to assign the StageTimer whenever it needs to be reset. Thus, + ** this value is the control of how fast the stage value increments. + */ + unsigned char Rate; + + public: + StageClass(void) { + StageTimer = 0; + Stage = 0; + Rate = 0; + }; + + int Fetch_Stage(void) const {return Stage;}; + int Fetch_Rate(void) const {return Rate;}; + void Set_Stage(int stage) {Stage = stage;}; + void Set_Rate(unsigned char rate) {Rate = StageTimer = rate;}; + void AI(void) {}; + bool Graphic_Logic(void) { + if (Rate) { + StageTimer--; + if (!StageTimer) { + Stage++; + StageTimer = Rate; + return true; + } + } + return false; + }; + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const { + mono->Set_Cursor(56, 7); + mono->Printf("%3d[%d]", Stage, Rate); + }; + #endif + + /* + ** File I/O. + */ + void Code_Pointers(void) { return; } + void Decode_Pointers(void) { return; } +}; + + +#endif diff --git a/STARTUP.CPP b/STARTUP.CPP new file mode 100644 index 0000000..70a7f21 --- /dev/null +++ b/STARTUP.CPP @@ -0,0 +1,793 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\startup.cpv 2.17 16 Oct 1995 16:48:12 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : STARTUP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 3, 1994 * + * * + * Last Update : August 27, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Delete_Swap_Files -- Deletes previously existing swap files. * + * Prog_End -- Cleans up library systems in prep for game exit. * + * main -- Initial startup routine (preps library systems). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include +#include +#include "ccdde.h" + +bool Read_Private_Config_Struct(char *profile, NewConfigType *config); +void Delete_Swap_Files(void); +void Print_Error_End_Exit(char *string); +void Print_Error_Exit(char *string); +WinTimerClass *WinTimer; +extern void Create_Main_Window ( HANDLE instance , int command_show , int width , int height); + +extern bool ReadyToQuit; +void Read_Setup_Options(RawFileClass *config_file); + +bool VideoBackBufferAllowed = true; +void Check_From_WChat(char *wchat_name); +bool SpawnedFromWChat = false; + +extern "C"{ + bool __cdecl Detect_MMX_Availability(void); + void __cdecl Init_MMX(void); +} + + + +#if (0) +char WibbleBuffer[1024*1024]; + +void CD_Test(void) +{ + HANDLE handle; + DWORD size; + + handle= CreateFile("e:\\scores.mix", GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (handle== INVALID_HANDLE_VALUE){ + return; + } + + unsigned bytes_read; + + do{ + bytes_read = ReadFile (handle , WibbleBuffer , 1024*1024, &size, NULL); + + }while(size == 1024*1024); + + + CloseHandle (handle); +} +#endif //(0) + + + +/*********************************************************************************************** + * main -- Initial startup routine (preps library systems). * + * * + * This is the routine that is first called when the program starts up. It basically * + * handles the command line parsing and setting up library systems. * + * * + * INPUT: argc -- Number of command line arguments. * + * * + * argv -- Pointer to array of comman line argument strings. * + * * + * OUTPUT: Returns with execution failure code (if any). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1995 JLB : Created. * + *=============================================================================================*/ + +HINSTANCE ProgramInstance; +extern BOOL CC95AlreadyRunning; +void Move_Point(short &x, short &y, register DirType dir, unsigned short distance); + +void Check_Use_Compressed_Shapes (void); + +int PASCAL WinMain ( HINSTANCE instance , HINSTANCE , char * command_line , int command_show ) +{ +// Heap_Dump_Check( "first thing in main" ); +// malloc(1); + + CCDebugString ("C&C95 - Starting up.\n"); + + + //WindowsTimer = new WinTimerClass(60,FALSE); + //CD_Test(); + + + + /* + ** These values return 0x47 if code is working correctly + */ +// int temp = Desired_Facing256 (1070, 5419, 1408, 5504); + + + + /* + ** If we are already running then switch to the existing process and exit + */ + SpawnedFromWChat = false; + + if (CC95AlreadyRunning) { //Set in the DDEServer constructor + //MessageBox (NULL, "Error - attempt to restart C&C95 when already running.", "Command & Conquer", MB_ICONEXCLAMATION|MB_OK); + + HWND ccwindow; + ccwindow = FindWindow ("Command & Conquer", "Command & Conquer"); + if (ccwindow){ + SetForegroundWindow ( ccwindow ); + ShowWindow ( ccwindow, SW_RESTORE ); + } + + return (EXIT_SUCCESS); + } + + + DDSCAPS surface_capabilities; + + if (Ram_Free(MEM_NORMAL) < 5000000) { +#ifdef GERMAN + printf("Zuwenig Hauptspeicher verfgbar.\n"); +#else +#ifdef FRENCH + printf("M‚moire vive (RAM) insuffisante.\n"); +#else + printf("Insufficient RAM available.\n"); +#endif +#endif + return(EXIT_FAILURE); + } + + + //void *test_buffer = Alloc(20,MEM_NORMAL); + + //memset ((char*)test_buffer, 0, 21); + + //Free(test_buffer); + + + int argc; //Command line argument count + unsigned command_scan; + char command_char; + char * argv[20]; //Pointers to command line arguments + char path_to_exe[280]; + + ProgramInstance = instance; + + /* + ** Get the full path to the .EXE + */ + GetModuleFileName (instance, &path_to_exe[0], 280); + + /* + ** First argument is supposed to be a pointer to the .EXE that is running + ** + */ + argc=1; //Set argument count to 1 + argv[0]=&path_to_exe[0]; //Set 1st command line argument to point to full path + + /* + ** Get pointers to command line arguments just like if we were in DOS + ** + ** The command line we get is cr/zero? terminated. + ** + */ + + command_scan=0; + + do { + /* + ** Scan for non-space character on command line + */ + do { + command_char = *( command_line+command_scan++ ); + } while ( command_char==' ' ); + + if ( command_char!=0 && command_char != 13 ){ + argv[argc++]=command_line+command_scan-1; + + /* + ** Scan for space character on command line + */ + do { + command_char = *( command_line+command_scan++ ); + } while ( command_char!=' ' && command_char != 0 && command_char!=13 ); + *( command_line+command_scan-1 ) = 0; + } + + } while ( command_char != 0 && command_char != 13 && argc<20 ); + + + + /* + ** Remember the current working directory and drive. + */ + unsigned olddrive; + char oldpath[PATH_MAX]; + getcwd(oldpath, sizeof(oldpath)); + _dos_getdrive(&olddrive); + + + /* + ** Change directory to the where the executable is located. Handle the + ** case where there is no path attached to argv[0]. + */ + char drive[_MAX_DRIVE]; + char path[_MAX_PATH]; + unsigned drivecount; + _splitpath(argv[0], drive, path, NULL, NULL); + if (!drive[0]) { + drive[0] = ('A' + olddrive)-1; + } + if (!path[0]) { + strcpy(path, "."); + } + _dos_setdrive(toupper((drive[0])-'A')+1, &drivecount); + if (path[strlen(path)-1] == '\\') { + path[strlen(path)-1] = '\0'; + } + chdir(path); + +#ifdef JAPANESE + ForceEnglish = false; +#endif + if (Parse_Command_Line(argc, argv)) { + + + WindowsTimer = new WinTimerClass(60,FALSE); + + int time_test = WindowsTimer->Get_System_Tick_Count(); + Sleep (1000); + if (WindowsTimer->Get_System_Tick_Count() == time_test){ +#ifdef FRENCH + MessageBox(0, "Error - L'horloge systŠme n'a pas pu s'initialiser en raison de l'instabilit‚ du sytŠme. Vous devez red‚marrer Windows.", "Command & Conquer" , MB_OK|MB_ICONSTOP); +#else +#ifdef GERMAN + MessageBox(0, "Fehler - das Timer-System konnte aufgrund einer Instabilit„t des Systems nicht initialisiert werden. Bitte starten Sie Windows neu.", "Command & Conquer", MB_OK|MB_ICONSTOP); +#else + MessageBox(0, "Error - Timer system failed to start due to system instability. You need to restart Windows.", "Command & Conquer", MB_OK|MB_ICONSTOP); +#endif //GERMAN +#endif //FRENCH + return(EXIT_FAILURE); + } + + RawFileClass cfile("CONQUER.INI"); + +#ifdef JAPANESE + //////////////////////////////////////if(!ForceEnglish) KBLanguage = 1; +#endif + + + /* + ** Check for existance of MMX support on the processor + */ + if (Detect_MMX_Availability()){ + //MessageBox(NULL, "MMX extensions detected - enabling MMX support.", "Command & Conquer",MB_ICONEXCLAMATION|MB_OK); + MMXAvailable = true; + } + + /* + ** If there is loads of memory then use uncompressed shapes + */ + Check_Use_Compressed_Shapes(); + + /* + ** If there is not enough disk space free, dont allow the product to run. + */ + if (Disk_Space_Available() < INIT_FREE_DISK_SPACE) { +#ifdef GERMAN + char disk_space_message [512]; + sprintf (disk_space_message, "Nicht genug Festplattenplatz fr Command & Conquer.\nSie brauchen %d MByte freien Platz auf der Festplatte.", (INIT_FREE_DISK_SPACE) / (1024 * 1024)); + MessageBox(NULL, disk_space_message, "Command & Conquer", MB_ICONEXCLAMATION|MB_OK); + if ( WindowsTimer ) + delete WindowsTimer; + return (EXIT_FAILURE); +#endif +#ifdef FRENCH + char disk_space_message [512]; + sprintf (disk_space_message, "Espace disque insuffisant pour lancer Command & Conquer.\nVous devez disposer de %d Mo d'espace disponsible sur disque dur.", (INIT_FREE_DISK_SPACE) / (1024 * 1024)); + MessageBox(NULL, disk_space_message, "Command & Conquer", MB_ICONEXCLAMATION|MB_OK); + if ( WindowsTimer ) + delete WindowsTimer; + return (EXIT_FAILURE); +#endif +#if !(FRENCH | GERMAN) + int reply = MessageBox(NULL, "Warning - you are critically low on free disk space for virtual memory and save games. Do you want to play C&C anyway?", "Command & Conquer", MB_ICONQUESTION|MB_YESNO); + if (reply == IDNO){ + + if ( WindowsTimer ) + delete WindowsTimer; + return (EXIT_FAILURE); + } + +#endif + } + + CDFileClass::Set_CD_Drive (CDList.Get_First_CD_Drive()); + + if (cfile.Is_Available()) { + +#ifndef NOMEMCHECK + char * cdata = (char *)Load_Alloc_Data(cfile); + Read_Private_Config_Struct(cdata, &NewConfig); + delete [] cdata; +#else + Read_Private_Config_Struct((char *)Load_Alloc_Data(cfile), &NewConfig); +#endif + Read_Setup_Options( &cfile ); + + CCDebugString ("C&C95 - Creating main window.\n"); + + Create_Main_Window( instance , command_show , ScreenWidth , ScreenHeight ); + + CCDebugString ("C&C95 - Initialising audio.\n"); + + SoundOn = Audio_Init ( MainWindow , 16 , false , 11025*2 , 0 ); + + Palette = new(MEM_CLEAR) unsigned char[768]; + + BOOL video_success = FALSE; + CCDebugString ("C&C95 - Setting video mode.\n"); + /* + ** Set 640x400 video mode. If its not available then try for 640x480 + */ + if (ScreenHeight == 400){ + if (Set_Video_Mode (MainWindow, ScreenWidth, ScreenHeight, 8)){ + video_success = TRUE; + }else{ + if (Set_Video_Mode (MainWindow, ScreenWidth, 480, 8)){ + video_success = TRUE; + ScreenHeight = 480; + } + } + }else{ + if (Set_Video_Mode (MainWindow, ScreenWidth, ScreenHeight, 8)){ + video_success = TRUE; + } + } + + if (!video_success){ + CCDebugString ("C&C95 - Failed to set video mode.\n"); + MessageBox(MainWindow, Text_String(TXT_UNABLE_TO_SET_VIDEO_MODE), "Command & Conquer", MB_ICONEXCLAMATION|MB_OK); + if (WindowsTimer) delete WindowsTimer; + if (Palette) delete [] Palette; + return (EXIT_FAILURE); + } + + CCDebugString ("C&C95 - Initialising video surfaces.\n"); + + if (ScreenWidth==320){ + VisiblePage.Init( ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)0); + ModeXBuff.Init( ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)(GBC_VISIBLE | GBC_VIDEOMEM)); + } else { + VisiblePage.Init( ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)(GBC_VISIBLE | GBC_VIDEOMEM)); + + /* + ** Check that we really got a video memory page. Failure is fatal. + */ + memset (&surface_capabilities, 0, sizeof(surface_capabilities)); + VisiblePage.Get_DD_Surface()->GetCaps(&surface_capabilities); + if (surface_capabilities.dwCaps & DDSCAPS_SYSTEMMEMORY){ + /* + ** Aaaarrgghh! + */ + CCDebugString ("C&C95 - Unable to allocate primary surface.\n"); + MessageBox(MainWindow, Text_String(TXT_UNABLE_TO_ALLOCATE_PRIMARY_VIDEO_BUFFER), "Command & Conquer", MB_ICONEXCLAMATION|MB_OK); + if (WindowsTimer) delete WindowsTimer; + if (Palette) delete [] Palette; + return (EXIT_FAILURE); + } + + /* + ** If we have enough left then put the hidpage in video memory unless... + ** + ** If there is no blitter then we will get better performance with a system + ** memory hidpage + ** + ** Use a system memory page if the user has specified it via the ccsetup program. + */ + CCDebugString ("C&C95 - Allocating back buffer "); + long video_memory = Get_Free_Video_Memory(); + unsigned video_capabilities = Get_Video_Hardware_Capabilities(); + if (video_memory < ScreenWidth*ScreenHeight || + (! (video_capabilities & VIDEO_BLITTER)) || + (video_capabilities & VIDEO_NO_HARDWARE_ASSIST) || + !VideoBackBufferAllowed){ + CCDebugString ("in system memory.\n"); + HiddenPage.Init (ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)0); + } else { + //HiddenPage.Init (ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)0); + CCDebugString ("in video memory.\n"); + HiddenPage.Init (ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)GBC_VIDEOMEM); + + /* + ** Make sure we really got a video memory hid page. If we didnt then things + ** will run very slowly. + */ + memset (&surface_capabilities, 0, sizeof(surface_capabilities)); + HiddenPage.Get_DD_Surface()->GetCaps(&surface_capabilities); + if (surface_capabilities.dwCaps & DDSCAPS_SYSTEMMEMORY){ + /* + ** Oh dear, big trub. This must be an IBM Aptiva or something similarly cruddy. + ** We must redo the Hidden Page as system memory. + */ + AllSurfaces.Remove_DD_Surface(HiddenPage.Get_DD_Surface()); // Remove the old surface from the AllSurfaces list + HiddenPage.Get_DD_Surface()->Release(); + HiddenPage.Init (ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)0); + }else{ + VisiblePage.Attach_DD_Surface(&HiddenPage); + } + } + } + + ScreenHeight = 400; + + if (VisiblePage.Get_Height() == 480){ + SeenBuff.Attach(&VisiblePage,0, 40, 640, 400); + HidPage.Attach(&HiddenPage, 0, 40, 640, 400); + }else{ + SeenBuff.Attach(&VisiblePage,0, 0, 640, 400); + HidPage.Attach(&HiddenPage, 0, 0, 640, 400); + } + CCDebugString ("C&C95 - Adjusting variables for resolution.\n"); + Options.Adjust_Variables_For_Resolution(); + + CCDebugString ("C&C95 - Setting palette.\n"); + /////////Set_Palette(Palette); + + WindowList[0][WINDOWWIDTH] = SeenBuff.Get_Width() >> 3; + WindowList[0][WINDOWHEIGHT] = SeenBuff.Get_Height(); + + /* + ** Install the memory error handler + */ + Memory_Error = &Memory_Error_Handler; + + /* + ** Initialise MMX support if its available + */ + CCDebugString ("C&C95 - Entering MMX detection.\n"); + if (MMXAvailable){ + Init_MMX(); + } + + CCDebugString ("C&C95 - Creating mouse class.\n"); + WWMouse = new WWMouseClass(&SeenBuff, 32, 32); +// MouseInstalled = Install_Mouse(32,24,320,200); + MouseInstalled = TRUE; + + /* + ** See if we should run the intro + */ + CCDebugString ("C&C95 - Reading CONQUER.INI.\n"); + char *buffer = (char*)Alloc(64000 , MEM_NORMAL); //(char *)HidPage.Get_Buffer(); + cfile.Read(buffer, cfile.Size()); + buffer[cfile.Size()] = '\0'; + + /* + ** Check for forced intro movie run disabling. If the conquer + ** configuration file says "no", then don't run the intro. + */ + char tempbuff[5]; + WWGetPrivateProfileString("Intro", "PlayIntro", "Yes", tempbuff, 4, buffer); + if ((stricmp(tempbuff, "No") == 0) || SpawnedFromWChat) { + Special.IsFromInstall = false; + }else{ + Special.IsFromInstall = true; + } + SlowPalette = WWGetPrivateProfileInt("Options", "SlowPalette", 1, buffer); + +#ifdef DEMO + /* + ** Check for override directory path for CD searches. + */ + WWGetPrivateProfileString("CD", "Path", ".", OverridePath, sizeof(OverridePath), buffer); +#endif + + /* + ** Regardless of whether we should run it or not, here we're + ** gonna change it to say "no" in the future. + */ + WWWritePrivateProfileString("Intro", "PlayIntro", "No", buffer); + cfile.Write(buffer, strlen(buffer)); + + Free(buffer); + + CCDebugString ("C&C95 - Checking availability of C&CSPAWN.INI packet from WChat.\n"); + if (DDEServer.Get_MPlayer_Game_Info()){ + CCDebugString ("C&C95 - C&CSPAWN.INI packet available.\n"); + Check_From_WChat(NULL); + }else{ + CCDebugString ("C&C95 - C&CSPAWN.INI packet not arrived yet.\n"); + //Check_From_WChat("C&CSPAWN.INI"); + //if (Special.IsFromWChat){ + // DDEServer.Disable(); + //} + } + + /* + ** If the intro is being run for the first time, then don't + ** allow breaking out of it with the key. + */ + if (Special.IsFromInstall) { + BreakoutAllowed = false; + } + + Memory_Error_Exit = Print_Error_End_Exit; + + CCDebugString ("C&C95 - Entering main game.\n"); + Main_Game(argc, argv); + + VisiblePage.Clear(); + HiddenPage.Clear(); +// Set_Video_Mode(RESET_MODE); + + Memory_Error_Exit = Print_Error_Exit; + + CCDebugString ("C&C95 - About to exit.\n"); + ReadyToQuit = 1; + + PostMessage(MainWindow, WM_DESTROY, 0, 0); + do + { + Keyboard::Check(); + }while (ReadyToQuit == 1); + + CCDebugString ("C&C95 - Returned from final message loop.\n"); + //Prog_End(); + //Invalidate_Cached_Icons(); + //VisiblePage.Un_Init(); + //HiddenPage.Un_Init(); + //AllSurfaces.Release(); + //Reset_Video_Mode(); + //Stop_Profiler(); + return (EXIT_SUCCESS); + + } else { +#ifdef GERMAN + puts("Bitte erst das SETUP-Programm starten.\n"); +#else +#ifdef FRENCH + puts("Lancez d'abord le programme de configuration SETUP.\n"); +#else + puts("Run SETUP program first."); + puts("\n"); +#endif + Kbd.Get(); +#endif + } + +// Remove_Keyboard_Interrupt(); + if (WindowsTimer){ + delete WindowsTimer; + WindowsTimer = NULL; + } + + if (Palette){ + delete [] Palette; + Palette = NULL; + } + } + + /* + ** Restore the current drive and directory. + */ +#ifdef NOT_FOR_WIN95 + _dos_setdrive(olddrive, &drivecount); + chdir(oldpath); +#endif //NOT_FOR_WIN95 + + return(EXIT_SUCCESS); +} + + +/*********************************************************************************************** + * Prog_End -- Cleans up library systems in prep for game exit. * + * * + * This routine should be called before the game terminates. It handles cleaning up * + * library systems so that a graceful return to the host operating system is achieved. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1995 JLB : Created. * + *=============================================================================================*/ +void __cdecl Prog_End(void) +{ +#ifndef DEMO + if (GameToPlay == GAME_MODEM || GameToPlay == GAME_NULL_MODEM) { + NullModem.Change_IRQ_Priority(0); + } +#endif + CCDebugString ("C&C95 - About to call Sound_End.\n"); + Sound_End(); + CCDebugString ("C&C95 - Returned from Sound_End.\n"); + if (WWMouse){ + CCDebugString ("C&C95 - Deleting mouse object.\n"); + delete WWMouse; + WWMouse = NULL; + } + if (WindowsTimer){ + CCDebugString ("C&C95 - Deleting windows timer.\n"); + delete WindowsTimer; + WindowsTimer = NULL; + } + + if (Palette){ + CCDebugString ("C&C95 - Deleting palette object.\n"); + delete [] Palette; + Palette = NULL; + } +} + + +/*********************************************************************************************** + * Delete_Swap_Files -- Deletes previously existing swap files. * + * * + * This routine will scan through the current directory and delete any swap files it may * + * find. This is used to clear out any left over swap files from previous runs (crashes) * + * of the game. This routine presumes that it cannot delete the swap file that is created * + * by the current run of the game. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/27/1995 JLB : Created. * + *=============================================================================================*/ +void Delete_Swap_Files(void) +{ + struct find_t ff; // for _dos_findfirst + + if (!_dos_findfirst("*.SWP", _A_NORMAL, &ff)) { + do { + unlink(ff.name); + } while(!_dos_findnext(&ff)); + } +} + + +void Print_Error_End_Exit(char *string) +{ + printf( "%s\n", string ); + Get_Key(); + Prog_End(); + printf( "%s\n", string ); + exit(1); +} + + +void Print_Error_Exit(char *string) +{ + printf( "%s\n", string ); + exit(1); +} + + + + + + + + +/*********************************************************************************************** + * Read_Setup_Options -- Read stuff in from the INI file that we need to know sooner * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 4:09PM ST : Created * + *=============================================================================================*/ +void Read_Setup_Options( RawFileClass *config_file ) +{ + char *buffer = new char [config_file->Size()]; + + if (config_file->Is_Available()){ + + config_file->Read (buffer, config_file->Size()); + + VideoBackBufferAllowed = WWGetPrivateProfileInt ("Options", "VideoBackBuffer", 1, buffer); + AllowHardwareBlitFills = WWGetPrivateProfileInt ("Options", "HardwareFills", 1, buffer); + ScreenHeight = WWGetPrivateProfileInt ("Options", "Resolution", 0, buffer) ? 480 : 400; + IsV107 = WWGetPrivateProfileInt ("Options", "Compatibility", 0, buffer); + + /* + ** See if an alternative socket number has been specified + */ + int socket = WWGetPrivateProfileInt ("Options", "Socket", 0, buffer); + if (socket >0 ){ + socket += 0x4000; + if (socket >= 0x4000 && socket < 0x8000) { + Ipx.Set_Socket (socket); + } + } + + /* + ** See if a destination network has been specified + */ + char netbuf [512]; + memset (netbuf, 0, sizeof (netbuf) ); + char *netptr = WWGetPrivateProfileString ("Options", "DestNet", NULL, netbuf, sizeof (netbuf), buffer); + + if (netptr && strlen (netbuf)){ + NetNumType net; + NetNodeType node; + + /* + ** Scan the string, pulling off each address piece + */ + int i = 0; + char * p = strtok(netbuf,"."); + int x; + while (p) { + sscanf(p,"%x",&x); // convert from hex string to int + if (i < 4) { + net[i] = (char)x; // fill NetNum + } else { + node[i-4] = (char)x; // fill NetNode + } + i++; + p = strtok(NULL,"."); + } + + /* + ** If all the address components were successfully read, fill in the + ** BridgeNet with a broadcast address to the network across the bridge. + */ + if (i >= 4) { + IsBridge = 1; + memset(node, 0xff, 6); + BridgeNet = IPXAddressClass(net, node); + } + } + + } + + delete [] buffer; +} diff --git a/STATS.CPP b/STATS.CPP new file mode 100644 index 0000000..5d95cb8 --- /dev/null +++ b/STATS.CPP @@ -0,0 +1,676 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : WINSTUB.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 05/29/1996 * + * * + * Last Update : May 29th 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * Internet game statistics to collect and upload to the server * + * * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "tcpip.h" +#include "packet.h" +#include "ccdde.h" + +#define FIELD_PACKET_TYPE "TYPE" +#define FIELD_GAME_ID "IDNO" +#define FIELD_START_CREDITS "CRED" +#define FIELD_BASES "BASE" +#define FIELD_TIBERIUM "TIBR" +#define FIELD_CRATES "CRAT" +#define FIELD_AI_PLAYERS "AIPL" +#define FIELD_CAPTURE_THE_FLAG "FLAG" +#define FIELD_START_UNIT_COUNT "UNIT" +#define FIELD_TECH_LEVEL "TECH" +#define FIELD_SCENARIO "SCEN" +#define FIELD_COMPLETION "CMPL" +#define FIELD_START_TIME "TIME" +#define FIELD_GAME_DURATION "DURA" +#define FIELD_FRAME_RATE "AFPS" +#define FIELD_SPEED_SETTING "SPED" +#define FIELD_GAME_VERSION "VERS" +#define FIELD_GAME_BUILD_DATE "DATE" +#define FIELD_COVERT_PRESENT "COVT" +#define FIELD_CPU_TYPE "PROC" +#define FIELD_MEMORY "MEMO" +#define FIELD_VIDEO_MEMORY "VIDM" +#define FIELD_PLAYER1_HANDLE "NAM1" +#define FIELD_PLAYER2_HANDLE "NAM2" +#define FIELD_PLAYER1_TEAM "SID1" +#define FIELD_PLAYER2_TEAM "SID2" +#define FIELD_PLAYER1_COLOR "COL1" +#define FIELD_PLAYER2_COLOR "COL2" +#define FIELD_PLAYER1_CREDITS "CRD1" +#define FIELD_PLAYER2_CREDITS "CRD2" + +#define FIELD_PLAYER1_UNITS_LEFT "UNL1" +#define FIELD_PLAYER2_UNITS_LEFT "UNL2" +#define FIELD_PLAYER1_INFANTRY_LEFT "INL1" +#define FIELD_PLAYER2_INFANTRY_LEFT "INL2" +#define FIELD_PLAYER1_PLANES_LEFT "PLL1" +#define FIELD_PLAYER2_PLANES_LEFT "PLL2" +#define FIELD_PLAYER1_BUILDINGS_LEFT "BLL1" +#define FIELD_PLAYER2_BUILDINGS_LEFT "BLL2" + +#define FIELD_PLAYER1_UNITS_BOUGHT "UNB1" +#define FIELD_PLAYER2_UNITS_BOUGHT "UNB2" +#define FIELD_PLAYER1_INFANTRY_BOUGHT "INB1" +#define FIELD_PLAYER2_INFANTRY_BOUGHT "INB2" +#define FIELD_PLAYER1_PLANES_BOUGHT "PLB1" +#define FIELD_PLAYER2_PLANES_BOUGHT "PLB2" +#define FIELD_PLAYER1_BUILDINGS_BOUGHT "BLB1" +#define FIELD_PLAYER2_BUILDINGS_BOUGHT "BLB2" + +#define FIELD_PLAYER1_UNITS_KILLED "UNK1" +#define FIELD_PLAYER2_UNITS_KILLED "UNK2" +#define FIELD_PLAYER1_INFANTRY_KILLED "INK1" +#define FIELD_PLAYER2_INFANTRY_KILLED "INK2" +#define FIELD_PLAYER1_PLANES_KILLED "PLK1" +#define FIELD_PLAYER2_PLANES_KILLED "PLK2" +#define FIELD_PLAYER1_BUILDINGS_KILLED "BLK1" +#define FIELD_PLAYER2_BUILDINGS_KILLED "BLK2" + +#define FIELD_PLAYER1_BUILDINGS_CAPTURED "BLC1" +#define FIELD_PLAYER2_BUILDINGS_CAPTURED "BLC2" + +#define FIELD_PLAYER1_CRATES_FOUND "CRA1" +#define FIELD_PLAYER2_CRATES_FOUND "CRA2" +#define FIELD_PLAYER1_HARVESTED "HRV1" +#define FIELD_PLAYER2_HARVESTED "HRV2" + + +#define PACKET_TYPE_HOST_GAME_INFO (unsigned char) 50 +#define PACKET_TYPE_GUEST_GAME_INFO (unsigned char) 51 + +enum { + COMPLETION_CONNECTION_LOST, + COMPLETION_PLAYER_1_WON, + COMPLETION_PLAYER_1_WON_BY_RESIGNATION, + COMPLETION_PLAYER_1_WON_BY_DISCONNECTION, + COMPLETION_PLAYER_2_WON, + COMPLETION_PLAYER_2_WON_BY_RESIGNATION, + COMPLETION_PLAYER_2_WON_BY_DISCONNECTION +}; + + +extern unsigned long PlanetWestwoodGameID; +extern HINSTANCE ProgramInstance; +extern unsigned long PlanetWestwoodStartTime; + +extern "C" char CPUType; + + +bool GameTimerInUse = false; +TimerClass GameTimer; +long GameEndTime; +void *PacketLater = NULL; + +/*********************************************************************************************** + * Send_Statistics_To_Server -- sends internet game statistics to the Westeood server * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/29/96 12:38PM ST : Created * + *=============================================================================================*/ + +void Send_Statistics_Packet(void) +{ +#ifndef DEMO + + PacketClass stats; + HouseClass *player; + static int packet_size; + int index; + bool packet_later = false; // Should the packet be sent later + void *packet; + + static char field_player_handle[5] = { "NAM?" }; + static char field_player_team[5] = { "SID?" }; + static char field_player_color[5] = { "COL?" }; + static char field_player_credits[5] = { "CRD?" }; + static char field_player_units_left[5] = { "UNL?" }; + static char field_player_infantry_left[5] = { "INL?" }; + static char field_player_planes_left[5] = { "PLL?" }; + static char field_player_buildings_left[5] = { "BLL?" }; + static char field_player_units_bought[5] = { "UNB?" }; + static char field_player_infantry_bought[5] = { "INB?" }; + static char field_player_planes_bought[5] = { "PLB?" }; + static char field_player_buildings_bought[5] = { "BLB?" }; + static char field_player_units_killed[5] = { "UNK?" }; + static char field_player_infantry_killed[5] = { "INK?" }; + static char field_player_planes_killed[5] = { "PLK?" }; + static char field_player_buildings_killed[5] = { "BLK?" }; + static char field_player_buildings_captured[5] = { "BLC?" }; + static char field_player_crates_found[5] = { "CRA?" }; + static char field_player_harvested[5] = { "HRV?" }; + + static char *houses[] = { + "GDI", + "NOD", + "NUT", + "JUR", + "M01", + "M02", + "M03", + "M04", + "M05", + "M06" + }; + + CCDebugString ("C&C95 - In Send_Statistics_Packet.\n"); + + + if (!PacketLater){ + + CCDebugString ("C&C95 - PacketLater is false.\n"); + + + /* + ** Field to identify this as C&C 95 internet game statistics packet + */ + if (Server){ + stats.Add_Field(FIELD_PACKET_TYPE, PACKET_TYPE_HOST_GAME_INFO); + }else{ + stats.Add_Field(FIELD_PACKET_TYPE, PACKET_TYPE_GUEST_GAME_INFO); + } + + /* + ** Game ID. A unique game identifier assigned by WChat. + */ + stats.Add_Field(FIELD_GAME_ID, PlanetWestwoodGameID); + + /* + ** Start credits. + */ + stats.Add_Field(FIELD_START_CREDITS, (unsigned long)MPlayerCredits); + + /* + ** Bases (On/Off) + */ + stats.Add_Field(FIELD_BASES, MPlayerBases ? "ON" : "OFF"); + + /* + ** Tiberium (On/Off) + */ + stats.Add_Field(FIELD_TIBERIUM, MPlayerTiberium ? "ON" : "OFF"); + + /* + ** Crates (On/Off) + */ + stats.Add_Field(FIELD_CRATES, MPlayerGoodies ? "ON" : "OFF"); + + /* + ** AI Players (On/Off/Capture the flag) + */ + stats.Add_Field(FIELD_AI_PLAYERS, MPlayerGhosts ? "ON" : "OFF"); + stats.Add_Field(FIELD_CAPTURE_THE_FLAG, Special.IsCaptureTheFlag ? "ON" : "OFF"); + + /* + ** Start unit count + */ + stats.Add_Field(FIELD_START_UNIT_COUNT, (unsigned long)MPlayerUnitCount); + + /* + ** Tech level. + */ + stats.Add_Field(FIELD_TECH_LEVEL, (unsigned long)BuildLevel); + + CCDebugString ("C&C95 - Adding stats field for scenario.\n"); + /* + ** Scenario + */ + char fname[128]; + char namebuffer[40]; + char *abuffer = (char *)_ShapeBuffer; + memset(abuffer, '\0', _ShapeBufferSize); + sprintf(fname,"%s.INI",ScenarioName); + CCFileClass fileo; + fileo.Set_Name (fname); + fileo.Read(abuffer, _ShapeBufferSize-1); + fileo.Close(); + WWGetPrivateProfileString("Basic", "Name", "Nulls-Ville", namebuffer, 40, abuffer); + stats.Add_Field(FIELD_SCENARIO, namebuffer); + //stats.Add_Field(FIELD_SCENARIO, MPlayerScenarios[ScenarioIdx]); + + + /* + ** Game completion status. + ** + ** Connection lost. + ** Player 1 won + ** Player 2 won + ** Player 1 won by resignation + ** Player 2 won by resignation + ** Player 1 aborted + ** Player 2 aborted + ** + */ + CCDebugString ("C&C95 - Adding stats field for completion status.\n"); + HouseClass *player1 = HouseClass::As_Pointer(MPlayerHouses[0]); + HouseClass *player2 = HouseClass::As_Pointer(MPlayerHouses[1]); + + int completion = -1; + + if (ConnectionLost){ + completion = COMPLETION_CONNECTION_LOST; + CCDebugString ("C&C95 - Completion status is connection lost.\n"); + }else{ + + if (player1 && player2){ + if (player1->IGaveUp){ + completion = COMPLETION_PLAYER_2_WON_BY_DISCONNECTION; + CCDebugString ("C&C95 - Completion status is player 1 disconnected.\n"); + } + + if (player2->IGaveUp){ + completion = COMPLETION_PLAYER_1_WON_BY_DISCONNECTION; + CCDebugString ("C&C95 - Completion status is player 2 disconnected.\n"); + } + + + if (player2->IsDefeated){ + /* + ** Player 1 won. Find out how. + */ + completion = COMPLETION_PLAYER_1_WON; + if (player2->Resigned){ + completion = COMPLETION_PLAYER_1_WON_BY_RESIGNATION; + CCDebugString ("C&C95 - Completion status is player 2 resigned.\n"); + }else{ + if (player2->IGaveUp){ + completion = COMPLETION_PLAYER_1_WON_BY_DISCONNECTION; + CCDebugString ("C&C95 - Completion status is player 2 disconnected.\n"); + } + } + + + }else{ + + if (player1->IsDefeated){ + /* + ** Player 2 won. Find out how. + */ + completion = COMPLETION_PLAYER_2_WON; + if (player1->Resigned){ + completion = COMPLETION_PLAYER_2_WON_BY_RESIGNATION; + CCDebugString ("C&C95 - Completion status is player 1 resigned.\n"); + }else{ + if (player1->IGaveUp){ + completion = COMPLETION_PLAYER_2_WON_BY_DISCONNECTION; + CCDebugString ("C&C95 - Completion status is player 1 disconnected.\n"); + } + } + } + } + } + } + + stats.Add_Field (FIELD_COMPLETION, (char) completion); + + + + + + + /* + ** Game start time (GMT or Pacific?) + ** + ** Passed from WChat + */ + stats.Add_Field (FIELD_START_TIME, (long) PlanetWestwoodStartTime); + + /* + ** Game duration (seconds). + */ + stats.Add_Field (FIELD_GAME_DURATION, (long) GameEndTime/60); + + /* + ** Avg. frame rate. + */ + if (GameEndTime/60 == 0){ + stats.Add_Field (FIELD_FRAME_RATE, 0L ); + }else{ + stats.Add_Field (FIELD_FRAME_RATE, (long) Frame / (GameEndTime/60) ); + } + + + CCDebugString ("C&C95 - Adding hardware info stats.\n"); + /* + ** CPU type + */ + stats.Add_Field (FIELD_CPU_TYPE, (char)CPUType); + + /* + ** Memory + */ + MEMORYSTATUS mem_info; + mem_info.dwLength=sizeof(mem_info); + GlobalMemoryStatus(&mem_info); + stats.Add_Field (FIELD_MEMORY, (long)mem_info.dwTotalPhys); + + /* + ** Video memory + */ + DDCAPS video_capabilities; + long video_memory; + + if (DirectDrawObject){ + video_capabilities.dwSize = sizeof (video_capabilities); + if (DD_OK == DirectDrawObject->GetCaps (&video_capabilities , NULL)){ + video_memory = video_capabilities.dwVidMemTotal; + video_memory += 1024*1024 -1; + video_memory &= 0xfff00000; + stats.Add_Field (FIELD_VIDEO_MEMORY, (long) video_memory); + } + } + + CCDebugString ("C&C95 - Adding game info stats.\n"); + /* + ** Game speed setting. + */ + stats.Add_Field (FIELD_SPEED_SETTING, (char)Options.GameSpeed); + + /* + ** C&C 95 version/build date + */ + char version[128]; + sprintf (version, "%d%s", Version_Number(), VersionText); + stats.Add_Field (FIELD_GAME_VERSION, version); + + char path_to_exe[280]; + FILETIME write_time; //File time is 64 bits + + GetModuleFileName (ProgramInstance, path_to_exe, 280); + RawFileClass file; + file.Set_Name(path_to_exe); + file.Open(); + HANDLE handle = file.Get_File_Handle(); + + if (handle != INVALID_HANDLE_VALUE){ + if (GetFileTime (handle, NULL, NULL, &write_time)){ + write_time.dwLowDateTime = htonl (write_time.dwLowDateTime); + write_time.dwHighDateTime = htonl (write_time.dwHighDateTime); + stats.Add_Field (FIELD_GAME_BUILD_DATE, (void*)&write_time, sizeof (write_time)); + } + } + + /* + ** Covert installed? (Yes/No) + */ + stats.Add_Field(FIELD_COVERT_PRESENT, (char) Expansion_Present()); + + CCDebugString ("C&C95 - Adding house specific stats.\n"); + /* + ** Build the player specific statistics + ** + */ + for (int house = 0 ; house < 2 ; house++){ + + player = HouseClass::As_Pointer(MPlayerHouses[house]); + + if (player){ + /* + ** Player handle. + */ + field_player_handle[3] = '1' + (char)house; + stats.Add_Field (field_player_handle, (char*) MPlayerNames[house]); + + /* + ** Player team. (NOD or GDI) + */ + field_player_team[3] = '1' + (char)house; + stats.Add_Field (field_player_team, houses[player->ActLike]); + + /* + ** Player color + */ + field_player_color[3] = '1' + (char)house; + stats.Add_Field (field_player_color, (unsigned char) (player->Class->House - HOUSE_MULTI1)); + + /* + ** Player end credits. + */ + field_player_credits[3] = '1' + (char)house; + stats.Add_Field (field_player_credits, player->Credits + player->Tiberium); + + /* + ** Number of each unit/building type built + */ + field_player_infantry_bought[3] = '1' + (char)house; + field_player_units_bought[3] = '1' + (char)house; + field_player_planes_bought[3] = '1' + (char)house; + field_player_buildings_bought[3] = '1' + (char)house; + + player->InfantryTotals->To_Network_Format(); + player->UnitTotals->To_Network_Format(); + player->AircraftTotals->To_Network_Format(); + player->BuildingTotals->To_Network_Format(); + + stats.Add_Field (field_player_infantry_bought, (void*) player->InfantryTotals->Get_All_Totals(), player->InfantryTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_units_bought, (void*) player->UnitTotals->Get_All_Totals(), player->UnitTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_planes_bought, (void*) player->AircraftTotals->Get_All_Totals(), player->AircraftTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_buildings_bought, (void*) player->BuildingTotals->Get_All_Totals(), player->BuildingTotals->Get_Unit_Count()*4); + + player->InfantryTotals->To_PC_Format(); + player->UnitTotals->To_PC_Format(); + player->AircraftTotals->To_PC_Format(); + player->BuildingTotals->To_PC_Format(); + + /* + ** Clear out the counts and use the space to count up the current number of units/buildings + */ + player->InfantryTotals->Clear_Unit_Total(); + player->AircraftTotals->Clear_Unit_Total(); + player->UnitTotals->Clear_Unit_Total(); + player->BuildingTotals->Clear_Unit_Total(); + + /* + ** Number of units remaining to player + */ + for (index = 0; index < Units.Count(); index++) { + UnitClass const * unit = Units.Ptr(index); + if (unit->House == player){ + player->UnitTotals->Increment_Unit_Total (unit->Class->Type); + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass const * infantry = Infantry.Ptr(index); + if (infantry->House == player && !infantry->Class->IsCivilian){ + player->InfantryTotals->Increment_Unit_Total (infantry->Class->Type); + } + } + + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass const * aircraft = Aircraft.Ptr(index); + if (aircraft->House == player && aircraft->Class->Type != AIRCRAFT_CARGO){ + player->AircraftTotals->Increment_Unit_Total (aircraft->Class->Type); + } + } + + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass const * building = Buildings.Ptr(index); + if (building->House == player){ + player->BuildingTotals->Increment_Unit_Total (building->Class->Type); + } + } + + player->InfantryTotals->To_Network_Format(); + player->UnitTotals->To_Network_Format(); + player->AircraftTotals->To_Network_Format(); + player->BuildingTotals->To_Network_Format(); + + field_player_infantry_left[3] = '1' + (char)house; + field_player_units_left[3] = '1' + (char)house; + field_player_planes_left[3] = '1' + (char)house; + field_player_buildings_left[3] = '1' + (char)house; + stats.Add_Field (field_player_infantry_left, (void*) player->InfantryTotals->Get_All_Totals(), player->InfantryTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_units_left, (void*) player->UnitTotals->Get_All_Totals(), player->UnitTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_planes_left, (void*) player->AircraftTotals->Get_All_Totals(), player->AircraftTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_buildings_left, (void*) player->BuildingTotals->Get_All_Totals(), player->BuildingTotals->Get_Unit_Count()*4); + + + /* + ** Number of enemy units/buildings of each type destroyed. + */ + + player->DestroyedInfantry->To_Network_Format(); + player->DestroyedUnits->To_Network_Format(); + player->DestroyedAircraft->To_Network_Format(); + player->DestroyedBuildings->To_Network_Format(); + + field_player_infantry_killed[3] = '1' + (char)house; + field_player_units_killed[3] = '1' + (char)house; + field_player_planes_killed[3] = '1' + (char)house; + field_player_buildings_killed[3] = '1' + (char)house; + stats.Add_Field (field_player_infantry_killed, (void*) player->DestroyedInfantry->Get_All_Totals(), player->DestroyedInfantry->Get_Unit_Count()*4); + stats.Add_Field (field_player_units_killed, (void*) player->DestroyedUnits->Get_All_Totals(), player->DestroyedUnits->Get_Unit_Count()*4); + stats.Add_Field (field_player_planes_killed, (void*) player->DestroyedAircraft->Get_All_Totals(), player->DestroyedAircraft->Get_Unit_Count()*4); + stats.Add_Field (field_player_buildings_killed, (void*) player->DestroyedBuildings->Get_All_Totals(), player->DestroyedBuildings->Get_Unit_Count()*4); + + + /* + ** Number and type of enemy buildings captured + */ + field_player_buildings_captured[3] = '1' + (char)house; + player->CapturedBuildings->To_Network_Format(); + stats.Add_Field (field_player_buildings_captured, (void*) player->CapturedBuildings->Get_All_Totals(), player->CapturedBuildings->Get_Unit_Count()*4); + + /* + ** Number of crates discovered and their contents + */ + field_player_crates_found[3] = '1' + (char)house; + player->TotalCrates->To_Network_Format(); + stats.Add_Field (field_player_crates_found, (void*) player->TotalCrates->Get_All_Totals(), player->TotalCrates->Get_Unit_Count()*4); + + /* + ** Amount of tiberium turned into credits + */ + field_player_harvested[3] = '1' + (char)house; + stats.Add_Field (field_player_harvested, (unsigned long) player->HarvestedCredits); + } + } + + CCDebugString ("C&C95 - Calling Create_Comms_Packet.\n"); + /* + ** Create the comms packet to be sent + */ + packet = stats.Create_Comms_Packet(packet_size); + CCDebugString ("C&C95 - Returned from Create_Comms_Packet.\n"); + + /* + ** If a player disconnected then dont send the packet at this time - save it for later + */ + if (completion == COMPLETION_PLAYER_1_WON_BY_DISCONNECTION + || completion == COMPLETION_PLAYER_2_WON_BY_DISCONNECTION){ + PacketLater = packet; + CCDebugString ("C&C95 - Flagging to send the packet later.\n"); + return; + } + + }else{ //else for if (!PacketLater) + + CCDebugString ("C&C95 - PacketLater is true.\n"); + + /* + ** Send the packet we calculated earlier when the disconnect occurred + */ + packet = PacketLater; + PacketLater = NULL; + } + + /* + ** Send it..... + */ + int times = 100; //100 times max + CountDownTimerClass send_timer; + + CCDebugString ("C&C95 - About to send stats packet to DDE server.\n"); + while ( ! Send_Data_To_DDE_Server ((char*)packet, packet_size, DDEServerClass::DDE_PACKET_GAME_RESULTS)){ + CCDebugString ("C&C95 - Stats packet send failed.\n"); + send_timer.Set (60, true); + while (send_timer.Time()){}; + } + + + /* + ** Save it to disk as well so I can see it + */ +#if (0) + RawFileClass anotherfile ("packet.net"); + anotherfile.Write(packet, packet_size); +#endif //(0) + /* + ** Tidy up + */ + CCDebugString ("C&C95 - About to delete packet memory.\n"); + delete [] packet; + + GameStatisticsPacketSent = true; + CCDebugString ("C&C95 - Returning from Send_Statistics_Packet.\n"); +#endif //DEMO + +} + + + + + + + + + +void Register_Game_Start_Time(void) +{ + + GameTimer.Set (0, true); + GameTimerInUse = true; +} + + +extern void Register_Game_End_Time(void) +{ + GameEndTime = GameTimer.Time(); + GameTimerInUse = false; +} + diff --git a/STD.LNT b/STD.LNT new file mode 100644 index 0000000..1807aae --- /dev/null +++ b/STD.LNT @@ -0,0 +1,102 @@ +// Watcom C, C++ (32 bit), -si4 -sp4, +// Standard lint options + +// Compiler Options for Watcom C, C++ 32 bit + +-cwc + +// This file contains options to allow PC-lint to process source +// files for your compiler. It is used as follows: +// +// lint co-wc32.lnt source-file(s) +// +-d_M_IX86=200 // assume Intel 80286 architecture -- modify to suit +-d__declspec()= // ignore this construct + + // additional reserve words needed ++rw(_loadds,_export) ++rw(__interrupt,__near,__far,__huge,__fortran,__pascal,__cdecl) ++rw(__export,__loadds,__saveregs,__asm,__fastcall,__stdcall) ++rw(_export) + ++fcd // makes cdecl significant -- used for proto generation ++fcu // chars are by default unsigned ++fsu // so are strings +-d__386__ // pre-defined macro for 386 version, not set by -cwc +-d__FLAT__ // not set by -cwc +-si4 // sizeof(int) is 4 +-spN4 // sizeof(near pointer) is 4 +-spF6 // sizeof( far pointer) is 6 +-sld10 // sizeof(long double) is 10. +-function(exit,_exit) // _exit() is like exit() +-emacro(734,putc) // don't complain about items being too large. +-emacro(506,putc) // don't complain about constant Boolean +-emacro(???,va_arg) // the va_arg() macro can yield 415, 416, 661, 662 + // 796 and 797 (out-of-bounds errors). + + // While processing compiler (library) header files ... +-elib(46) // an unsigned short bit field is used as __FILLER__ +-elib(522) // function return value ignored +-elib(537) // repeated include file (ios.h) +-elib(641) // converting enum to int +-elib(652) // suppress message about #define of earlier declared symbols +-elib(655) // ORing enum's +-elib(726) // extraneous comma in enumeration +-elib(760) // suppress message about multiple identical macro defs +-elib(762) // suppress message about multiple identical declarations and +-elib(806) // small bit field is signed +-elib(1053) // prototypes cannot be distinguished +-elib(1511) // member (rdbuf) hides nonvirtual member +-elib(1704) // private copy constructor +-elib(1712) // default constructor missing +-elib(1717) // empty prototypes +-elib(1720) // strange argument to assignment operator +-elib(1721) // unusual operator =() declaration +-elib(1722) // assignment operator does not return ref to class +-elib(1724) // strange argument to copy constructor + +-esym(1702,operator<<,operator>>) + +// The following functions exhibit variable return modes. +// That is, they may equally-usefully be called for a value +// as called just for their effects. Accordingly we inhibit +// Warning 534 for these functions. +// Feel free to add to or subtract from this list. + +-esym(534,close,creat,fclose,fflush,fprintf,fputc) +-esym(534,fputs,fscanf,fseek,fwrite,lseek,memcpy,memmove,memset) +-esym(534,printf,puts,scanf,sprintf,sscanf,strcat,strcpy) +-esym(534,strncat,strncpy,unlink,write) + +//------------------------------------------------------------------ + +// Please note -- this is a representative set of error suppression +// options. Please adjust to suit your own policies +// See PC-lint for C/C++ manual (chapter LIVING WITH LINT) +// for further details. + +-e502 -e713 -e737 -eau // don't report on signed/unsigned mismatches +-e734 // allow sub-integer loss of information +-e701 -e703 // shifting int left is OK +-e537 // don't care about repeated include file +-e641 // converting enum to int is ok +-e1042 // operator ++/-- don't need class parameters +-e963 -e763 // redundant declarations are ok +-e1712 // no default constructor defined is ok +-e1704 // private constructors are ok +-e534 // ignoring return value is ok +-e732 // going from signed to unsigned parameter is ok +-e1411 // functions hiding base functions is ok +-e788 // switch with default doesn't need all values specified +-e655 -e656 // compatable enum bit and arithmetic operations are ok +-e1542 // members possibly not initialized isn't a valid warning +-e522 // calling 'new' without assignment isn't always an error + +-e1401 // uninitialized by constructor warning disabled. + + +// 32 bit integer and pointer size is four bytes. +-si4 -sp4 + +// Include directories +-ic:\projects\c&czero\code\watcom\h;..\vq\include;..\gcl510\h diff --git a/SUPER.CPP b/SUPER.CPP new file mode 100644 index 0000000..7d6c01b --- /dev/null +++ b/SUPER.CPP @@ -0,0 +1,386 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\super.cpv 1.5 16 Oct 1995 16:49:04 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SUPER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/28/95 * + * * + * Last Update : July 29, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SuperClass::AI -- Process the super weapon AI. * + * SuperClass::Anim_Stage -- Fetches the animation stage for this super weapon. * + * SuperClass::Discharged -- Handles discharged action for special super weapon. * + * SuperClass::Enable -- Enable this super special weapon. * + * SuperClass::Forced_Charge -- Force the super weapon to full charge state. * + * SuperClass::Impatient_Click -- Called when player clicks on unfinished super weapon. * + * SuperClass::Recharge -- Starts the special super weapon recharging. * + * SuperClass::Remove -- Removes super weapon availability. * + * SuperClass::SuperClass -- Constructor for special super weapon objects. * + * SuperClass::Suspend -- Suspend the charging of the super weapon. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * SuperClass::SuperClass -- Constructor for special super weapon objects. * + * * + * This is the constructor for the super weapons. * + * * + * INPUT: recharge -- The recharge delay time (in game frames). * + * * + * charging -- Voice to announce that the weapon is charging. * + * * + * ready -- Voice to announce that the weapon is fully charged. * + * * + * impatient -- Voice to announce current charging state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +SuperClass::SuperClass(int recharge, VoxType ready, VoxType charging, VoxType impatient, VoxType suspend) +{ + IsPresent = false; + IsOneTime = false; + IsReady = false; + IsSuspended = false; + OldStage = -1; + Control = 0; + RechargeTime = recharge; + SuspendTime = 0; + VoxRecharge = ready; + VoxCharging = charging; + VoxImpatient = impatient; + VoxSuspend = suspend; +} + + +/*********************************************************************************************** + * SuperClass::Suspend -- Suspend the charging of the super weapon. * + * * + * This will temporarily put on hold the charging of the special weapon. This might be the * + * result of insufficient power. * + * * + * INPUT: on -- Should the weapon charging be suspended? Else, it will unsuspend. * + * * + * OUTPUT: Was the weapon suspend state changed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::Suspend(bool on) +{ + if (IsPresent && !IsReady && !IsOneTime) { + if (on != IsSuspended) { + if (on) { + SuspendTime = Control; + } else { + Control = SuspendTime; + } + IsSuspended = on; + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * SuperClass::Enable -- Enable this super special weapon. * + * * + * This routine is called when the special weapon needs to be activated. This is used for * + * both the normal super weapons and the speicial one-time super weapons (from crates). * + * * + * INPUT: onetime -- Is this a special one time super weapon? * + * * + * player -- Is this weapon for the player? If true, then there might be a voice * + * announcement of this weapon's availability. * + * * + * quiet -- Request that the weapon start in suspended state (quiet mode). * + * * + * OUTPUT: Was the special super weapon enabled? Failure might indicate that the weapon was * + * already available. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::Enable(bool onetime, bool player, bool quiet) +{ + if (!IsPresent) { + IsPresent = true; + IsOneTime = onetime; + bool retval = Recharge(player && !quiet); + if (quiet) Suspend(true); + return(retval); + } + return(false); +} + + +/*********************************************************************************************** + * SuperClass::Remove -- Removes super weapon availability. * + * * + * Call this routine when the super weapon should be removed because of some outside * + * force. For one time special super weapons, they can never be removed except as the * + * result of discharging them. * + * * + * INPUT: none * + * * + * OUTPUT: Was the special weapon removed and disabled? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::Remove(bool forced) +{ + if (IsPresent && (!IsOneTime || forced)) { + IsReady = false; + IsPresent = false; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SuperClass::Recharge -- Starts the special super weapon recharging. * + * * + * This routine is called when the special weapon is allowed to recharge. Suspension will * + * be disabled and the animation process will begin. * + * * + * INPUT: player -- Is this for a player owned super weapon? If so, then a voice * + * announcement might be in order. * + * * + * OUTPUT: Was the super weapon begun charging up? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::Recharge(bool player) +{ + if (IsPresent && !IsReady) { + IsSuspended = false; + OldStage = -1; +#ifdef CHEAT_KEYS + if (Special.IsSpeedBuild) { + Control = 1; + } else { + Control = RechargeTime; + } +#else + Control = RechargeTime; +#endif + if (player && VoxCharging != VOX_NONE) { + Speak(VoxCharging); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * Superclass::Discharged -- Handles discharged action for special super weapon. * + * * + * This routine should be called when the special super weapon has been discharged. The * + * weapon will either begin charging anew or will be removed entirely -- depends on the * + * one time flag for the weapon. * + * * + * INPUT: player -- Is this special weapon for the player? If so, then there might be a * + * voice announcement. * + * * + * OUTPUT: Should the sidebar be reprocessed because the special weapon has been eliminated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::Discharged(bool player) +{ + if (!IsSuspended && IsPresent && IsReady) { + IsReady = false; + if (IsOneTime) { + IsOneTime = false; + return(Remove()); + } else { + Recharge(player); + } + } + return(false); +} + + +/*********************************************************************************************** + * SuperClass::AI -- Process the super weapon AI. * + * * + * This routine will process the charge up AI for this super weapon object. If the weapon * + * has advanced far enough to change any sidebar graphic that might represent it, then * + * "true" will be returned. Use this return value to intelligenly update the sidebar. * + * * + * INPUT: player -- Is this for the player? If it is and the weapon is now fully charged, * + * then this fully charged state will be announced to the player. * + * * + * OUTPUT: Was the weapon's state changed such that a sidebar graphic update will be * + * necessary? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::AI(bool player) +{ + if (IsPresent && !IsReady) { + if (IsSuspended) { + if (OldStage != -1) { + OldStage = -1; + return(true); + } + } else { + if (Control.Expired()) { + IsReady = true; + if (player && VoxRecharge != VOX_NONE) { + Speak(VoxRecharge); + } + return(true); + } else { + if (Anim_Stage() != OldStage) { + OldStage = Anim_Stage(); + return(true); + } + } + } + } + return(false); +} + + +/*********************************************************************************************** + * SuperClass::Anim_Stage -- Fetches the animation stage for this super weapon. * + * * + * This will return the current animation stage for this super weapon. The value will be * + * between zero (uncharged) to ANIMATION_STAGES (fully charged). Use this value to render * + * the appropriate graphic on the sidebar. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current animation stage for this special super weapon powerup. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +int SuperClass::Anim_Stage(void) const +{ + if (IsPresent) { + if (IsReady) { + return(ANIMATION_STAGES); + } + int time = Control.Time(); + if (IsSuspended) { + time = SuspendTime; + } + + return(Fixed_To_Cardinal(ANIMATION_STAGES, Cardinal_To_Fixed(RechargeTime, RechargeTime-time))); + } + return(0); +} + + +/*********************************************************************************************** + * SuperClass::Impatient_Click -- Called when player clicks on unfinished super weapon. * + * * + * This routine is called when the player clicks on the super weapon icon on the sidebar * + * when the super weapon is not ready yet. This results in a voice message feedback to the * + * player. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +void SuperClass::Impatient_Click(void) const +{ + if (IsSuspended) { + if (VoxSuspend != VOX_NONE) { + Speak(VoxSuspend); + } + } else { + if (VoxImpatient != VOX_NONE) { + Speak(VoxImpatient); + } + } +} + + +/*********************************************************************************************** + * SuperClass::Forced_Charge -- Force the super weapon to full charge state. * + * * + * This routine will force the special weapon to full charge state. Call it when the weapon * + * needs to be instantly charged. The airstrike (when it first becomes available) is a * + * good example. * + * * + * INPUT: player -- Is this for the player? If true, then the full charge state will be * + * announced. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void SuperClass::Forced_Charge(bool player) +{ + if (IsPresent) { + IsReady = true; + IsSuspended = false; + if (player && VoxRecharge != VOX_NONE) { + Speak(VoxRecharge); + } + } +} diff --git a/SUPER.H b/SUPER.H new file mode 100644 index 0000000..a3daaad --- /dev/null +++ b/SUPER.H @@ -0,0 +1,84 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\super.h_v 1.5 16 Oct 1995 16:47:04 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SUPER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/28/95 * + * * + * Last Update : July 28, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SUPER_H +#define SUPER_H + +#include "ftimer.h" + +class SuperClass { + public: + SuperClass(int recharge=0, VoxType charging=VOX_NONE, VoxType ready=VOX_NONE, VoxType impatient=VOX_NONE, VoxType suspend=VOX_NONE); + + bool Suspend(bool on); + bool Enable(bool onetime = false, bool player=false, bool quiet=false); + void Forced_Charge(bool player=false); + bool AI(bool player=false); + bool Remove(bool forced=false); + void Impatient_Click(void) const; + int Anim_Stage(void) const; + bool Discharged(bool player); + bool Is_Ready(void) const {return(IsReady);}; + bool Is_Present(void) const {return(IsPresent);}; + bool Is_One_Time(void) const {return(IsOneTime && IsPresent);}; + + private: + bool Recharge(bool player=false); + + unsigned IsPresent:1; + unsigned IsOneTime:1; + unsigned IsReady:1; + unsigned IsSuspended:1; + + TCountDownTimerClass Control; + int OldStage; + int SuspendTime; + + VoxType VoxRecharge; + VoxType VoxCharging; + VoxType VoxImpatient; + VoxType VoxSuspend; + int RechargeTime; + + enum { + ANIMATION_STAGES=102 + }; +}; + + + +#endif diff --git a/SUPPORT.ASM b/SUPPORT.ASM new file mode 100644 index 0000000..cac959c --- /dev/null +++ b/SUPPORT.ASM @@ -0,0 +1,459 @@ +; +; Command & Conquer(tm) +; Copyright 2025 Electronic Arts Inc. +; +; 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 3 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 General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . +; + +; $Header: F:\projects\c&c\vcs\code\support.asv 2.13 16 Oct 1995 16:52:36 JOE_BOSTIC $ +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : Command & Conquer * +;* * +;* File Name : SUPPORT.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : September 23, 1993 * +;* * +;* Last Update : May 10, 1994 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* strtrim -- Remove the trailing white space from a string. * +;* Fat_Put_Pixel -- Draws a fat pixel. * +;* Conquer_Build_Fading_Table -- Builds custom shadow/light fading table.* +;* Remove_From_List -- Removes a pointer from a list of pointers. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE "gbuffer.inc" + DISPLAY "Command & Conquer assembly support routines." + + CODESEG + + +;*************************************************************************** +;* strtrim -- Remove the trailing white space from a string. * +;* * +;* Use this routine to remove white space characters from the beginning * +;* and end of the string. The string is modified in place by * +;* this routine. * +;* * +;* INPUT: buffer -- Pointer to the string to modify. * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 10/07/1992 JLB : Created. * +;*=========================================================================* +; VOID cdecl strtrim(BYTE *buffer); + global C strtrim :NEAR + PROC strtrim C near + USES ax, edi, esi + + ARG buffer:DWORD ; Pointer to string to modify. + + cmp [buffer],0 + je short ??fini + + ; Prepare for string scanning by loading pointers. + cld + mov esi,[buffer] + mov edi,esi + + ; Strip white space from the start of the string. +??looper: + lodsb + cmp al,20h ; Space + je short ??looper + cmp al,9 ; TAB + je short ??looper + stosb + + ; Copy the rest of the string. +??gruntloop: + lodsb + stosb + or al,al + jnz short ??gruntloop + dec edi + ; Strip the white space from the end of the string. +??looper2: + mov [edi],al + dec edi + mov ah,[edi] + cmp ah,20h + je short ??looper2 + cmp ah,9 + je short ??looper2 + +??fini: + ret + + ENDP strtrim + + +;*************************************************************************** +;* Fat_Put_Pixel -- Draws a fat pixel. * +;* * +;* Use this routine to draw a "pixel" that is bigger than 1 pixel * +;* across. This routine is faster than drawing a similar small shape * +;* and faster than calling Fill_Rect. * +;* * +;* INPUT: x,y -- Screen coordinates to draw the pixel's upper * +;* left corner. * +;* * +;* color -- The color to render the pixel in. * +;* * +;* size -- The number of pixels width of the big "pixel". * +;* * +;* page -- The pointer to a GraphicBuffer class or something * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 03/17/1994 JLB : Created. * +;*=========================================================================* +; VOID cdecl Fat_Put_Pixel(long x, long y, long color, long size, void *page) + global C Fat_Put_Pixel:NEAR + PROC Fat_Put_Pixel C near + USES eax, ebx, ecx, edx, edi, esi + + ARG x:DWORD ; X coordinate of upper left pixel corner. + ARG y:DWORD ; Y coordinate of upper left pixel corner. + ARG color:DWORD ; Color to use for the "pixel". + ARG siz:DWORD ; Size of "pixel" to plot (square). + ARG gpage:DWORD ; graphic page address to plot onto + + cmp [siz],0 + je short ??exit + + ; Set EDI to point to start of logical page memory. + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[gpage] ; get a pointer to viewport + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get the correct offset + + ; Verify the the Y pixel offset is legal. + mov eax,[y] + cmp eax,[(GraphicViewPort ebx).GVPHeight] ;YPIXEL_MAX + jae short ??exit + mov ecx,[(GraphicViewPort ebx).GVPWidth] + add ecx,[(GraphicViewPort ebx).GVPXAdd] + add ecx,[(GraphicViewPort ebx).GVPPitch] + mul ecx + add edi,eax + + ; Verify the the X pixel offset is legal. + + mov edx,[(GraphicViewPort ebx).GVPWidth] + cmp edx,[x] + mov edx,ecx + jbe short ??exit + add edi,[x] + + ; Write the pixel to the screen. + mov ebx,[siz] ; Copy of pixel size. + sub edx,ebx ; Modulo to reach start of next row. + mov eax,[color] +??again: + mov ecx,ebx + rep stosb + add edi,edx ; EDI points to start of next row. + dec [siz] + jnz short ??again + +??exit: + ret + + ENDP Fat_Put_Pixel + + +;*************************************************************************** +;* Conquer_Build_Fading_Table -- Builds custom shadow/light fading table. * +;* * +;* This routine is used to build a special fading table for C&C. There * +;* are certain colors that get faded to and cannot be faded again. * +;* With this rule, it is possible to draw a shadow multiple times and * +;* not have it get any lighter or darker. * +;* * +;* INPUT: palette -- Pointer to the 768 byte IBM palette to build from. * +;* * +;* dest -- Pointer to the 256 byte remap table. * +;* * +;* color -- Color index of the color to "fade to". * +;* * +;* frac -- The fraction to fade to the specified color * +;* * +;* OUTPUT: Returns with pointer to the remap table. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 10/07/1992 JLB : Created. * +;*=========================================================================*/ +;VOID * cdecl Conquer_Build_Fading_Table(VOID *palette, VOID *dest, long color, long frac); + global C Conquer_Build_Fading_Table : NEAR + PROC Conquer_Build_Fading_Table C near + USES ebx, ecx, edi, esi + + ARG palette:DWORD + ARG dest:DWORD + ARG color:DWORD + ARG frac:DWORD + + LOCAL matchvalue:DWORD ; Last recorded match value. + LOCAL targetred:BYTE ; Target gun red. + LOCAL targetgreen:BYTE ; Target gun green. + LOCAL targetblue:BYTE ; Target gun blue. + LOCAL idealred:BYTE + LOCAL idealgreen:BYTE + LOCAL idealblue:BYTE + LOCAL matchcolor:BYTE ; Tentative match color. + +ALLOWED_COUNT EQU 16 +ALLOWED_START EQU 256-ALLOWED_COUNT + + cld + + ; If the source palette is NULL, then just return with current fading table pointer. + cmp [palette],0 + je ??fini1 + cmp [dest],0 + je ??fini1 + + ; Fractions above 255 become 255. + mov eax,[frac] + cmp eax,0100h + jb short ??ok + mov [frac],0FFh +??ok: + + ; Record the target gun values. + mov esi,[palette] + mov ebx,[color] + add esi,ebx + add esi,ebx + add esi,ebx + lodsb + mov [targetred],al + lodsb + mov [targetgreen],al + lodsb + mov [targetblue],al + + ; Main loop. + xor ebx,ebx ; Remap table index. + + ; Transparent black never gets remapped. + mov edi,[dest] + mov [edi],bl + inc edi + + ; EBX = source palette logical number (1..255). + ; EDI = running pointer into dest remap table. +??mainloop: + inc ebx + mov esi,[palette] + add esi,ebx + add esi,ebx + add esi,ebx + + mov edx,[frac] + shr edx,1 + ; new = orig - ((orig-target) * fraction); + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetred] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealred],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetgreen] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealgreen],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetblue] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealblue],dh ; preserve ideal color gun value. + + ; Sweep through a limited set of existing colors to find the closest + ; matching color. + + mov eax,[color] + mov [matchcolor],al ; Default color (self). + mov [matchvalue],-1 ; Ridiculous match value init. + mov ecx,ALLOWED_COUNT + + mov esi,[palette] ; Pointer to original palette. + add esi,(ALLOWED_START)*3 + + ; BH = color index. + mov bh,ALLOWED_START +??innerloop: + + xor edx,edx ; Comparison value starts null. + + ; Build the comparison value based on the sum of the differences of the color + ; guns squared. + lodsb + sub al,[idealred] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealgreen] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealblue] + mov ah,al + imul ah + add edx,eax + jz short ??perfect ; If perfect match found then quit early. + + cmp edx,[matchvalue] + jae short ??notclose + mov [matchvalue],edx ; Record new possible color. + mov [matchcolor],bh +??notclose: + inc bh ; Checking color index. + loop ??innerloop + mov bh,[matchcolor] +??perfect: + mov [matchcolor],bh + xor bh,bh ; Make BX valid main index again. + + ; When the loop exits, we have found the closest match. + mov al,[matchcolor] + stosb + cmp ebx,ALLOWED_START-1 + jne ??mainloop + + ; Fill the remainder of the remap table with values + ; that will remap the color to itself. + mov ecx,ALLOWED_COUNT +??fillerloop: + inc bl + mov al,bl + stosb + loop ??fillerloop + +??fini1: + mov esi,[dest] + mov eax,esi + ret + + ENDP Conquer_Build_Fading_Table + + +;*************************************************************************** +;* Remove_From_List -- Removes a pointer from a list of pointers. * +;* * +;* This low level routine is used to remove a pointer from a list of * +;* pointers. The trailing pointers are moved downward to fill the * +;* hole. * +;* * +;* INPUT: list -- Pointer to list of pointer. * +;* * +;* index -- Pointer to length of pointer list. * +;* * +;* ptr -- The pointer value to search for and remove. * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 04/11/1994 JLB : Created. * +;* 04/22/1994 JLB : Convert to assembly language. * +;* 05/10/1994 JLB : Short pointers now. * +;*=========================================================================*/ +;VOID cdecl Remove_From_List(VOID **list, long *index, long ptr); + global C Remove_From_List:NEAR + PROC Remove_From_List C near + USES edi, esi, ecx, eax + ARG list:DWORD ; Pointer to list. + ARG index:DWORD ; Pointer to count. + ARG element:DWORD ; Element to remove. + + ; Fetch the number of elements in the list. If there are no + ; elements, then just exit quickly. + mov edi,[index] + mov ecx,[edi] + jcxz short ??fini2 + + ; Fetch pointer to list. + cmp [list],0 + je short ??fini2 + mov edi,[list] + + ; Loop through all elements searching for a match. + mov eax,[element] + repne scasd + jne short ??fini2 ; No match found. + + ; Copy all remaining elements down. If this is the + ; last element in the list then nothing needs to be + ; copied -- just decrement the list size. + jcxz short ??nocopy ; No copy necessary. + mov esi,edi + sub edi,4 + rep movsd + + ; Reduce the list count by one. +??nocopy: + mov edi,[index] + dec [DWORD PTR edi] + +??fini2: + ret + + ENDP Remove_From_List + + +; long cdecl Get_EAX(); + global C Get_EAX :NEAR + PROC Get_EAX C near + ret + + ENDP Get_EAX + +;---------------------------------------------------------------------------- + + END diff --git a/TAB.CPP b/TAB.CPP new file mode 100644 index 0000000..891c4a7 --- /dev/null +++ b/TAB.CPP @@ -0,0 +1,253 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\tab.cpv 2.18 16 Oct 1995 16:52:04 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TAB.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : August 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TabClass::AI -- Handles player I/O with the tab buttons. * + * TabClass::Draw_It -- Displays the tab buttons as necessary. * + * TabClass::Set_Active -- Activates a "filefolder tab" button. * + * TabClass::TabClass -- Default construct for the tab button class. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +void const * TabClass::TabShape = NULL; + + +/*********************************************************************************************** + * TabClass::TabClass -- Default construct for the tab button class. * + * * + * The default constructor merely sets the tab buttons to default non-selected state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +TabClass::TabClass(void) +{ + IsToRedraw = false; +// Select = -1; +} + + +/*********************************************************************************************** + * TabClass::Draw_It -- Displays the tab buttons as necessary. * + * * + * This routine is called whenever the display is being redrawn (in some fashion). The * + * parameter can be used to force the tab buttons to redraw completely. The default action * + * is to only redraw if the tab buttons have been explicitly flagged to be redraw. The * + * result of this is the elimination of unnecessary redraws. * + * * + * INPUT: complete -- bool; Force redraw of the entire tab button graphics? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + * 05/19/1995 JLB : New EVA style. * + *=============================================================================================*/ +void TabClass::Draw_It(bool complete) +{ + + SidebarClass::Draw_It(complete); + + if (Debug_Map){ + //HidPage.Unlock(); + return; + } + + /* + ** Redraw the top bar imagery if flagged to do so or if the entire display needs + ** to be redrawn. + */ + int width = SeenBuff.Get_Width(); + int rightx = width - 1; + + if (complete || IsToRedraw) { + + if (LogicPage->Lock()){ + + LogicPage->Fill_Rect(0, 0, rightx, Tab_Height-2, BLACK); + CC_Draw_Shape(TabShape, 0, 0, 0, WINDOW_MAIN, SHAPE_NORMAL); + CC_Draw_Shape(TabShape, 0, width-Eva_Width, 0, WINDOW_MAIN, SHAPE_NORMAL); + Draw_Credits_Tab(); + LogicPage->Draw_Line(0, Tab_Height-1, rightx, Tab_Height-1, BLACK); + + Fancy_Text_Print(TXT_TAB_BUTTON_CONTROLS, Eva_Width/2, 0, 11, TBLACK, TPF_GREEN12_GRAD|TPF_CENTER | TPF_USE_GRAD_PAL); + Fancy_Text_Print(TXT_TAB_SIDEBAR, width-(Eva_Width/2), 0, 11, TBLACK, TPF_GREEN12_GRAD|TPF_CENTER | TPF_USE_GRAD_PAL); + } + LogicPage->Unlock(); + } + + Credits.Graphic_Logic(complete || IsToRedraw); + IsToRedraw = false; +} + + +void TabClass::Draw_Credits_Tab(void) +{ + CC_Draw_Shape(TabShape, 0, 320, 0, WINDOW_MAIN, SHAPE_NORMAL); +} + + +/*********************************************************************************************** + * TC::Hilite_Tab -- Draw a tab in its depressed state * + * * + * * + * * + * INPUT: Tab to draw (not used) * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/21/96 10:47AM ST : Created * + *=============================================================================================*/ + +void TabClass::Hilite_Tab(int tab) +{ + int xpos = 0; + int text = TXT_TAB_BUTTON_CONTROLS; + tab = tab; + + CC_Draw_Shape(TabShape, 1 , xpos, 0, WINDOW_MAIN, SHAPE_NORMAL); + Fancy_Text_Print(TXT_TAB_BUTTON_CONTROLS, 80, 0, 11, TBLACK, TPF_GREEN12|TPF_CENTER | TPF_USE_GRAD_PAL); +} + + +/*********************************************************************************************** + * TabClass::AI -- Handles player I/O with the tab buttons. * + * * + * This routine is called every game tick and passed whatever key the player has supplied. * + * If the input selects a tab button, then the graphic gets updated accordingly. * + * * + * INPUT: input -- The player's input character (might be mouse click). * + * * + * x,y -- Mouse coordinates at time of input. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + * 05/31/1995 JLB : Fixed to handle mouse shape properly. * + * 08/25/1995 JLB : Handles new scrolling option. * + *=============================================================================================*/ +void TabClass::AI(KeyNumType &input, int x, int y) +{ + if (y >= 0 && y < Tab_Height && x < (SeenBuff.Get_Width() - 1) && x > 0) { + + bool ok = false; + int width = SeenBuff.Get_Width(); + + /* + ** If the mouse is at the top of the screen, then the tab bars only work + ** in certain areas. If the special scroll modification is not active, then + ** the tabs never work when the mouse is at the top of the screen. + */ + if (y > 0 || (Special.IsScrollMod && ((x > 3 && x < Eva_Width) || (x < width-3 && x > width-Eva_Width)))) { + ok = true; + } + + if (ok) { + if (input == KN_LMOUSE) { + int sel = -1; + if (x < Eva_Width) sel = 0; + if (x > width-Eva_Width) sel = 1; + if (sel >= 0) { + Set_Active(sel); + input = KN_NONE; + } + } + + Override_Mouse_Shape(MOUSE_NORMAL, false); + } + } + + Credits.AI(); + + SidebarClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * TabClass::Set_Active -- Activates a "filefolder tab" button. * + * * + * This function is used to activate one of the file folder tab buttons that appear at the * + * top edge of the screen. * + * * + * INPUT: select -- The button to activate. 0 = left button, 1=next button, etc. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ + void TabClass::Set_Active(int select) +{ + switch (select) { + case 0: + Queue_Options(); + break; + + case 1: + Map.SidebarClass::Activate(-1); + break; + + default: + break; + } +} + +void TabClass::One_Time(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + Eva_Width = 80 * factor; + Tab_Height = 8 * factor; + + SidebarClass::One_Time(); + TabShape = Hires_Retrieve("TABS.SHP"); +} diff --git a/TAB.H b/TAB.H new file mode 100644 index 0000000..158bb9a --- /dev/null +++ b/TAB.H @@ -0,0 +1,82 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\tab.h_v 2.18 16 Oct 1995 16:45:26 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TAB.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : December 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TAB_H +#define TAB_H + +#include "sidebar.h" +#include "credits.h" + +class TabClass: public SidebarClass +{ + public: + TabClass(void); + + virtual void AI(KeyNumType &input, int x, int y); + virtual void Draw_It(bool complete=false); + + virtual void One_Time(void); // One-time inits + static void Draw_Credits_Tab(void); + static void Hilite_Tab(int tab); + void Redraw_Tab(void) {IsToRedraw = true;Flag_To_Redraw(false);}; + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + inline int Get_Tab_Height(void) { return(Tab_Height); }; + + CreditClass Credits; + + protected: + + /* + ** If the tab graphic is to be redrawn, then this flag is true. + */ + unsigned IsToRedraw:1; + int Eva_Width; + int Tab_Height; + + private: + void Set_Active(int select); + + static void const * TabShape; +}; + + +#endif diff --git a/TARCOM.CPP b/TARCOM.CPP new file mode 100644 index 0000000..4c667fd --- /dev/null +++ b/TARCOM.CPP @@ -0,0 +1,183 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\tarcom.cpv 2.17 16 Oct 1995 16:52:20 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TARCOM.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 16, 1994 * + * * + * Last Update : July 19, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TarComClass::Debug_Dump -- Displays the status of the tarcom class to the mono screen. * + * TarComClass::AI -- Handles the logical AI for the tarcom class. * + * TarComClass::~TarComClass -- Destructor for turret object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * TarComClass::~TarComClass -- Destructor for turret object. * + * * + * This is the destructor for turret objects. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +TarComClass::~TarComClass(void) +{ +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * TarComClass::Debug_Dump -- Displays the status of the tarcom class to the mono screen. * + * * + * This routine is used to display the tarcom class status to the monochrome monitor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void TarComClass::Debug_Dump(MonoClass *mono) const +{ + TurretClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * TarComClass::AI -- Handles the logical AI for the tarcom class. * + * * + * This handles the AI logic for the targeting computer. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void TarComClass::AI(void) +{ + TurretClass::AI(); + + if (Class->Primary != WEAPON_NONE) { + + /* + ** Determine which weapon can fire. First check for the primary weapon. If that weapon + ** cannot fire, then check any secondary weapon. If neither weapon can fire, then the + ** failure code returned is that from the primary weapon. + */ + WeaponTypeClass const * weapon = &Weapons[Class->Primary]; + int primary = 0; + FireErrorType ok = Can_Fire(TarCom, 0); + if (ok != FIRE_OK) { + if (Can_Fire(TarCom, 1) == FIRE_OK) { + ok = FIRE_OK; + primary = 1; + weapon = &Weapons[Class->Secondary]; + } + } + + switch (ok) { + case FIRE_OK: + if (What_Am_I() != RTTI_UNIT) { + IsFiring = false; + } else { + if (!((UnitClass *)this)->Class->IsFireAnim) { + IsFiring = false; + } + } + + if (TurretClass::Fire_At(TarCom, primary)) { + Sound_Effect(weapon->Sound, Coord); + } + break; + + case FIRE_FACING: + if (Class->IsLockTurret) { + if (!Target_Legal(NavCom) && !IsDriving) { + PrimaryFacing.Set_Desired(Direction(TarCom)); + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + } + } else { + if (*this == UNIT_FTANK) { + SecondaryFacing.Set_Desired(Facing_Dir(Dir_Facing(Direction(TarCom)))); + } else { + SecondaryFacing.Set_Desired(Direction(TarCom)); + } +// SecondaryFacing.Set_Desired(Direction256(Center_Coord(), As_Coord(TarCom))); + } + break; + + case FIRE_CLOAKED: + IsFiring = false; + Do_Uncloak(); + break; + } + } + + if (Target_Legal(TarCom) && !IsRotating) { + DirType dir = Direction(TarCom); + + if (Class->IsTurretEquipped) { + SecondaryFacing.Set_Desired(dir); + } else { + + /* + ** Non turret equipped vehicles will rotate their body to face the target only + ** if the vehicle isn't currently moving or facing the correct direction. This + ** applies only to tracked vehicles. Wheeled vehicles never rotate to face the + ** target, since they aren't maneuverable enough. + */ + if ((Class->Speed == SPEED_TRACK || *this == UNIT_BIKE) && !Target_Legal(NavCom) && !IsDriving && PrimaryFacing.Difference(dir)) { + if (*this == UNIT_FTANK) { + PrimaryFacing.Set_Desired(Facing_Dir(Dir_Facing(dir))); + } else { + PrimaryFacing.Set_Desired(dir); + } + } + } + } +} + + diff --git a/TARCOM.H b/TARCOM.H new file mode 100644 index 0000000..4649034 --- /dev/null +++ b/TARCOM.H @@ -0,0 +1,78 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\tarcom.h_v 2.16 16 Oct 1995 16:45:54 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TARCOM.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TARCOM_H +#define TARCOM_H + +#include "turret.h" +#include "bullet.h" + +/**************************************************************************** +** Units that can perform combat are handled by this class. It performs +** such operations as determining threat value down to actually launching the +** projectile. +*/ +class TarComClass : public TurretClass +{ + public: + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + TarComClass(void) {}; + TarComClass(UnitType classid, HousesType house) : TurretClass(classid, house) {}; + virtual ~TarComClass(void); + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + virtual void AI(void); +// virtual bool Target_Something_Nearby(ThreatType rangmatters=THREAT_NORMAL); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + +}; + +#endif + + diff --git a/TARGET.CPP b/TARGET.CPP new file mode 100644 index 0000000..7938350 --- /dev/null +++ b/TARGET.CPP @@ -0,0 +1,502 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\target.cpv 2.17 16 Oct 1995 16:51:06 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TARGET.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : August 27, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * As_Aircraft -- Converts the target value into an aircraft pointer. * + * As_Animation -- Converts target value into animation pointer. * + * As_Building -- Converts a target value into a building object pointer. * + * As_Bullet -- Converts the target into a bullet pointer. * + * As_Cell -- Converts a target value into a cell number. * + * As_Coord -- Converts a target value into a coordinate value. * + * As_Infantry -- If the target is infantry, return a pointer to it. * + * As_Movement_Coord -- Fetches coordinate if trying to move to this target. * + * As_Object -- Converts a target value into an object pointer. * + * As_Team -- Converts a target number into a team pointer. * + * As_TeamType -- Converts a target into a team type pointer. * + * As_Techno -- Converts a target value into a TechnoClass pointer. * + * As_Trigger -- Converts specified target into a trigger pointer. * + * As_Unit -- Converts a target value into a unit pointer. * + * Target_Legal -- Determines if the specified target is legal. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "target.h" + + +/*********************************************************************************************** + * As_Trigger -- Converts specified target into a trigger pointer. * + * * + * This routine will convert the specified target number into a trigger pointer. * + * * + * INPUT: target -- The target number to convert. * + * * + * OUTPUT: Returns with the trigger pointer that the specified target number represents. If * + * it doesn't represent a legal trigger object, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +TriggerClass * As_Trigger(TARGET target) +{ + return(Is_Target_Trigger(target) ? Triggers.Raw_Ptr(Target_Value(target)) : NULL); +} + + +/*********************************************************************************************** + * As_Team -- Converts a target number into a team pointer. * + * * + * This routine will convert the specified target number into a team pointer. * + * * + * INPUT: target -- The target number to convert. * + * * + * OUTPUT: Returns with the team object that the specified target number represents. If it * + * doesn't represent a legal team then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +TeamClass * As_Team(TARGET target) +{ + return(Is_Target_Team(target) ? Teams.Raw_Ptr(Target_Value(target)) : NULL); +} + + +/*********************************************************************************************** + * As_TeamType -- Converts a target into a team type pointer. * + * * + * This routine will convert the specified target number into a team type pointer. * + * * + * INPUT: target -- The target number to convert. * + * * + * OUTPUT: Returns with a pointer to the team type represented by the target number. If the * + * target number doesn't represent a legal team type, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +TeamTypeClass * As_TeamType(TARGET target) +{ + return(Is_Target_TeamType(target) ? TeamTypes.Raw_Ptr(Target_Value(target)) : NULL); +} + + +/*********************************************************************************************** + * As_Animation -- Converts target value into animation pointer. * + * * + * This routine will convert the specified target number into an animation pointer. * + * * + * INPUT: target -- The target number to convert into an animation pointer. * + * * + * OUTPUT: Returns with a pointer to the legal animation that this target represents. If it * + * doesn't represent a legal animation, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +AnimClass * As_Animation(TARGET target) +{ + return(Is_Target_Animation(target) ? Anims.Raw_Ptr(Target_Value(target)) : NULL); +} + + +/*********************************************************************************************** + * As_Bullet -- Converts the target into a bullet pointer. * + * * + * This routine will convert the specified target number into a bullet pointer. * + * * + * INPUT: target -- The target number to convert. * + * * + * OUTPUT: Returns with a pointer to the bullet it specifies. If the target doesn't refer to * + * a legal bullet, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +BulletClass * As_Bullet(TARGET target) +{ + return(Is_Target_Bullet(target) ? Bullets.Raw_Ptr(Target_Value(target)) : NULL); +} + + +/*********************************************************************************************** + * As_Aircraft -- Converts the target value into an aircraft pointer. * + * * + * This routine will convert the specified target value into an aircraft object pointer. * + * * + * INPUT: target -- The target value to convert. * + * * + * OUTPUT: Returns with a pointer to the aircraft that this target value represents. If the * + * specified target value doesn't represent an aircraft, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/27/1995 JLB : Created. * + *=============================================================================================*/ +AircraftClass * As_Aircraft(TARGET target) +{ + return(Is_Target_Aircraft(target) ? Aircraft.Raw_Ptr(Target_Value(target)) : NULL); +} + + +/*********************************************************************************************** + * As_Techno -- Converts a target value into a TechnoClass pointer. * + * * + * This routine will take the target value specified and convert it into a TechnoClass * + * pointer if the target represents an object that has a TechnoClass. * + * * + * INPUT: target -- The target value to convert into a TechnoClass pointer. * + * * + * OUTPUT: Returns with a pointer to the associated object's TechnoClass. If the target * + * cannot be converted into a TechnoClass pointer, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +TechnoClass * As_Techno(TARGET target) +{ + ObjectClass * obj = As_Object(target); + + if (obj && obj->Is_Techno()) { + return(TechnoClass *)obj; + } + return(NULL); +} + + +/*********************************************************************************************** + * As_Object -- Converts a target value into an object pointer. * + * * + * This routine is used to convert the target value specified into an object pointer. If * + * the target doesn't represent an object or the target value is illegal, then NULL is * + * returned. * + * * + * INPUT: target -- The target value to convert from. * + * * + * OUTPUT: Returns with a pointer to the object it represent, or NULL if not an object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * As_Object(TARGET target) +{ + int val = Target_Value(target); + ObjectClass * object = NULL; + + switch (Target_Kind(target)) { + case KIND_INFANTRY: + object = Infantry.Raw_Ptr(val); + break; + + case KIND_UNIT: + object = Units.Raw_Ptr(val); + break; + + case KIND_BUILDING: + object = Buildings.Raw_Ptr(val); + break; + + case KIND_AIRCRAFT: + object = Aircraft.Raw_Ptr(val); + break; + + case KIND_TERRAIN: + object = Terrains.Raw_Ptr(val); + break; + + case KIND_BULLET: + object = Bullets.Raw_Ptr(val); + break; + + case KIND_ANIMATION: + object = Anims.Raw_Ptr(val); + break; + + default: + break; + } +#if (0) + /* + ** Special check to ensure that a target value that references an + ** invalid object will not be converted back into an object pointer. + ** This condition is rare, but could occur in a network game if the + ** object it refers to is destroyed between the time an event message + ** is sent and when it is received. + */ + if (object != NULL && !object->IsActive) { + object = NULL; + } +#endif //(0) + + return(object); +} + + +/*********************************************************************************************** + * As_Unit -- Converts a target value into a unit pointer. * + * * + * This routine is used to convert the target value specified into a pointer to a unit * + * object. * + * * + * INPUT: target -- The target value to convert into a unit pointer. * + * * + * OUTPUT: Returns with a pointer to the unit the target value represents or NULL if not * + * a unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +UnitClass * As_Unit(TARGET target) +{ + return(Is_Target_Unit(target) ? Units.Raw_Ptr(Target_Value(target)) : NULL); +} + + +/*********************************************************************************************** + * As_Infantry -- If the target is infantry, return a pointer to it. * + * * + * This routine will translate the specified target value into an infantry pointer if the * + * target actually represents an infantry object. * + * * + * INPUT: target -- The target to convert to a pointer. * + * * + * OUTPUT: Returns a pointer to the infantry object that this target value represents. If * + * the target doesn't represent an infantry object, then return NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +InfantryClass * As_Infantry(TARGET target) +{ + return(Is_Target_Infantry(target) ? Infantry.Raw_Ptr(Target_Value(target)) : NULL); +} + + +#ifdef NEVER +TerrainClass * As_Terrain(TARGET target) +{ + return(Is_Target_Terrain(target) ? &Terrains[Target_Value(target)] : NULL); +} +#endif + + +/*********************************************************************************************** + * As_Building -- Converts a target value into a building object pointer. * + * * + * This routine is used to convert the target value specified into a building pointer. * + * * + * INPUT: target -- The target value to convert from. * + * * + * OUTPUT: Returns with a pointer to the building object that the target value represents. * + * If it doesn't represent a building, then return NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +BuildingClass * As_Building(TARGET target) +{ + return(Is_Target_Building(target) ? Buildings.Raw_Ptr(Target_Value(target)) : NULL); +} + + +#ifdef NEVER +/*********************************************************************************************** + * Target_Legal -- Determines if the specified target is legal. * + * * + * This routine is used to check for the legality of the target value specified. It is * + * necessary to call this routine if there is doubt about the the legality of the target. * + * It is possible for the unit that a target value represents to be eliminated and thus * + * rendering the target value invalid. * + * * + * INPUT: target -- The target value to check. * + * * + * OUTPUT: bool; Is the target value legal? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +bool Target_Legal(TARGET target) +{ + if (!Target_Legal(target)) return(false); + + ObjectClass * obj = As_Object(target); + if (obj) { + return(obj->Get_Strength() > 0 && obj->IsActive && !obj->IsInLimbo && obj->Class_Of().IsLegalTarget); + } + return(true); +} +#endif + + +/*********************************************************************************************** + * As_Cell -- Converts a target value into a cell number. * + * * + * This routine is used to convert the target value specified, into a cell value. This is * + * necessary for find path and other procedures that need a cell value. * + * * + * INPUT: target -- The target value to convert to a cell value. * + * * + * OUTPUT: Returns with the target value expressed as a cell location. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +CELL As_Cell(TARGET target) +{ + return(Coord_Cell(As_Coord(target))); +} + + +/*********************************************************************************************** + * As_Coord -- Converts a target value into a coordinate value. * + * * + * This routine is used to convert the target value specified into a coordinate value. It * + * is necessary for those procedures that require a coordinate value. * + * * + * INPUT: target -- The target value to convert. * + * * + * OUTPUT: Returns with the target expressed as a COORD value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + * 11/16/1994 JLB : Simplified. * + *=============================================================================================*/ +COORDINATE As_Coord(TARGET target) +{ + if (Target_Legal(target)) { + /* + ** Cell target values are handled as a special case. The value of the target number is + ** actually the cell index number. + */ + if (Is_Target_Cell(target)) { + return(Cell_Coord((CELL)Target_Value(target))); + } + + /* + ** Normal targets correspond to game objects. Fetch the object pointer and then ask it + ** for the center coordinate. Return the center coordinate as the target's coordinate. + */ + ObjectClass * obj = As_Object(target); + if (obj) { + + /* + ** If this is invalid memory or the object is dead then return 0 + ** This is a kludge to fix the problem of team target objects being assigned after + ** the object is already destroyed - 1/15/97 3:13PM + */ + if (IsBadReadPtr ((void*)obj, sizeof (ObjectClass) ) || !obj->IsActive){ +//OutputDebugString ("C&C95 - As_Coord called for invalid target object\m"); + return(0x00000000L); + } + + return(obj->Target_Coord()); + } + } + + /* + ** An unrecognized target value results in a null coordinate value. + */ + return(0x00000000L); +} + + +/*********************************************************************************************** + * As_Movement_Coord -- Fetches coordinate if trying to move to this target. * + * * + * This routine will convert the specified target into a coordinate location. This location * + * is used when moving to the target specified. For cells, this is the center of the cell. * + * For special buildings that allow docking, it is the center location of the docking * + * bay. * + * * + * INPUT: target -- The target to convert into a coordinate value. * + * * + * OUTPUT: Returns with the docking coordinate of the target value specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/27/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE As_Movement_Coord(TARGET target) +{ + if (Target_Legal(target)) { + /* + ** Cell target values are handled as a special case. The value of the target number is + ** actually the cell index number. + */ + if (Is_Target_Cell(target)) { + return(Cell_Coord((CELL)Target_Value(target))); + } + + /* + ** Normal targets correspond to game objects. Fetch the object pointer and then ask it + ** for the center coordinate. Return the center coordinate as the target's coordinate. + */ + ObjectClass * obj = As_Object(target); + if (obj) { + return(obj->Docking_Coord()); + } + } + + /* + ** An unrecognized target value results in a null coordinate value. + */ + return(0x00000000L); +} diff --git a/TARGET.H b/TARGET.H new file mode 100644 index 0000000..00f4a38 --- /dev/null +++ b/TARGET.H @@ -0,0 +1,157 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\target.h_v 2.16 16 Oct 1995 16:45:04 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TARGET.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 25, 1994 * + * * + * Last Update : April 25, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TARGET_H +#define TARGET_H + +/************************************************************************** +** When a unit proceeds with carrying out its mission, it can have several +** intermediate goals. Each goal (or target if you will) can be one of the +** following kinds. +*/ +typedef enum KindType { + KIND_NONE, + KIND_CELL, + KIND_UNIT, + KIND_INFANTRY, + KIND_BUILDING, + KIND_TERRAIN, + KIND_AIRCRAFT, + KIND_TEMPLATE, + KIND_BULLET, + KIND_ANIMATION, + KIND_TRIGGER, + KIND_TEAM, + KIND_TEAMTYPE +} KindType; + + +#define TARGET_MANTISSA 12 // Bits of value precision. +#define TARGET_MANTISSA_MASK (~((~0)<>TARGET_EXPONENT)) + +inline KindType Target_Kind(TARGET a){return (KindType)(((unsigned)a)>>TARGET_MANTISSA);} +inline unsigned Target_Value(TARGET a){return (((unsigned)a) & TARGET_MANTISSA_MASK);} + +inline bool Is_Target_Team(TARGET a) {return (Target_Kind(a) == KIND_TEAM);} +inline bool Is_Target_TeamType(TARGET a) {return (Target_Kind(a) == KIND_TEAMTYPE);} +inline bool Is_Target_Trigger(TARGET a) {return (Target_Kind(a) == KIND_TRIGGER);} +inline bool Is_Target_Infantry(TARGET a) {return (Target_Kind(a) == KIND_INFANTRY);} +inline bool Is_Target_Bullet(TARGET a){return (Target_Kind(a) == KIND_BULLET);} +inline bool Is_Target_Terrain(TARGET a){return (Target_Kind(a) == KIND_TERRAIN);} +inline bool Is_Target_Cell(TARGET a){return (Target_Kind(a) == KIND_CELL);} +inline bool Is_Target_Unit(TARGET a){return (Target_Kind(a) == KIND_UNIT);} +inline bool Is_Target_Building(TARGET a){return (Target_Kind(a) == KIND_BUILDING);} +inline bool Is_Target_Template(TARGET a){return (Target_Kind(a) == KIND_TEMPLATE);} +inline bool Is_Target_Aircraft(TARGET a){return (Target_Kind(a) == KIND_AIRCRAFT);} +inline bool Is_Target_Animation(TARGET a) {return (Target_Kind(a) == KIND_ANIMATION);} + +inline TARGET Build_Target(KindType kind, int value) {return (TARGET)((((unsigned)kind) << TARGET_MANTISSA) | (unsigned)value);} +inline TARGET As_Target(CELL cell){return (TARGET)(((unsigned)KIND_CELL << TARGET_MANTISSA) | cell);} + +class UnitClass; +class BuildingClass; +class TechnoClass; +class TerrainClass; +class ObjectClass; +class InfantryClass; +class BulletClass; +class TriggerClass; +class TeamClass; +class TeamTypeClass; +class AnimClass; +class AircraftClass; + +#ifdef NEVER +class TargetClass +{ + public: + TargetClass(void) {Target.Raw = 0;}; + + /* + ** This handles assignment from an integer and conversion + ** to an integer. + */ + inline TargetClass(int val, KindType kind) {Target.Component.Value=val; Target.Component.Kind=kind;}; + inline TargetClass(int val) {Target.Raw = val;}; + inline operator int () {return Target.Raw;}; + //inline TargetClass & operator = (const int &val) {*((int*)this)=val; return *this;}; + + inline bool Is_Filled(void) {return Target.Component.Kind != KIND_NONE;}; + inline void Invalidate(void) {Target.Component.Kind = 0;}; + inline bool Is_Cell(void) {return Target.Component.Kind == KIND_CELL;}; + inline bool Is_Unit(void) {return Target.Component.Kind == KIND_UNIT;}; + inline bool Is_Building(void) {return Target.Component.Kind == KIND_BUILDING;}; + inline bool Is_Aircraft(void) {return Target.Component.Kind == KIND_AIRCRAFT;}; + inline int As_Value(void) {return Target.Component.Value;}; + inline KindType As_Kind(void) {return Target.Component.Kind;}; + + // Allows comparing one target to another (for equality). + inline bool operator == (TargetClass t1) { + return (Target.Raw == t1.Target.Raw); + }; + + UnitClass * As_Unit(void); + BuildingClass * As_Building(void); + bool Legal(void); + CELL As_Cell(void); + COORDINATE As_Coord(void); + int Distance(TechnoClass *base); + static int As_Target(UnitClass *unit); + static int As_Target(BuildingClass *building); + static int As_Target(CELL cell); + + private: + + /* + ** This is the special encoded target value. + */ + union { + struct { + unsigned Value:TARGET_MANTISSA; + KindType Kind:TARGET_EXPONENT; + } Component; + int Raw; + } Target; + + + static int Build(int value, KindType kind); +}; +#endif + +#endif diff --git a/TCPIP.CPP b/TCPIP.CPP new file mode 100644 index 0000000..cc19bbb --- /dev/null +++ b/TCPIP.CPP @@ -0,0 +1,1004 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TCPIP.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : March 11th, 1996 * + * * + * Last Update : March 20th, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * Overview: * + * * + * Member functions of the TcpipManagerClass which provides the Winsock * + * interface for C&C * + * * + * * + *-------------------------------------------------------------------------* + * Functions: * + * * + * TMC::TcpipManagerClass -- constructor for the TcpipManagerClass * + * TMC::~TcpipManagerClass -- destructor for the TcpipManagerClass * + * TMC::Close -- restores any currently in use Winsock resources * + * TMC::Init -- Initialised Winsock for use. * + * TMC::Start_Server -- Initialise connection and start listening. * + * TMC::Read -- read any pending input from the stream socket * + * TMC::Write -- Send data via the Winsock streaming socket * + * TMC::Add_Client -- A client has requested to connect. * + * TMC::Message_Handler -- Message handler for Winsock. * + * TMC::Set_Host_Address -- Set the address of the host * + * TMC::Start_Client -- Start trying to connect to a game host * + * TMC::Close_Socket -- Close an opened Winsock socket. * + * TMC::Copy_To_In_Buffer -- copy data from our winsock buffer * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include "tcpip.h" + +#ifdef FORCE_WINSOCK + + +/* +** Nasty globals +*/ +BOOL Server; //Is this player acting as client or server +TcpipManagerClass Winsock; //The object for interfacing with Winsock + + + +/*********************************************************************************************** + * TMC::TcpipManagerClass -- constructor for the TcpipManagerClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:51PM ST : Created * + *=============================================================================================*/ +TcpipManagerClass::TcpipManagerClass(void) +{ + WinsockInitialised = FALSE; + Connected = FALSE; + UseUDP = TRUE; + SocketReceiveBuffer = 4096; + SocketSendBuffer = 4096; + +} + + +/*********************************************************************************************** + * TMC::~TcpipManagerClass -- destructor for the TcpipManagerClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:52PM ST : Created * + *=============================================================================================*/ + +TcpipManagerClass::~TcpipManagerClass(void) +{ + Close(); +} + + +/*********************************************************************************************** + * TMC::Close -- restores any currently in use Winsock resources * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:52PM ST : Created * + *=============================================================================================*/ + +void TcpipManagerClass::Close(void) +{ + /* + ** If we never initialised the class in the first place then just return + */ + if (!WinsockInitialised) return; + + /* + ** Cancel any outstaning asyncronous events + */ + if (Async){ + WSACancelAsyncRequest(Async); + } + + /* + ** Close any open sockets + */ + if (ConnectSocket != INVALID_SOCKET){ + Close_Socket(ConnectSocket); + ConnectSocket = INVALID_SOCKET; + } + + if (ListenSocket != INVALID_SOCKET){ + Close_Socket(ListenSocket); + ListenSocket = INVALID_SOCKET; + } + + if (UDPSocket != INVALID_SOCKET){ + Close_Socket(ListenSocket); + UDPSocket = INVALID_SOCKET; + } + + /* + ** Call the Winsock cleanup function to say we are finished using Winsock + */ + WSACleanup(); + + WinsockInitialised = FALSE; + Connected = FALSE; +} + + + +/*********************************************************************************************** + * TMC::Init -- Initialised Winsock for use. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if Winsock is available and was initialised * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:54PM ST : Created * + *=============================================================================================*/ + +BOOL TcpipManagerClass::Init(void) +{ + short version; + int rc; + + /* + ** Just return true if we are already set up + */ + if (WinsockInitialised) return (TRUE); + + /* + ** Initialise sockets to null + */ + ListenSocket = INVALID_SOCKET; + ConnectSocket =INVALID_SOCKET; + UDPSocket = INVALID_SOCKET; + + /* + ** Start WinSock, and fill in our WinSockData + */ + version = (WINSOCK_MINOR_VER << 8) | WINSOCK_MAJOR_VER; + rc = WSAStartup(version, &WinsockInfo); + if (rc != 0) { + return (FALSE); + } + + /* + ** Check the Winsock version number + */ + if ((WinsockInfo.wVersion & 0x00ff) != (version & 0x00ff) || + (WinsockInfo.wVersion >> 8) != (version >> 8)) { + return (FALSE); + } + + /* + ** Everything is OK so return success + */ + WinsockInitialised = TRUE; + return (TRUE); + +} + + + + +/*********************************************************************************************** + * TMC::Start_Server -- initialise out connection as the server. Start listening for clients. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:56PM ST : Created * + *=============================================================================================*/ + +void TcpipManagerClass::Start_Server(void) +{ + int i; + //struct sockaddr_in addr; + + Start_Client(); + + /* + ** Set up the incoming and outgoing data buffers head and tail pointers + */ + //InBufferHead = 0; + //InBufferTail = 0; + //OutBufferHead= 0; + //OutBufferTail= 0; + + TXBufferHead = 0; + TXBufferTail = 0; + RXBufferHead = 0; + RXBufferTail = 0; + + for (i=0 ; ih_name); + } + Async = 0; + return; + + }else{ + /* + ** We are the client + */ + ConnectStatus = CONTACTING_SERVER; + if (WSAGETASYNCERROR(lParam)==0) { + hentry = (struct hostent *)&HostBuff[0]; + strcpy (Server.Name, hentry->h_name); + } + else { + Server.Name[0] = 0; + } + Async = 0; + return; + } + + + /* + ** Retrieve host by name: Start connecting now that we have the + ** address. + */ + case WM_HOSTBYNAME: + if (WSAGETASYNCERROR(lParam)==0) { + hentry = (struct hostent *)&HostBuff[0]; + memcpy (&(Server.Addr.s_addr), hentry->h_addr, 4); + memcpy(&UDPIPAddress, hentry->h_addr, 4); + strcpy (Server.DotAddr, inet_ntoa(Server.Addr)); + ConnectStatus = CONNECTED_OK; + Connected = TRUE; + } + else { + Server.Name[0] = 0; + strcpy (Server.DotAddr, "????"); + ConnectStatus = SERVER_ADDRESS_LOOKUP_FAILED; + } + Async = 0; + return; + + + /* + ** Connection is ready - accept the client + */ + case WM_ACCEPT: + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + ConnectStatus = UNABLE_TO_ACCEPT_CLIENT; + return; + } + if (Add_Client()) { + ConnectStatus = CONNECTED_OK; + Connected = TRUE; + } + else { + ConnectStatus = UNABLE_TO_ACCEPT_CLIENT; + } + return; + + + + /* + ** Handle UDP packet events + */ + case WM_UDPASYNCEVENT: + event = WSAGETSELECTEVENT(lParam); + switch (event) { + + case FD_READ: + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + Clear_Socket_Error(UDPSocket); + return; + } + addr_len = sizeof(addr); + rc = recvfrom(UDPSocket, ReceiveBuffer, WS_RECEIVE_BUFFER_LEN, 0, + (LPSOCKADDR)&addr, &addr_len); + if (rc == SOCKET_ERROR) { + Clear_Socket_Error(UDPSocket); + return; + } + memcpy(&UDPIPAddress, &addr.sin_addr.s_addr, 4); + Copy_To_In_Buffer(rc); + return; + + + case FD_WRITE: + if (UseUDP){ + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + Clear_Socket_Error(UDPSocket); + return; + } + + addr.sin_family = AF_INET; + addr.sin_port = htons(PlanetWestwoodPortNumber); + memcpy (&addr.sin_addr.s_addr, &UDPIPAddress, 4); + + /* + ** Send as many bytes as there are in the buffer; if there's + ** an error, just bail out. If we get a WOULDBLOCK error, + ** WinSock will send us another message when the socket is + ** available for another write. + */ + while (TransmitBuffers[TXBufferTail].InUse){ + rc = sendto(UDPSocket, + TransmitBuffers[TXBufferTail].Buffer, + TransmitBuffers[TXBufferTail].DataLength, + 0, + (LPSOCKADDR)&addr, + sizeof (addr)); + + if (rc == SOCKET_ERROR){ + if (WSAGetLastError() != WSAEWOULDBLOCK) { + Clear_Socket_Error(UDPSocket); + } + break; + } + TransmitBuffers[TXBufferTail++].InUse = false; + TXBufferTail &= WS_NUM_TX_BUFFERS-1; + } + return; + } + } + + + + /* + ** Handle the asynchronous event callbacks + */ + case WM_ASYNCEVENT: + event = WSAGETSELECTEVENT(lParam); + switch (event) { + /* + ** FD_CLOSE: the client has gone away. Remove the client from our system. + */ + case FD_CLOSE: + rc = WSAGETSELECTERROR(lParam); + if (rc != 0 && rc != WSAECONNRESET) { + ConnectStatus = CONNECTION_LOST; + return; + } + if (Async != 0) { + WSACancelAsyncRequest(Async); + } + WSAAsyncSelect (ConnectSocket, MainWindow, WM_ASYNCEVENT, 0); + Close_Socket (ConnectSocket); + ConnectSocket = INVALID_SOCKET; + //Connected = FALSE; + ConnectStatus = CONNECTION_LOST; + break; +#if (0) + /* + ** FD_READ: Data is available on our socket. This message is sent every time + ** data becomes available so we only have to do one read + */ + case FD_READ: + if (!UseUDP){ + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + return; + } + rc = recv(ConnectSocket, ReceiveBuffer, WS_RECEIVE_BUFFER_LEN, 0); + if (rc == SOCKET_ERROR) { + Clear_Socket_Error(ConnectSocket); + return; + } + Copy_To_In_Buffer(rc); + return; + } + + /* + ** FD_WRITE: The socket is available for writing. We may actually have sent this + ** message from elewhere in the class. + */ + case FD_WRITE: + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + return; + } + /* + ** Send as many bytes as there are in the buffer; if there's + ** an error, just bail out. If we get a WOULDBLOCK error, + ** WinSock will send us another message when the socket is + ** available for another write. + */ + while (OutBufferHead > OutBufferTail){ + rc = send(ConnectSocket, OutBuffer + OutBufferTail, + OutBufferHead - OutBufferTail, 0); + if (rc == SOCKET_ERROR){ + if (WSAGetLastError() != WSAEWOULDBLOCK) { + Clear_Socket_Error(ConnectSocket); + } + break; + } + OutBufferTail+=rc; + } + if (OutBufferHead == OutBufferTail){ + OutBufferHead = OutBufferTail = 0; + } + return; +#endif //(0) + /* + ** FD_CONNECT: A connection was made, or an error occurred. + */ + case FD_CONNECT: + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + ConnectStatus = UNABLE_TO_CONNECT; + return; + } + + ConnectStatus = CONNECTED_OK; + Connected = TRUE; + return; + + } + } +} + + + +/*********************************************************************************************** + * TMC::Copy_To_In_Buffer -- copy data from our winsock buffer to our internal buffer * + * * + * * + * * + * INPUT: bytes to copy * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:17PM ST : Created * + *=============================================================================================*/ +void TcpipManagerClass::Copy_To_In_Buffer(int bytes) +{ + if (!ReceiveBuffers[RXBufferHead].InUse){ + memcpy (ReceiveBuffers[RXBufferHead].Buffer, ReceiveBuffer, MIN(bytes, WS_INTERNET_BUFFER_LEN)); + ReceiveBuffers[RXBufferHead].InUse = true; + ReceiveBuffers[RXBufferHead++].DataLength = MIN(bytes, WS_INTERNET_BUFFER_LEN); + RXBufferHead &= WS_NUM_RX_BUFFERS-1; + } +} + + + +/*********************************************************************************************** + * TMC::Set_Host_Address -- Set the address of the host game we want to connect to * + * * + * * + * * + * INPUT: ptr to address string * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:19PM ST : Created * + *=============================================================================================*/ +void TcpipManagerClass::Set_Host_Address(char *address) +{ + strcpy(HostAddress, address); +} + + + +/*********************************************************************************************** + * TMC::Start_Client -- Start trying to connect to a game host * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:19PM ST : Created * + *=============================================================================================*/ + +void TcpipManagerClass::Start_Client(void) +{ + struct sockaddr_in addr; + bool delay = true; + int i; + + addr.sin_family = AF_INET; + addr.sin_port = 0; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + /* + ** Set up the incoming and outgoing data buffers head and tail pointers + */ +// InBufferHead = 0; +// InBufferTail = 0; +// OutBufferHead= 0; +// OutBufferTail= 0; + + TXBufferHead = 0; + TXBufferTail = 0; + RXBufferHead = 0; + RXBufferTail = 0; + + for (i=0 ; i. +*/ + + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TCPIP.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : March 11th, 1996 * + * * + * Last Update : March 11th, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * * + * * + * * + *-------------------------------------------------------------------------* + * Functions: * + * * + * * + * * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +extern bool Server; + +#define FORCE_WINSOCK 1 + +#define WINSOCK_MINOR_VER 1 +#define WINSOCK_MAJOR_VER 1 +#define PORTNUM 0x1000 +#define UDP_PORT 0x1001 +#define WS_INTERNET_BUFFER_LEN 1024 +#define WS_NUM_TX_BUFFERS 16 //Must be a power of 2 +#define WS_NUM_RX_BUFFERS 16 //MUst be a power of 2 +#define WS_RECEIVE_BUFFER_LEN 1024 +//#define WS_IN_BUFFER_LEN 8192 +//#define WS_OUT_BUFFER_LEN 8192 + +#define PLANET_WESTWOOD_HANDLE_MAX 20 +#define PLANET_WESTWOOD_PASSWORD_MAX 20 +#define IP_ADDRESS_MAX 40 +#define PORT_NUMBER_MAX 6 + +//........................................................................... +// Custom messages: WM_USER + 1 to WM_USER + 100 +// These will be sent to the dialog procedure, for display only. +//........................................................................... +#define WM_UPDATE_STATUS (WM_USER + 1) // update status text +#define WM_UPDATE_CLIENTS (WM_USER + 2) // update client list box +#define WM_UPDATE_MESSAGE (WM_USER + 3) // update received message list + +//........................................................................... +// Messages for Async processing. +//........................................................................... +#define WM_ACCEPT (WM_USER + 101) // client wants to connect +#define WM_HOSTBYADDRESS (WM_USER + 102) // async get host by address +#define WM_HOSTBYNAME (WM_USER + 103) // async get host by name +#define WM_ASYNCEVENT (WM_USER + 104) // other Async event +#define WM_UDPASYNCEVENT (WM_USER + 105) // UDP socket Async event + + +#define VSS_ID -1 // ID of the VSS connection. + +class TcpipManagerClass { + + public: + + TcpipManagerClass(void); + ~TcpipManagerClass(void); + + BOOL Init(void); + void Start_Server(void); + void Start_Client(void); + void Close_Socket(SOCKET s); + void Message_Handler(HWND window, UINT message, UINT wParam, LONG lParam); + void Copy_To_In_Buffer(int bytes); + int Read(void *buffer, int buffer_len); + void Write(void *buffer, int buffer_len); + BOOL Add_Client(void); + void Close(void); + void Set_Host_Address(char *address); + void Set_Protocol_UDP(BOOL state); + void Clear_Socket_Error(SOCKET socket); + + inline BOOL Get_Connected(void) {return (Connected);} + + typedef enum ConnectStatusEnum { + CONNECTED_OK = 0, + NOT_CONNECTING, + CONNECTING, + UNABLE_TO_CONNECT_TO_SERVER, + CONTACTING_SERVER, + SERVER_ADDRESS_LOOKUP_FAILED, + RESOLVING_HOST_ADDRESS, + UNABLE_TO_ACCEPT_CLIENT, + UNABLE_TO_CONNECT, + CONNECTION_LOST + } ConnectStatusEnum; + + inline ConnectStatusEnum Get_Connection_Status(void) {return (ConnectStatus);} + + private: + + //........................................................................... + // This structure defines all the info we need about a host + //........................................................................... + typedef struct { + struct in_addr Addr; // address + char DotAddr[16]; // decimal-dot address string + char Name[255]; // character-string name + } HostType; + + typedef struct { + char Buffer[WS_INTERNET_BUFFER_LEN]; + int DataLength; + bool InUse:1; + } InternetBufferType; + + + BOOL WinsockInitialised; + WSADATA WinsockInfo; + SOCKET ListenSocket; + SOCKET ConnectSocket; + SOCKET UDPSocket; + IN_ADDR ClientIPAddress; + HANDLE Async; + char HostBuff[MAXGETHOSTSTRUCT]; + char ClientName[128]; + char ReceiveBuffer[WS_RECEIVE_BUFFER_LEN]; + //char InBuffer[WS_IN_BUFFER_LEN]; + //int InBufferHead; + //int InBufferTail; + //char OutBuffer[WS_OUT_BUFFER_LEN]; + //int OutBufferHead; + //int OutBufferTail; + BOOL IsServer; + BOOL Connected; + HostType Server; + char HostAddress[IP_ADDRESS_MAX]; + ConnectStatusEnum ConnectStatus; + BOOL UseUDP; + IN_ADDR UDPIPAddress; + int SocketReceiveBuffer; + int SocketSendBuffer; + InternetBufferType ReceiveBuffers[WS_NUM_TX_BUFFERS]; + InternetBufferType TransmitBuffers[WS_NUM_RX_BUFFERS]; + int TXBufferHead; + int TXBufferTail; + int RXBufferHead; + int RXBufferTail; + +}; + + +extern TcpipManagerClass Winsock; + +extern char PlanetWestwoodIPAddress[IP_ADDRESS_MAX]; +extern long PlanetWestwoodPortNumber; +extern bool PlanetWestwoodIsHost; +extern int Read_Game_Options(char *); +extern bool UseVirtualSubnetServer; +extern int InternetMaxPlayers; + + +#define TXT_WINSOCK_CONNECTING 4567+13 +#define TXT_WINSOCK_NOT_CONNECTING 4567+14 +#define TXT_WINSOCK_UNABLE_TO_CONNECT_TO_SERVER 4567+15 +#define TXT_WINSOCK_CONTACTING_SERVER 4567+16 +#define TXT_WINSOCK_SERVER_ADDRESS_LOOKUP_FAILED 4567+17 +#define TXT_WINSOCK_UNABLE_TO_ACCEPT_CLIENT 4567+18 +#define TXT_WINSOCK_UNABLE_TO_CONNECT 4567+19 +#define TXT_WINSOCK_CONNECTION_LOST 4567+20 +#define TXT_WINSOCK_RESOLVING_HOST_ADDRESS 4567+21 + + +#if (0) + + +struct tag tGameStatisticsStruct{ + char WinnersName[20]; + char LosersName[20]; + int WinnersTeam; + int LosersTeam; + int WinnersCredits; + int LosersCredits; + int WinnersKills; + int LosersKills; + int ScenarioPlayed; + int GameTimeElapsed; + int VersionNumber; + char TimeDateStamp[12]; +} GameStatisticsStruct; + + + + + +extern GameStatisticsStruct GameStatistics; + +#endif //(0) + + diff --git a/TDATA.CPP b/TDATA.CPP new file mode 100644 index 0000000..d5f728f --- /dev/null +++ b/TDATA.CPP @@ -0,0 +1,1029 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\tdata.cpv 2.16 16 Oct 1995 16:52:04 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 2, 1994 * + * * + * Last Update : May 8, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TerrainTypeClass::Prep_For_Add -- Prepares to add terrain object. * + * TerrainTypeClass::Display -- Display a generic terrain object. * + * TerrainTypeClass::From_Name -- Convert name to terrain type. * + * TerrainTypeClass::Init -- Loads terrain object shape files. * + * TerrainTypeClass::Create_And_Place -- Creates and places terrain object on map. * + * TerrainTypeClass::Create_On_Of -- Creates a terrain object from type. * + * TerrainTypeClass::TerrainTypeClass -- The general constructor for the terrain type objects* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "terrain.h" +#include "type.h" + + +#define TREE_NORMAL 600 +#define TREE_WEAK 400 +#define TREE_STRONG 800 + +static short const _List1[] = {0, REFRESH_EOL}; +static short const _List000010[] = {MAP_CELL_W+1, REFRESH_EOL}; +static short const _List000011101000[] = {MAP_CELL_W, MAP_CELL_W+1, MAP_CELL_W+2, MAP_CELL_W*2, REFRESH_EOL}; +static short const _List00001[] = {4, REFRESH_EOL}; +static short const _List000110[] = {MAP_CELL_W, MAP_CELL_W+1, REFRESH_EOL}; +static short const _List000111[] = {MAP_CELL_W, MAP_CELL_W+1, MAP_CELL_W+2, REFRESH_EOL}; +static short const _List001011100110[] = {2, MAP_CELL_W, MAP_CELL_W+1, MAP_CELL_W+2, MAP_CELL_W*2+1, MAP_CELL_W*2+2, REFRESH_EOL}; +static short const _List0010[] = {MAP_CELL_W, REFRESH_EOL}; +static short const _List0011[] = {MAP_CELL_W, MAP_CELL_W+1, REFRESH_EOL}; +static short const _List001[] = {2, REFRESH_EOL}; +static short const _List010110[] = {1, MAP_CELL_W, MAP_CELL_W+1, REFRESH_EOL}; +static short const _List01[] = {1, REFRESH_EOL}; +static short const _List1001[] = {0, MAP_CELL_W+1, REFRESH_EOL}; +static short const _List101001[] = {0, 2, MAP_CELL_W+2, REFRESH_EOL}; +static short const _List10[] = {0, REFRESH_EOL}; +static short const _List110000011001[] = {0, 1, MAP_CELL_W+3, MAP_CELL_W*2, MAP_CELL_W*2+3, REFRESH_EOL}; +static short const _List110000[] = {0, 1, REFRESH_EOL}; +static short const _List110001[] = {0, 1, MAP_CELL_W+2, REFRESH_EOL}; +static short const _List1100[] = {0, 1, REFRESH_EOL}; +static short const _List110110[] = {0, 1, MAP_CELL_W, MAP_CELL_W+1, REFRESH_EOL}; +static short const _List1101[] = {0, 1, MAP_CELL_W+1, REFRESH_EOL}; +static short const _List111000010110[] = {0, 1, 2, MAP_CELL_W+3, MAP_CELL_W*2+1, MAP_CELL_W*2+2, REFRESH_EOL}; +static short const _List111001[] = {0, 1, 2, MAP_CELL_W+2, REFRESH_EOL}; +static short const _List111101[] = {0, 1, 2, MAP_CELL_W, MAP_CELL_W+2, REFRESH_EOL}; +static short const _List11110[] = {0, 1, 2, 3, REFRESH_EOL}; + + +static TerrainTypeClass const Tree1Class( + TERRAIN_TREE1, + THEATERF_WINTER|THEATERF_TEMPERATE, + XYP_COORD(11,41), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + true, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + true, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + false, // Is it immune to normal combat damage? + "T01", + TXT_TREE, + TREE_NORMAL, + ARMOR_WOOD, + (short const *)_List0010, + (short const *)_List1001 +); + +static TerrainTypeClass const Tree2Class( + TERRAIN_TREE2, + THEATERF_WINTER|THEATERF_TEMPERATE, + XYP_COORD(11,44), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + true, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + true, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + false, // Is it immune to normal combat damage? + "T02", + TXT_TREE, + TREE_NORMAL, + ARMOR_WOOD, + (short const *)_List0010, + (short const *)_List1001 +); + +static TerrainTypeClass const Tree3Class( + TERRAIN_TREE3, + THEATERF_WINTER|THEATERF_TEMPERATE, + XYP_COORD(12,45), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + true, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + true, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + false, // Is it immune to normal combat damage? + "T03", + TXT_TREE, + TREE_NORMAL, + ARMOR_WOOD, + (short const *)_List0010, + (short const *)_List1001 +); + +static TerrainTypeClass const Tree4Class( + TERRAIN_TREE4, + THEATERF_DESERT, + XYP_COORD(8,9), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + true, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + true, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + false, // Is it immune to normal combat damage? + "T04", + TXT_TREE, + TREE_NORMAL, + ARMOR_WOOD, + (short const *)_List1, + NULL +); + + +static TerrainTypeClass const Tree5Class( + TERRAIN_TREE5, + THEATERF_WINTER|THEATERF_TEMPERATE, + XYP_COORD(15,41), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + true, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + true, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + false, // Is it immune to normal combat damage? + "T05", + TXT_TREE, + TREE_NORMAL, + ARMOR_WOOD, + (short const *)_List0010, + (short const *)_List1001 +); + +static TerrainTypeClass const Tree6Class( + TERRAIN_TREE6, + THEATERF_WINTER|THEATERF_TEMPERATE, + XYP_COORD(16,37), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + true, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + true, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + false, // Is it immune to normal combat damage? + "T06", + TXT_TREE, + TREE_NORMAL, + ARMOR_WOOD, + (short const *)_List0010, + (short const *)_List1001 +); + +static TerrainTypeClass const Tree7Class( + TERRAIN_TREE7, + THEATERF_WINTER|THEATERF_TEMPERATE, + XYP_COORD(15,41), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + true, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + true, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + false, // Is it immune to normal combat damage? + "T07", + TXT_TREE, + TREE_NORMAL, + ARMOR_WOOD, + (short const *)_List0010, + (short const *)_List1001 +); + +static TerrainTypeClass const Tree8Class( + TERRAIN_TREE8, + THEATERF_WINTER|THEATERF_TEMPERATE|THEATERF_DESERT, + XYP_COORD(14,22), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + true, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + true, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + false, // Is it immune to normal combat damage? + "T08", + TXT_TREE, + TREE_NORMAL, + ARMOR_WOOD, + (short const *)_List10, + (short const *)_List01 +); + +static TerrainTypeClass const Tree9Class( + TERRAIN_TREE9, + THEATERF_DESERT, + XYP_COORD(11,22), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + true, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + true, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + false, // Is it immune to normal combat damage? + "T09", + TXT_TREE, + TREE_NORMAL, + ARMOR_WOOD, + (short const *)_List10, + (short const *)_List01 +); + +static TerrainTypeClass const Tree10Class( + TERRAIN_TREE10, + THEATERF_WINTER|THEATERF_TEMPERATE, + XYP_COORD(25,43), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + true, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + true, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + false, // Is it immune to normal combat damage? + "T10", + TXT_TREE, + TREE_NORMAL, + ARMOR_WOOD, + (short const *)_List0011, + (short const *)_List1100 +); + +static TerrainTypeClass const Tree11Class( + TERRAIN_TREE11, + THEATERF_WINTER|THEATERF_TEMPERATE, + XYP_COORD(23,44), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + true, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + true, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + false, // Is it immune to normal combat damage? + "T11", + TXT_TREE, + TREE_NORMAL, + ARMOR_WOOD, + (short const *)_List0011, + (short const *)_List1100 +); + +static TerrainTypeClass const Tree12Class( + TERRAIN_TREE12, + THEATERF_WINTER|THEATERF_TEMPERATE, + XYP_COORD(14,36), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + true, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + true, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + false, // Is it immune to normal combat damage? + "T12", + TXT_TREE, + TREE_NORMAL, + ARMOR_WOOD, + (short const *)_List0010, + (short const *)_List1001 +); + +static TerrainTypeClass const Tree13Class( + TERRAIN_TREE13, + THEATERF_WINTER|THEATERF_TEMPERATE, + XYP_COORD(19,40), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + true, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + true, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + false, // Is it immune to normal combat damage? + "T13", + TXT_TREE, + TREE_NORMAL, + ARMOR_WOOD, + (short const *)_List0010, + (short const *)_List1101 +); + +static TerrainTypeClass const Tree14Class( + TERRAIN_TREE14, + THEATERF_WINTER|THEATERF_TEMPERATE, + XYP_COORD(19,40), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + true, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + true, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + false, // Is it immune to normal combat damage? + "T14", + TXT_TREE, + TREE_NORMAL, + ARMOR_WOOD, + (short const *)_List0011, + (short const *)_List1100 +); + +static TerrainTypeClass const Tree15Class( + TERRAIN_TREE15, + THEATERF_WINTER|THEATERF_TEMPERATE, + XYP_COORD(19,40), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + true, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + true, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + false, // Is it immune to normal combat damage? + "T15", + TXT_TREE, + TREE_NORMAL, + ARMOR_WOOD, + (short const *)_List0011, + (short const *)_List1100 +); + +static TerrainTypeClass const Tree16Class( + TERRAIN_TREE16, + THEATERF_WINTER|THEATERF_TEMPERATE, + XYP_COORD(13,36), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + true, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + true, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + false, // Is it immune to normal combat damage? + "T16", + TXT_TREE, + TREE_NORMAL, + ARMOR_WOOD, + (short const *)_List0010, + (short const *)_List1001 +); + +static TerrainTypeClass const Tree17Class( + TERRAIN_TREE17, + THEATERF_WINTER|THEATERF_TEMPERATE, + XYP_COORD(18,44), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + true, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + true, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + false, // Is it immune to normal combat damage? + "T17", + TXT_TREE, + TREE_NORMAL, + ARMOR_WOOD, + (short const *)_List0010, + (short const *)_List1001 +); + +static TerrainTypeClass const Tree18Class( + TERRAIN_TREE18, + THEATERF_DESERT, + XYP_COORD(33,40), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + true, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + true, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + false, // Is it immune to normal combat damage? + "T18", + TXT_TREE, + TREE_NORMAL, + ARMOR_WOOD, + (short const *)_List000010, + (short const *)_List111101 +); + +static TerrainTypeClass const Split1Class( + TERRAIN_BLOSSOMTREE1, + THEATERF_TEMPERATE|THEATERF_WINTER, + XYP_COORD(18,44), // Center base coordinate offset. + true, // Spawns Tiberium spontaneously? + false, // Does it have destruction animation? + true, // Does it have transformation (blossom tree) anim? + false, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + true, // Is it immune to normal combat damage? + "SPLIT2", + TXT_BLOSSOM_TREE, + TREE_NORMAL, + ARMOR_WOOD, + (short const *)_List0010, + (short const *)_List1101 +); + +static TerrainTypeClass const Split2Class( + TERRAIN_BLOSSOMTREE2, + THEATERF_TEMPERATE|THEATERF_WINTER|THEATERF_DESERT, + XYP_COORD(18,44), // Center base coordinate offset. + true, // Spawns Tiberium spontaneously? + false, // Does it have destruction animation? + true, // Does it have transformation (blossom tree) anim? + false, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + true, // Is it immune to normal combat damage? + "SPLIT3", + TXT_BLOSSOM_TREE, + TREE_NORMAL, + ARMOR_WOOD, + (short const *)_List0010, + (short const *)_List1101 +); + +static TerrainTypeClass const Clump1Class( + TERRAIN_CLUMP1, + THEATERF_WINTER|THEATERF_TEMPERATE, + XYP_COORD(28,41), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + false, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + false, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + true, // Is it immune to normal combat damage? + "TC01", + TXT_TREE, + TREE_NORMAL, + ARMOR_WOOD, + (short const *)_List000110, + (short const *)_List110001 +); + +static TerrainTypeClass const Clump2Class( + TERRAIN_CLUMP2, + THEATERF_WINTER|THEATERF_TEMPERATE, + XYP_COORD(38,41), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + false, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + false, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + true, // Is it immune to normal combat damage? + "TC02", + TXT_TREE, + TREE_NORMAL, + ARMOR_WOOD, + (short const *)_List010110, + (short const *)_List101001 +); + +static TerrainTypeClass const Clump3Class( + TERRAIN_CLUMP3, + THEATERF_WINTER|THEATERF_TEMPERATE, + XYP_COORD(33,35), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + false, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + false, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + true, // Is it immune to normal combat damage? + "TC03", + TXT_TREE, + TREE_NORMAL, + ARMOR_WOOD, + (short const *)_List110110, + (short const *)_List001 +); + +static TerrainTypeClass const Clump4Class( + TERRAIN_CLUMP4, + THEATERF_WINTER|THEATERF_TEMPERATE, + XYP_COORD(44,49), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + false, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + false, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + true, // Is it immune to normal combat damage? + "TC04", + TXT_TREE, + TREE_NORMAL, + ARMOR_WOOD, + (short const *)_List000011101000, + (short const *)_List111000010110 +); + +static TerrainTypeClass const Clump5Class( + TERRAIN_CLUMP5, + THEATERF_WINTER|THEATERF_TEMPERATE, + XYP_COORD(49,58), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + false, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + false, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + true, // Is it immune to normal combat damage? + "TC05", + TXT_TREE, + TREE_NORMAL, + ARMOR_WOOD, + (short const *)_List001011100110, + (short const *)_List110000011001 +); + +static TerrainTypeClass const Rock1Class( + TERRAIN_ROCK1, + THEATERF_DESERT, + XYP_COORD(33,41), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + false, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + false, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + true, // Is it immune to normal combat damage? + "ROCK1", + TXT_ROCK, + 1000, + ARMOR_STEEL, + (short const *)_List0011, + (short const *)_List111001 +); + +static TerrainTypeClass const Rock2Class( + TERRAIN_ROCK2, + THEATERF_DESERT, + XYP_COORD(24,23), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + false, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + false, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + true, // Is it immune to normal combat damage? + "ROCK2", + TXT_ROCK, + 1000, + ARMOR_STEEL, + (short const *)_List1100, + (short const *)_List001 +); + +static TerrainTypeClass const Rock3Class( + TERRAIN_ROCK3, + THEATERF_DESERT, + XYP_COORD(20,39), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + false, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + false, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + true, // Is it immune to normal combat damage? + "ROCK3", + TXT_ROCK, + 1000, + ARMOR_STEEL, + (short const *)_List000110, + (short const *)_List110001 +); + +static TerrainTypeClass const Rock4Class( + TERRAIN_ROCK4, + THEATERF_DESERT, + XYP_COORD(12,20), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + false, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + false, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + true, // Is it immune to normal combat damage? + "ROCK4", + TXT_ROCK, + 1000, + ARMOR_STEEL, + (short const *)_List10, + (short const *)_List01 +); + +static TerrainTypeClass const Rock5Class( + TERRAIN_ROCK5, + THEATERF_DESERT, + XYP_COORD(17,19), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + false, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + false, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + true, // Is it immune to normal combat damage? + "ROCK5", + TXT_ROCK, + 1000, + ARMOR_STEEL, + (short const *)_List10, + (short const *)_List01 +); + +static TerrainTypeClass const Rock6Class( + TERRAIN_ROCK6, + THEATERF_DESERT, + XYP_COORD(28,40), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + false, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + false, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + true, // Is it immune to normal combat damage? + "ROCK6", + TXT_ROCK, + 1000, + ARMOR_STEEL, + (short const *)_List000111, + (short const *)_List110000 +); + +static TerrainTypeClass const Rock7Class( + TERRAIN_ROCK7, + THEATERF_DESERT, + XYP_COORD(57,22), // Center base coordinate offset. + false, // Spawns Tiberium spontaneously? + false, // Does it have destruction animation? + false, // Does it have transformation (blossom tree) anim? + false, // Does it catch fire? + false, // Is this object crushable by heavy vehicles? + false, // Can this object be selected by the player? + false, // Can it be the target of a move or attack order? + true, // Don't make a big deal about it if it gets destroyed? + true, // Is it immune to normal combat damage? + "ROCK7", + TXT_ROCK, + 1000, + ARMOR_STEEL, + (short const *)_List11110, + (short const *)_List00001 +); + + +TerrainTypeClass const * const TerrainTypeClass::Pointers[TERRAIN_COUNT] = { + &Tree1Class, // TERRAIN_TREE1 + &Tree2Class, // TERRAIN_TREE2 + &Tree3Class, // TERRAIN_TREE3 + &Tree4Class, // TERRAIN_TREE4 + &Tree5Class, // TERRAIN_TREE5 + &Tree6Class, // TERRAIN_TREE6 + &Tree7Class, // TERRAIN_TREE7 + &Tree8Class, // TERRAIN_TREE8 + &Tree9Class, // TERRAIN_TREE9 + &Tree10Class, // TERRAIN_TREE10 + &Tree11Class, // TERRAIN_TREE11 + &Tree12Class, // TERRAIN_TREE12 + &Tree13Class, // TERRAIN_TREE13 + &Tree14Class, // TERRAIN_TREE14 + &Tree15Class, // TERRAIN_TREE15 + &Tree16Class, // TERRAIN_TREE16 + &Tree17Class, // TERRAIN_TREE17 + &Tree18Class, // TERRAIN_TREE18 + &Split1Class, // TERRAIN_BLOSSOMTREE1 + &Split2Class, // TERRAIN_BLOSSOMTREE2 + &Clump1Class, // TERRAIN_CLUMP1 + &Clump2Class, // TERRAIN_CLUMP2 + &Clump3Class, // TERRAIN_CLUMP3 + &Clump4Class, // TERRAIN_CLUMP4 + &Clump5Class, // TERRAIN_CLUMP5 + &Rock1Class, // TERRAIN_ROCK1 + &Rock2Class, // TERRAIN_ROCK2 + &Rock3Class, // TERRAIN_ROCK3 + &Rock4Class, // TERRAIN_ROCK4 + &Rock5Class, // TERRAIN_ROCK5 + &Rock6Class, // TERRAIN_ROCK6 + &Rock7Class // TERRAIN_ROCK7 +}; + + +/*********************************************************************************************** + * TerrainTypeClass::TerrainTypeClass -- The general constructor for the terrain type objects. * + * * + * This is the constructor for terrain type objects. It is only used to construct the * + * static (constant) terrain type objects. * + * * + * INPUT: see below.. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1994 JLB : Created. * + *=============================================================================================*/ +TerrainTypeClass::TerrainTypeClass( + TerrainType terrain, + int theater, + COORDINATE centerbase, + bool is_spawn, + bool is_destroyable, + bool is_transformable, + bool is_flammable, + bool is_crushable, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + char const *ininame, + int fullname, unsigned short strength, ArmorType armor, short const *occupy, short const *overlap) : + ObjectTypeClass(true, is_flammable, is_crushable, true, is_selectable, + is_legal_target, is_insignificant, is_immune, + fullname, ininame, armor, strength) +{ + CenterBase = centerbase; + IsTiberiumSpawn = is_spawn; + IsDestroyable = is_destroyable; + IsTransformable = is_transformable; + Theater = theater; + Type = terrain; + Occupy = occupy; + Overlap = overlap; +} + + +/*********************************************************************************************** + * TerrainTypeClass::Init -- Loads terrain object shape files. * + * * + * This routine is used to load up the terrain object shape files. * + * The shape files loaded depends on theater. * + * * + * INPUT: theater -- The theater to load the terrain shape data for. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainTypeClass::Init(TheaterType theater) +{ + if (theater != LastTheater){ + for (TerrainType index = TERRAIN_FIRST; index < TERRAIN_COUNT; index++) { + TerrainTypeClass const & terrain = As_Reference(index); + char fullname[_MAX_FNAME+_MAX_EXT]; + + /* + ** Clear any existing shape pointer. All terrain is theater specific, thus if + ** it isn't loaded in this routine, it shouldn't exist at all. + */ + ((void const *&)terrain.ImageData) = NULL; + + if (terrain.Theater & (1 << theater)) { + + /* + ** Load in the appropriate object shape data. + */ + _makepath(fullname, NULL, NULL, terrain.IniName, Theaters[theater].Suffix); + ((void const *&)terrain.ImageData) = MixFileClass::Retrieve(fullname); + + IsTheaterShape = true; + if (terrain.RadarIcon) delete[] (char *)terrain.RadarIcon; + ((void const *&)terrain.RadarIcon) = Get_Radar_Icon(terrain.Get_Image_Data(), 0, 1, 3); + IsTheaterShape = false; + } + } + } +} + + +/*********************************************************************************************** + * TerrainTypeClass::From_Name -- Convert name to terrain type. * + *  * + * This routine is used to convert a text name into the matching * + * terrain type number. This is used during scenario initialization. * + * * + * INPUT: name -- The name to convert. * + * * + * OUTPUT: Returns the TerrainType that matches the name specified. If * + * no match was found, then TERRAIN_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1994 JLB : Created. * + *=============================================================================================*/ +TerrainType TerrainTypeClass::From_Name(char const * name) +{ + TerrainType index; + + if (name) { + for (index = TERRAIN_FIRST; index < TERRAIN_COUNT; index++) { + if (stricmp(name, Pointers[index]->IniName) == 0) { + return(index); + } + } + } + return(TERRAIN_NONE); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * TerrainTypeClass::Display -- Display a generic terrain object. * + * * + * This routine is used to display a generic terrain object. Typical * + * use is during scenario editing. * + * * + * INPUT: x,y -- Pixel coordinates to display object at (centered). * + * * + * window-- The window to display the object within. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainTypeClass::Display(int x, int y, WindowNumberType window, HousesType) const +{ + CC_Draw_Shape(Get_Image_Data(), 0, x, y, window, SHAPE_NORMAL|SHAPE_CENTER|SHAPE_WIN_REL); +} + + +/*********************************************************************************************** + * TerrainTypeClass::Prep_For_Add -- Prepares to add terrain object. * + * * + * Submits all of the valid terrain objects to the scenario editor for possible selection * + * and subsequent placement on the map. All terrain objects, that have a valid shape * + * file available, are added. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainTypeClass::Prep_For_Add(void) +{ + for (TerrainType index = TERRAIN_FIRST; index < TERRAIN_COUNT; index++) { + if (As_Reference(index).Get_Image_Data()) { + Map.Add_To_List(&As_Reference(index)); + } + } +} +#endif + + +/*********************************************************************************************** + * TerrainTypeClass::Create_And_Place -- Creates and places terrain object on map. * + * * + * This support routine is used by the scenario editor to add a terrain object to the map. * + * * + * INPUT: cell -- The cell to place the terrain object in. * + * * + * OUTPUT: bool; Was the placement successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +bool TerrainTypeClass::Create_And_Place(CELL cell, HousesType ) const +{ + if (new TerrainClass(Type, cell)) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TerrainTypeClass::Create_On_Of -- Creates a terrain object from type. * + * * + * This is used to create a terrain object by using the terrain type as a guide. This * + * routine is typically used by the scenario editor in order to place a terrain object * + * onto the map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the created terrain object or NULL if one couldn't be * + * created. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * TerrainTypeClass::Create_One_Of(HouseClass *) const +{ + return(new TerrainClass(Type, -1)); +} + + +short const * TerrainTypeClass::Occupy_List(bool ) const +{ + if (Occupy) return(Occupy); + + static short const _simple[1] = { + REFRESH_EOL + }; + return(&_simple[0]); +} + + +short const * TerrainTypeClass::Overlap_List(void) const +{ + if (Overlap) return(Overlap); + + static short const _simple[1] = { + REFRESH_EOL + }; + return(&_simple[0]); +} diff --git a/TEAM.CPP b/TEAM.CPP new file mode 100644 index 0000000..355be64 --- /dev/null +++ b/TEAM.CPP @@ -0,0 +1,1505 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\team.cpv 2.18 16 Oct 1995 16:48:34 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEAM.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/11/94 * + * * + * Last Update : August 6, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Assign_Mission_Target -- Sets teams mission target and clears old target * + * TeamClass::Add -- Adds specified object to team. * + * TeamClass::AI -- Process team logic. * + * TeamClass::As_Target -- Converts this team object into a target number. * + * TeamClass::Calc_Center -- Determines average location of team members. * + * TeamClass::Control -- Updates control on a member unit. * + * TeamClass::Coordinate_Attack -- Handles coordinating a team attack. * + * TeamClass::Coordinate_Conscript -- Gives orders to new recruit. * + * TeamClass::Coordinate_Move -- Handles team movement coordination. * + * TeamClass::Coordinate_Regroup -- Handles team idling (regrouping). * + * TeamClass::Coordinate_Unload -- Tells the team to unload passengers now. * + * TeamClass::Detach -- Removes specified target from team tracking. * + * TeamClass::Init -- Initializes the team objects for scenario preparation. * + * TeamClass::Is_A_Member -- Tests if a unit is a member of a team * + * TeamClass::Recruit -- Attempts to recruit members to the team for the given index ID. * + * TeamClass::Remove -- Removes the specified object from the team. * + * TeamClass::Suspend_Teams -- Suspends activity for low priority teams * + * TeamClass::Took_Damage -- Informs the team when the team member takes damage. * + * TeamClass::Validate -- validates team pointer * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "mission.h" + +/* +** This array records the number of teams in existance of each type. +*/ +unsigned char TeamClass::Number[TEAMTYPE_MAX]; + +/* +** This array records the success rating of each of the team types. +*/ +unsigned char TeamClass::Success[TEAMTYPE_MAX]; + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * TeamClass::VTable; + + +/*********************************************************************************************** + * TeamClass::Validate -- validates team pointer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int TeamClass::Validate(void) const +{ + int num; + + num = Teams.ID(this); + if (num < 0 || num >= TEAM_MAX) { + Validate_Error("TEAM"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * TeamClass::Init -- Initializes the team objects for scenario preparation. * + * * + * This routine clears out the team object array in preparation for starting a new * + * scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Init(void) +{ + TeamClass *ptr; + + Teams.Free_All(); + memset(Number, 0, sizeof(Number)); + memset(Success, 0, sizeof(Success)); + + ptr = new TeamClass(); + VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0]; + delete ptr; +} + + +void * TeamClass::operator new (size_t) +{ + void * ptr = Teams.Allocate(); + if (ptr) { + ((TeamClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +void TeamClass::operator delete(void * ptr) +{ + if (ptr) { + ((TeamClass *)ptr)->IsActive = false; + } + Teams.Free((TeamClass *)ptr); +} + + +TeamClass::~TeamClass(void) +{ + if (GameActive && Class) { + Number[TeamTypes.ID(Class)]--; + while (Member) { + Remove(Member); + } + + if (Class->IsTransient && !Number[TeamTypes.ID(Class)]) { + delete (TeamTypeClass *)Class; + } + } +} + + +TeamClass::TeamClass(TeamTypeClass const * type, HouseClass * owner) : + Class(type), + House(owner) +{ + memset(Quantity, 0, sizeof(Quantity)); + IsAltered = true; + IsForcedActive = false; + IsFullStrength = false; + IsUnderStrength = true; + IsReforming = false; + IsLagging = false; + IsMoving = false; + IsHasBeen = false; + Center = 0; + Target = TARGET_NONE; + ObjectiveCenter = 0; + MissionTarget = TARGET_NONE; + Member = 0; + Total = 0; + Risk = 0; + CurrentMission = -1; + IsNextMission = true; + TimeOut = 0; + SuspendTimer.Clear(); + Suspended = false; + Number[TeamTypes.ID(Class)]++; +} + + +/*************************************************************************** + * TeamClass::Assign_Mission_Target -- Sets mission target and clears old * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/16/1995 PWG : Created. * + *=========================================================================*/ +void TeamClass::Assign_Mission_Target(TARGET new_target) +{ + Validate(); + /* + ** First go through and find anyone who is currently targetting + ** the old mission target and clear their Tarcom. + */ + FootClass * unit = Member; + while (unit) { + bool tar = (unit->TarCom == MissionTarget); + bool nav = (unit->NavCom == MissionTarget); + if (tar || nav) { + + /* + ** If the unit was doing something related to the team mission + ** then we kick him into guard mode so that he is easy to change + ** missions for. + */ + unit->Assign_Mission(MISSION_GUARD); + + /* + ** If the unit's tarcom is set to the old mission target, then + ** clear it, so that it will be reset by whatever happens next. + */ + if (nav) { + unit->Assign_Destination(TARGET_NONE); + } + + /* + ** If the unit's navcom is set to the old mission target, then + ** clear it, so that it will be reset by whatever happens next. + */ + if (tar) { + unit->Assign_Target(TARGET_NONE); + } + } + unit = (FootClass *)unit->Member; + } + + /* + ** If there is not currently an override on the current mission target + ** then assign both MissionTarget and Target to the new target. If + ** there is an overide, allow the team to keep fighting the overide but + ** make sure they pick up on the new mission when they are ready. + */ + if (Target == MissionTarget || !Target_Legal(Target)) { + MissionTarget = Target = new_target; + } else { + MissionTarget = new_target; + } +} + + +/*********************************************************************************************** + * TeamClass::AI -- Process team logic. * + * * + * General purpose team logic is handled by this routine. It should be called once per * + * active team per game tick. This routine handles recruitment and assigning orders to * + * member units. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + * 01/06/1995 JLB : Choreographed gesture. * + *=============================================================================================*/ +void TeamClass::AI(void) +{ + Validate(); + int desired = 0; + int old_under = IsUnderStrength; + int old_full = IsFullStrength; + + /* + ** If the team has been suspended then we need to check if its time for + ** us to reactivate the team. If not, no team logic will be processed + ** for this team. + */ + if (Suspended) { + if (!SuspendTimer.Expired()) { + return; + } + Suspended = false; + } + + /* + ** If this team senses that its composition has been altered, then it should + ** recalculate the under strength and full strength flags. + */ + if (IsAltered) { + + for (int index = 0; index < Class->ClassCount; index++) { + desired += Class->DesiredNum[index]; + } + + if (Total) { + IsFullStrength = (Total == desired); + + /* + ** Human controlled teams are always considered full strength. This ensures + ** that no new team members will be recruited and the team won't go into + ** regroup logic. + */ + if (House->IsHuman) { + IsUnderStrength = false; + } else { + + /* + ** Reinforcable teams will revert (or snap out of) the under strength + ** mode when the members transition the magic 1/3 strength threshhold. + */ + if (Class->IsReinforcable) { + IsUnderStrength = (Total <= desired / 3); + } else { + + /* + ** Teams that are not flagged as reinforcable are never considered under + ** strength if the team has already started its main mission. This + ** ensures that once the team has started, it won't dally to pick up + ** new members. + */ + IsUnderStrength = !IsHasBeen; + } + } + IsAltered = false; + } else { + IsUnderStrength = true; + IsFullStrength = false; + Center = 0; + + /* + ** A team that exists on the player's side is automatically destroyed + ** when there are no team members left. This team was created as a + ** result of reinforcement logic and no longer needs to exist when there + ** are no more team members. + */ + if (House->IsHuman || IsHasBeen) { + delete this; + return; + } + } + + /* + ** If the team has gone from under strength to no longer under + ** strength than the team needs to reform. + */ + if (old_under != IsUnderStrength) { + IsReforming = true; + } + } + + /* + ** If the team is under strength, then flag it to regroup. + */ + if (IsMoving && IsUnderStrength) { + IsMoving = false; + CurrentMission = -1; + if (Total) { + Calc_Center(Center, ObjectiveCenter); + + /* + ** When a team is badly damaged and needs to regroup it should + ** pick a friendly building to go and regroup at. Its first preference + ** should be somewhere near repair factory. If it cannot find a repair + ** factory then it should pick another structure that is friendly to + ** its side. + */ + CELL dest = Center; + int max = 0x7FFFFFFF; + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * b = Buildings.Ptr(index); + + if (b && !b->IsInLimbo && b->House == House && b->Class->Primary == WEAPON_NONE) { + CELL cell = Coord_Cell(b->Center_Coord()); + int dist = Map.Cell_Distance(cell, Center) * (Map.Cell_Threat(cell, House->Class->House) + 1); + + if (*b == STRUCT_REPAIR) { + dist >>= 1; + } + if (dist < max) { + cell = Member->Safety_Point(Center, cell, 2, 4); + if (cell != -1) { + max = dist; + dest = cell; + } + } + } + } + + // Should calculate a regroup location. + Target = ::As_Target(dest); + Coordinate_Move(); + return; + } else { + Center = 0; + } + } + + /* + ** Flag this team into action when it gets to full strength. Human owned teams are only + ** used for reinforcement purposes -- always consider them at full strength. + */ + if (!IsMoving && (IsFullStrength || IsForcedActive)) { + IsMoving = true; + IsHasBeen = true; + IsUnderStrength = false; + + /* + ** Infantry can do a gesture when they start their mission. Pick + ** a gesture at random. + */ + FootClass * techno = Member; + DoType doaction = (Random_Pick(1, 2) == 1) ? DO_GESTURE1 : DO_GESTURE2; + while (techno) { + if (!techno->IsInLimbo && techno->What_Am_I() == RTTI_INFANTRY) { + ((InfantryClass *)techno)->Do_Action(doaction); + } + + if (IsReforming || IsForcedActive) { + techno->IsInitiated = true; + } + + techno = techno->Member; + } + CurrentMission = -1; + IsNextMission = true; + IsForcedActive = false; + } + + /* + ** If the team is moving or if there is no center position for + ** the team, then the center position must be recalculated. + */ + if (IsReforming || IsMoving || Center == 0) { + Calc_Center(Center, ObjectiveCenter); + } + + /* + ** Try to recruit members if there is room to do so for this team. + ** Only try to recruit members for a non player controlled team. + */ + if (!IsMoving || (!IsFullStrength && Class->IsReinforcable) && !House->IsHuman) { + for (int index = 0; index < Class->ClassCount; index++) { + if (Quantity[index] < Class->DesiredNum[index]) { + Recruit(index); + } + } + } + + /* + ** If there are no members of the team and the team has reached + ** full strength at one time, then delete the team. + */ + if (!Member && IsHasBeen) { + delete this; + return; + } + + /* + ** If the mission should be advanced to the next entry, then do so at + ** this time. Various events may cause the mission to advance, but it + ** all boils down to the following change-mission code. + */ + if (IsMoving && !IsReforming && IsNextMission) { + IsNextMission = false; + CurrentMission++; + if (CurrentMission < Class->MissionCount) { + TeamMissionStruct const * mission = &Class->MissionList[CurrentMission]; + + TimeOut = mission->Argument * (TICKS_PER_MINUTE/10); + Target = TARGET_NONE; + switch (mission->Mission) { + case TMISSION_MOVECELL: + Assign_Mission_Target(::As_Target((CELL)mission->Argument)); + break; + + case TMISSION_MOVE: + case TMISSION_UNLOAD: + Assign_Mission_Target(::As_Target((CELL)Waypoint[mission->Argument])); + break; + + case TMISSION_ATTACKTARCOM: + Assign_Mission_Target(mission->Argument); + break; + + default: + Assign_Mission_Target(TARGET_NONE); + break; + } + } else { + delete this; + return; + } + } + + /* + ** Perform mission of the team. This depends on the mission list. + */ + if (Member && IsMoving && !IsReforming && !IsUnderStrength) { + /* + ** If the current Target has been dealt with but the mission target + ** has not, then the current target needs to be reset to the mission + ** target. + */ + if (!Target_Legal(Target)) { + Target = MissionTarget; + } + + /* + ** If the current mission is one that times out, then check for + ** this case. If it has timed out then advance to the next + ** mission in the list or disband the team. + */ + TeamMissionStruct const * mission = &Class->MissionList[CurrentMission]; + switch (mission->Mission) { + case TMISSION_ATTACKBASE: + if (!Target_Legal(MissionTarget)) { + Assign_Mission_Target(Member->Greatest_Threat(THREAT_BUILDINGS)); + if (!Target_Legal(MissionTarget)) IsNextMission = true; + } + Coordinate_Attack(); + break; + + case TMISSION_ATTACKUNITS: + if (!Target_Legal(MissionTarget)) { + Assign_Mission_Target(Member->Greatest_Threat(THREAT_VEHICLES|THREAT_INFANTRY)); + if (!Target_Legal(MissionTarget)) IsNextMission = true; + } + Coordinate_Attack(); + break; + + case TMISSION_ATTACKCIVILIANS: + if (!Target_Legal(MissionTarget)) { + Assign_Mission_Target(Member->Greatest_Threat(THREAT_CIVILIANS)); + if (!Target_Legal(MissionTarget)) IsNextMission = true; + } + Coordinate_Attack(); + break; + + case TMISSION_ATTACKTARCOM: + case TMISSION_RAMPAGE: + if (!Target_Legal(MissionTarget)) { + Assign_Mission_Target(Member->Greatest_Threat(THREAT_NORMAL)); + if (!Target_Legal(MissionTarget)) IsNextMission = true; + } + Coordinate_Attack(); + break; + + case TMISSION_DEFENDBASE: + Coordinate_Move(); + break; + +// case TMISSION_HARVEST: +// Coordinate_Move(); +// break; + + case TMISSION_UNLOAD: + Coordinate_Unload(); + break; + + case TMISSION_MOVE: + Coordinate_Move(); + break; + + case TMISSION_RETREAT: + Coordinate_Move(); + break; + + case TMISSION_GUARD: + Coordinate_Regroup(); + break; + + case TMISSION_LOOP: + CurrentMission = mission->Argument-1; + IsNextMission = true; + break; + } + + /* + ** Check for mission time out condition. If the mission does in fact time out, then + ** flag it so that the team mission list will advance. + */ + switch (mission->Mission) { + case TMISSION_ATTACKBASE: + case TMISSION_ATTACKUNITS: + case TMISSION_ATTACKCIVILIANS: + case TMISSION_RAMPAGE: + case TMISSION_DEFENDBASE: + case TMISSION_UNLOAD: + case TMISSION_RETREAT: + case TMISSION_GUARD: + if (TimeOut.Expired()) { + IsNextMission = true; + } + break; + } + + } else { + if (IsMoving) { + IsReforming = !Coordinate_Regroup(); + } else { + Coordinate_Move(); + } + } +} + + +/*********************************************************************************************** + * TeamClass::Add -- Adds specified object to team. * + * * + * Use this routine to add the specified object to the team. The object is checked to make * + * sure that it can be assigned to the team. If it can't, then the object will be left * + * alone and false will be returned. * + * * + * INPUT: obj -- Pointer to the object that is to be assigned to this team. * + * * + * typeindex-- Optional value that specifies the index in the team type class array * + * that this object belongs. * + * * + * OUTPUT: bool; Was the unit added to the team? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + * 01/02/1995 JLB : Initiation flag setup. * + * 08/06/1995 JLB : Allows member stealing from lesser priority teams. * + *=============================================================================================*/ +bool TeamClass::Add(FootClass * obj, int typeindex) +{ + Validate(); + /* + ** If this team doesn't accept new members, then don't accept this one either. + */ +// if (!Class->IsReinforcable && IsMoving) { +// return(false); +// } + + if (!obj || !obj->Strength || (obj->IsInLimbo && !ScenarioInit) || obj->In_Radio_Contact() || obj->House != House) { + return(false); + } + + TeamClass * team = obj->Team; + + /* + ** Trying to add the team member to itself is an error condition. Just return + ** with success, since the end result is the same. + */ + if (team == this) { + return(true); + } + + /* + ** If the object is doing some mission that precludes it from joining + ** a team then don't add it. + */ + if (obj->Mission == MISSION_STICKY || obj->Mission == MISSION_SLEEP || obj->Mission == MISSION_GUARD_AREA || obj->Mission == MISSION_HUNT || obj->Mission == MISSION_HARVEST) { + return(false); + } + + /* + ** If this object is part of another team, then check to make sure that it + ** is permitted to leave the other team in order to join this one. If not, + ** then no further processing is allowed -- bail. + */ + if (team && + (/*team->Total >= Total || team->IsMoving ||*/ + team->Class->RecruitPriority >= Class->RecruitPriority)) { + return(false); + } + + /* + ** If the proper team index was not provided, then find it in the type type class. + ** On the chance that a match could not be found, then it is illegal to add this + ** object to this team -- return with failure flag. + */ + if (typeindex == -1) { + for (typeindex = 0; typeindex < Class->ClassCount; typeindex++) { + if (Class->Class[typeindex] == &obj->Class_Of()) { + break; + } + } + } + + /* + ** If the team is already full of this type, then adding the object is not allowed. + ** Return with a failure flag in this case. + */ + if (Quantity[typeindex] >= Class->DesiredNum[typeindex]) { + return(false); + } + + /* + ** All is ok to add the object to the team, but if the object is already part of + ** another team, then it must be removed from that team first. + */ + if (team) { + team->Remove(obj); + } + + /* + ** Actually add the object to the team. + */ + Quantity[typeindex]++; + obj->IsInitiated = (Member == NULL); + obj->Member = Member; + Member = obj; + obj->Team = this; + Total++; + Risk += obj->Risk(); + if (!Center) { + Calc_Center(Center, ObjectiveCenter); + } + + /* + ** Return with success, since the object was added to the team. + */ + IsAltered = true; + return(true); +} + + +/*********************************************************************************************** + * TeamClass::Remove -- Removes the specified object from the team. * + * * + * Use this routine to remove an object from a team. Objects removed from the team are * + * then available to be recruited by other teams, or even by the same team at a later time. * + * * + * INPUT: obj -- Pointer to the object that is to be removed from this team. * + * * + * typeindex-- Optional index of where this object type is specified in the type * + * type class. This parameter can be omitted. It only serves to make * + * the removal process faster. * + * * + * OUTPUT: bool; Was the object removed from this team? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + * 01/02/1995 JLB : Initiation tracking and team captain selection. * + *=============================================================================================*/ +bool TeamClass::Remove(FootClass * obj, int typeindex) +{ + Validate(); + /* + ** Make sure that the object is in fact a member of this team. If not, then it can't + ** be removed. Return success because the end result is the same. + */ + if (obj->Team != this) { + return(true); + } + + /* + ** If the proper team index was not provided, then find it in the type type class. The + ** team type class will not be set if the appropriate type could not be found + ** for this object. This indicates that the object was illegally added. Continue to + ** process however, since removing this object from the team is a good idea. + */ + if (typeindex == -1) { + for (typeindex = 0; typeindex < Class->ClassCount; typeindex++) { + if (Class->Class[typeindex] == &obj->Class_Of()) { + break; + } + } + } + + /* + ** Decrement the counter for the team class. There is now one less of this object type. + */ + if ((unsigned)typeindex < Class->ClassCount) { + Quantity[typeindex]--; + } + + /* + ** Actually remove the object from the team. Scan through the team members + ** looking for the one that matches the one specified. If it is found, it + ** is unlinked from the member chain. During this scan, a check is made to + ** ensure that at least one remaining member is still initiated. If not, then + ** a new team captain must be chosen. + */ + bool initiated = false; + FootClass * prev = 0; + FootClass * curr = Member; + bool found = false; + while (curr && (!found || !initiated)) { + if (curr == obj) { + if (prev) { + prev->Member = curr->Member; + } else { + Member = curr->Member; + } + FootClass * temp = curr->Member; + curr->Member = 0; + curr->Team = 0; + curr = temp; + Total--; + found = true; + Risk -= obj->Risk(); + continue; + } + + /* + ** If this (remaining) member is initiated, then keep a record of this. + */ + initiated |= curr->IsInitiated; + + prev = curr; + curr = curr->Member; + } + + /* + ** If, after removing the team member, there are no initiated members left + ** in the team, then just make the first remaining member of the team the + ** team captain. Mark the center location of the team as invalid so that + ** it will be centered around the captain. + */ + if (!initiated && Member) { + Member->IsInitiated = true; + Center = 0; + } + + /* + ** Must record that the team composition has changed. At the next opportunity, + ** the team members will be counted and appropriate AI adjustments made. + */ + IsAltered = true; + return(true); +} + + +/*********************************************************************************************** + * TeamClass::Recruit -- Attempts to recruit members to the team for the given index ID. * + * * + * This routine will take the given index ID and scan for available objects of that type * + * to recruit to the team. Recruiting will continue until that object type has either * + * been exhausted or if the team's requirment for that type has been filled. * + * * + * INPUT: typeindex -- The index for the object type to recruit. The index is used to * + * look into the type type's array of object types that make up this * + * team. * + * * + * OUTPUT: Returns with the number of objects added to this team. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + * 04/10/1995 JLB : Scans for units too. * + *=============================================================================================*/ +int TeamClass::Recruit(int typeindex) +{ + Validate(); + int added = 0; // Total number added to team. + + /* + ** Quick check to see if recruiting is really allowed for this index or not. + */ + if (Class->DesiredNum[typeindex] > Quantity[typeindex]) { + + /* + ** For infantry objects, sweep through the infantry in the game looking for + ** ones owned by the house that owns the team. When found, try to add. + */ + if (Class->Class[typeindex]->What_Am_I() == RTTI_INFANTRYTYPE) { + + for (int index = 0; index < Infantry.Count(); index++) { + InfantryClass * infantry = Infantry.Ptr(index); + + if (infantry->House == House && infantry->Class == Class->Class[typeindex]) { + if (Add(infantry, typeindex)) { + added++; + } + } + + /* + ** If there is sufficient quantity of this type of object recruited to the + ** team, then abort further scanning for members. + */ + if (Quantity[typeindex] >= Class->DesiredNum[typeindex]) { + break; + } + } + } + + if (Class->Class[typeindex]->What_Am_I() == RTTI_UNITTYPE) { + + for (int index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + + if (unit->House == House && unit->Class == Class->Class[typeindex]) { + if (Add(unit, typeindex)) { + added++; + + /* + ** If a transport is added to the team, the occupants + ** are added by default. + */ + FootClass * f = unit->Attached_Object(); + while (f) { + Add(f); + f = (FootClass *)f->Next; + } + } + } + + /* + ** If there is sufficient quantity of this type of object recruited to the + ** team, then abort further scanning for members. + */ + if (Quantity[typeindex] >= Class->DesiredNum[typeindex]) { + break; + } + } + } + } + + return(added); +} + + +/*********************************************************************************************** + * TeamClass::Detach -- Removes specified target from team tracking. * + * * + * When a target object is about to be removed from the game (e.g., it was killed), then * + * any team that is looking at that target must abort from that target. * + * * + * INPUT: target -- The target object that is going to be removed from the game. * + * * + * all -- Is the target going away for good as opposed to just cloaking/hiding? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Detach(TARGET target, bool ) +{ + Validate(); + + /* + ** If the target to detatch matches the target of this team, then remove + ** the target from this team's Tar/Nav com and let the chips fall + ** where they may. + */ + if (Target == target) { + Target = TARGET_NONE; + } + if (MissionTarget == target) { + MissionTarget = TARGET_NONE; + } + +} + + +/*********************************************************************************************** + * TeamClass::As_Target -- Converts this team object into a target number. * + * * + * This routine is used by the save/load code to produce a persistant identifier for this * + * team object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the team represented as a target number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/14/1995 JLB : Created. * + *=============================================================================================*/ +TARGET TeamClass::As_Target(void) const +{ + Validate(); + return(Build_Target(KIND_TEAM, Teams.ID(this))); +} + + +/*********************************************************************************************** + * TeamClass::Calc_Center -- Determines average location of team members. * + * * + * Use this routine to calculate the "center" location of the team. This is the average * + * position of all members of the team. Using this center value it is possible to tell * + * if a team member is too far away and where to head to in order to group up. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the cell number of the team's center point. If the team contains * + * no members, then the return value will be zero. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Calc_Center(CELL ¢er, CELL &obj_center) const +{ + Validate(); + long x = 0; + long y = 0; + int dist = 0x7FFFFFFF; + int quantity = 0; + FootClass * unit; + + obj_center = 0; + center = 0; + + unit = Member; + while (unit) { + if (unit->IsInitiated && !unit->IsInLimbo) { + CELL c = Coord_Cell(unit->Center_Coord()); + if (unit->Distance(Target) < dist) { + dist = unit->Distance(Target); + obj_center = c; + } + x += Cell_X(c); + y += Cell_Y(c); + quantity++; + } + unit = unit->Member; + } + if (quantity) { + x /= quantity; + y /= quantity; + CELL cell = XY_Cell((int)x, (int)y); + center = cell; + } +} + + +/*********************************************************************************************** + * TeamClass::Took_Damage -- Informs the team when the team member takes damage. * + * * + * This routine is used when a team member takes damage. Usually the team will react in * + * some fashion to the attack. This reaction can range from running away to assigning this * + * new target as the team's target. * + * * + * INPUT: obj -- The team member that was damaged. * + * * + * result -- The severity of the damage taken. * + * * + * source -- The purpetrator of the damage. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Took_Damage(FootClass * , ResultType result, TechnoClass * source) +{ + Validate(); + if ((result != RESULT_NONE) && (!Class->IsSuicide)) { + if (!IsMoving) { + // Should run to a better hiding place or disband into a group of hunting units. + } else { + + if (source && !Is_A_Member(source) && Member && Member->What_Am_I() != RTTI_AIRCRAFT) { + if (Target != source->As_Target()) { + + /* + ** Don't change target if the team's target is one that can fire as well. There is + ** no point in endlessly shuffling between targets that have firepower. + */ + if (Target_Legal(Target)) { + TechnoClass * techno = As_Techno(Target); + + if (techno && ((TechnoTypeClass const &)techno->Class_Of()).Primary != WEAPON_NONE) { + if (techno->In_Range(Cell_Coord(Center), 0)) { + return; + } + } + } + Target = source->As_Target(); + } + } + } + } +} + + +/*********************************************************************************************** + * TeamClass::Coordinate_Attack -- Handles coordinating a team attack. * + * * + * This function is called when the team knows what it should attack. This routine will * + * give the necessary orders to the members of the team. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/06/1995 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Coordinate_Attack(void) +{ + Validate(); + if (!Target_Legal(Target)) { + Target = MissionTarget; + } + + if (!Target_Legal(Target)) { + IsNextMission = true; + + } else { + + FootClass * unit = Member; + while (unit) { + + Coordinate_Conscript(unit); + + if (unit->IsInitiated && !unit->IsInLimbo) { + + if (unit->Mission != MISSION_ATTACK && unit->Mission != MISSION_ENTER && unit->Mission != MISSION_CAPTURE) { + unit->Assign_Mission(MISSION_ATTACK); + unit->Assign_Target(TARGET_NONE); + unit->Assign_Destination(TARGET_NONE); + } + + if (unit->TarCom != Target) { + unit->Assign_Target(Target); + } + } + + unit = unit->Member; + } + } +} + + +/*********************************************************************************************** + * TeamClass::Coordinate_Regroup -- Handles team idling (regrouping). * + * * + * This routine is called when the team must delay at its current location. Team members * + * are grouped together by this function. It is called when the team needs to sit and * + * wait. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/06/1995 JLB : Created. * + *=============================================================================================*/ +bool TeamClass::Coordinate_Regroup(void) +{ + Validate(); + FootClass * unit = Member; + bool retval = true; + + /* + ** Regroup default logic. + */ + while (unit) { + + Coordinate_Conscript(unit); + + if (unit->IsInitiated && !unit->IsInLimbo) { + + if (unit->Distance(Center) > STRAY_DISTANCE && (unit->Mission != MISSION_GUARD_AREA || !Target_Legal(unit->TarCom))) { + if (unit->Mission != MISSION_MOVE || !Target_Legal(unit->NavCom) || ::Distance(As_Cell(unit->NavCom), Center) > STRAY_DISTANCE) { + unit->Assign_Mission(MISSION_MOVE); + unit->Assign_Destination(::As_Target(Center)); + } + retval = false; + } else { + + /* + ** This unit has gotten close enough to the team center so that it is + ** now considered intiated. An initiated unit is considered when calculating + ** the center of the team. + */ + unit->IsInitiated = true; + + /* + ** The team is regrouping, so just sit here and wait. + */ + if (unit->Mission != MISSION_GUARD_AREA) { + unit->Assign_Mission(MISSION_GUARD); + unit->Assign_Destination(TARGET_NONE); + } + } + + } + + unit = unit->Member; + } + return(retval); +} + + +/*********************************************************************************************** + * TeamClass::Coordinate_Move -- Handles team movement coordination. * + * * + * This routine is called when the team must move to a new location. Movement and grouping * + * commands associated with this task are initiated here. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/06/1995 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Coordinate_Move(void) +{ + Validate(); + FootClass * unit = Member; + bool finished = true; + + if (!Target_Legal(Target)) { + Target = MissionTarget; + } + + if (Target_Legal(Target)) { + + if (!Lagging_Units()) { + + + while (unit) { + + Coordinate_Conscript(unit); + + if (unit->IsInitiated && !unit->IsInLimbo) { + + if (unit->What_Am_I() != RTTI_AIRCRAFT && unit->Distance(Center) > STRAY_DISTANCE) { + IsLagging = true; + finished = false; + } else { + + if ((unit->Distance(Target)/ICON_LEPTON_W) > STRAY_DISTANCE || + (unit->What_Am_I() == RTTI_AIRCRAFT && + ((AircraftClass *)unit)->Altitude > 0 && + Class->MissionList[CurrentMission+1].Mission != TMISSION_MOVE)) { + + if (unit->Mission != MISSION_MOVE) { + unit->Assign_Mission(MISSION_MOVE); + } + if (unit->NavCom != Target) { + unit->Assign_Destination(Target); + } + finished = false; + } else { + if (unit->Mission == MISSION_MOVE && !Target_Legal(unit->NavCom)) { + unit->Enter_Idle_Mode(); + } + } + } + } + + unit = unit->Member; + } + } else { + finished = false; + } + } + + /* + ** If all the team members are close enough to the desired destination, then + ** move to the next mission. + */ + if (finished && IsMoving) { + IsNextMission = true; + } +} + + +/*************************************************************************** + * Lagging_Units -- processes units that cant keep up with the pack * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 08/01/1995 PWG : Created. * + *=========================================================================*/ +bool TeamClass::Lagging_Units(void) +{ + Validate(); + FootClass * unit = Member; + bool lag = false; + + /* + ** If the IsLagging bit is not set, then obviously there are no lagging + ** units. + */ + if (!IsLagging) return(false); + + /* + ** Scan through all of the units, searching for units who are having + ** trouble keeping up with the pack. + */ + while (unit) { + + if (!unit->IsInLimbo) { + /* + ** If we find a unit who has fallen to far away from the center of + ** the pack, then we need to order that unit to catch up with the + ** first unit. + */ + if (unit->Distance(ObjectiveCenter) > STRAY_DISTANCE) { + if (unit->Mission != MISSION_MOVE || !Target_Legal(unit->NavCom) || ::Distance(As_Cell(unit->NavCom), ObjectiveCenter) > STRAY_DISTANCE) { + unit->Assign_Mission(MISSION_MOVE); + unit->Assign_Destination(::As_Target(ObjectiveCenter)); + } + lag = true; + } else { + /* + ** We need to order all of the other units to hold there + ** position until all lagging units catch up. + */ + unit->Assign_Mission(MISSION_GUARD); + unit->Assign_Destination(TARGET_NONE); + } + } + unit = unit->Member; + } + + /* + ** Once we have handled the loop we know whether there are any lagging + ** units or not. + */ + IsLagging = lag; + return(lag); +} + + +/*********************************************************************************************** + * TeamClass::Coordinate_Unload -- Tells the team to unload passengers now. * + * * + * This routine tells all transport vehicles to unload passengers now. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/14/1995 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Coordinate_Unload(void) +{ + Validate(); + FootClass * unit = Member; + bool finished = true; + + while (unit) { + + Coordinate_Conscript(unit); + + if (unit->IsInitiated && !unit->IsInLimbo) { + if (unit->Is_Something_Attached()) { + + /* + ** Loaner transports will break off of the team at this time. The normal + ** unload logic for the transport will proceed normally. The rest of the team + ** members will be in a dormant state until they are unloaded. + */ + if (unit->IsALoaner) { + Remove(unit); + unit->Commence(); + unit->Assign_Mission(MISSION_UNLOAD); + unit->Assign_Destination(Target); + } else { + if (unit->Mission != MISSION_UNLOAD) { + unit->Assign_Mission(MISSION_UNLOAD); + unit->Assign_Destination(Target); + } + } + finished = false; + } + } + + unit = unit->Member; + } + + if (finished) { + IsNextMission = true; + } +} + + +/*********************************************************************************************** + * TeamClass::Coordinate_Conscript -- Gives orders to new recruit. * + * * + * This routine will give the movement orders to the conscript so that it will group * + * with the other members of the team. * + * * + * INPUT: unit -- Pointer to the conscript unit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/06/1995 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Coordinate_Conscript(FootClass * unit) +{ + Validate(); + if (unit && !unit->IsInitiated && !unit->IsInLimbo) { + if (unit->Distance(Center) > STRAY_DISTANCE) { + if (!Target_Legal(unit->NavCom)) { + unit->Assign_Mission(MISSION_MOVE); + unit->Assign_Target(TARGET_NONE); + unit->Assign_Destination(::As_Target(Center)); + } + } else { + + /* + ** This unit has gotten close enough to the team center so that it is + ** now considered intiated. An initiated unit is considered when calculating + ** the center of the team. + */ + unit->IsInitiated = true; + } + } +} + + +/*************************************************************************** + * TeamClass::Is_A_Member -- Tests if a unit is a member of a team * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/16/1995 PWG : Created. * + *=========================================================================*/ +bool TeamClass::Is_A_Member(void const * who) const +{ + Validate(); + FootClass * unit = Member; + while (unit) { + if (unit == who) { + return(true); + } + unit = unit->Member; + } + return(false); +} + + +/*************************************************************************** + * TeamClass::Suspend_Teams -- Suspends activity for low priority teams * + * * + * INPUT: int priority - determines what is considered low priority. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/19/1995 PWG : Created. * + *=========================================================================*/ +void TeamClass::Suspend_Teams(int priority) +{ + for (int index = 0; index < Teams.Count(); index++) { + TeamClass *team = Teams.Ptr(index); + + /* + ** If a team is below the "survival priority level", then it gets + ** destroyed. The team members are then free to be reassigned. + */ + if (team && team->Class->RecruitPriority < priority) { + FootClass * unit = team->Member; + while (team->Member) { + team->Remove(team->Member); + } + team->IsAltered = true; + team->SuspendTimer = TICKS_PER_MINUTE*2; + team->Suspended = true; + } + } +} diff --git a/TEAM.H b/TEAM.H new file mode 100644 index 0000000..b8ffc5c --- /dev/null +++ b/TEAM.H @@ -0,0 +1,249 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\team.h_v 2.16 16 Oct 1995 16:48:04 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEAM.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/11/94 * + * * + * Last Update : December 11, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TEAM_H +#define TEAM_H + +#include +#include "teamtype.h" +#include "abstract.h" + +/* +** Units are only allowed to stray a certain distance away from their +** team. When they exceed this distance, some sort of fixup must be +** done. +*/ +#define STRAY_DISTANCE 2 + +class TeamClass : public AbstractClass +{ + public: + /* + ** This specifies the type of team this is. + */ + TeamTypeClass const * const Class; + + /* + ** This specifies the owner of this team. + */ + HouseClass * const House; + + /* + ** This flag forces the team into active state regardless of whether it + ** is understrength or not. + */ + unsigned IsForcedActive:1; + + /* + ** This flag is set to true when the team initiates into active mode. The + ** flag is never cleared. By examining this flag, it is possible to determine + ** if the team has ever launched into active mode. + */ + unsigned IsHasBeen:1; + + /* + ** If the team is full strength, then this flag is true. A full strength + ** team will not try to recruit members. + */ + unsigned IsFullStrength:1; + + /* + ** A team that is below half strength has this flag true. It means that the + ** the team should hide back at the owner's base and try to recruit + ** members. + */ + unsigned IsUnderStrength:1; + + /* + ** If a team is not understrength but is not yet full strength, then + ** the team is regrouping. If this flag is set and the team becomes + ** full strength, the all members of the team will become initiated + ** and this flag will be reset. + */ + unsigned IsReforming:1; + + /* + ** This bit should be set if a team is determined to have lagging + ** units in its formation. + */ + unsigned IsLagging:1; + + private: + /* + ** If a team member was removed or added, then this flag will be set to true. The + ** team system uses this flag to tell whether it should recalculate the team + ** under strength or full strength flags. This process does not need to occur + ** EVERY time a unit added or deleted from a team, just every so often if the + ** team has been changed. + */ + unsigned IsAltered:1; + + /* + ** If the team is working on it's primary mission (it is past the build up stage) + ** then this flag will be true. The transition between "moving" and "stationary" + ** stages usually requires some action on the team's part. + */ + unsigned IsMoving:1; + + /* + ** When the team determines that the next mission should be advanced to, it will + ** set this flag to true. Mission advance will either change the behavior of the + ** team or cause it to disband. + */ + unsigned IsNextMission:1; + /* + ** Records whether the team is suspended from production. + */ + unsigned Suspended:1; + + public: + /* + ** A team will have a center point. This is the point used to determine if + ** any member of the team is "too far" from the team and must return. This + ** center point is usually calculated as the average position of all the + ** team members. + */ + CELL Center; + CELL ObjectiveCenter; + + /* + ** This is the target of the team. Typically, it is a unit or structure, but + ** for the case of teams with a movement mission, it might represent a + ** destination cell. + */ + TARGET MissionTarget; + TARGET Target; + + /* + ** This is the total number of members in this team. + */ + int Total; + + /* + ** This is the teams combined risk value + */ + int Risk; + /* + ** This is the amount of time the team is suspended for. + */ + TCountDownTimerClass SuspendTimer; + + //------------------------------------------------------------ + TeamClass(void) : Class(0), House(0) {IsActive=false;Member=0;IsAltered=true;}; + TeamClass(TeamTypeClass const * team, HouseClass * owner); + virtual ~TeamClass(void); + virtual RTTIType What_Am_I(void) const {return RTTI_TEAM;}; + static void operator delete(void *ptr); + static void * operator new(size_t size); + static void Init(void); + static void Suspend_Teams(int priority); + + TARGET As_Target(void) const; + + /* + ** File I/O. + */ + bool Load(FileClass & file); + bool Save(FileClass & file); + void Code_Pointers(void); + void Decode_Pointers(void); + + void Force_Active(void) {IsForcedActive = true;IsUnderStrength=false;}; + bool Remove(FootClass *, int typeindex=-1); + void Detach(TARGET target, bool all); + void AI(void); + void Took_Damage(FootClass * obj, ResultType result, TechnoClass * source); + bool Add(FootClass *, int typeindex=-1); + void Assign_Mission_Target(TARGET new_target); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + /* + ** This is a record of the current number of active teams of each + ** type. It can range from zero to MaxAllowed. + */ + static unsigned char Number[TEAMTYPE_MAX]; + + private: + + /* + ** The current mission index into the mission list is recorded here. + */ + int CurrentMission; + + /* + ** Some missions will time out. This is the timer that keeps track of the + ** time to transition between missions. + */ + TCountDownTimerClass TimeOut; + + void Coordinate_Unload(void); + bool Coordinate_Regroup(void); + void Coordinate_Attack(void); + void Coordinate_Move(void); + void Coordinate_Conscript(FootClass * unit); +// void Control(FootClass *, bool initial=false); + void Calc_Center(CELL ¢er, CELL &obj_center) const; + int Recruit(int typeindex); + bool Is_A_Member(void const * who) const; + bool Lagging_Units(void); + + /* + ** Points to the first member in the list of members for this team. + */ + FootClass * Member; + + unsigned char Quantity[TeamTypeClass::MAX_TEAM_CLASSCOUNT]; + + /* + ** This records the success of each team type. As the team carries out its + ** mission, it increments this counter if it considers the mission + ** to have been successfully completed. Teams with greater success + ** will be created more than the others. + */ + static unsigned char Success[TEAMTYPE_MAX]; + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; +}; + +#endif diff --git a/TEAMTYPE.CPP b/TEAMTYPE.CPP new file mode 100644 index 0000000..39c50bf --- /dev/null +++ b/TEAMTYPE.CPP @@ -0,0 +1,978 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\teamtype.cpv 2.17 16 Oct 1995 16:48:52 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEAMTYPE.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 7, 1994 * + * * + * Last Update : July 21, 1995 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * TeamTypeClass::TeamTypeClass -- class constructor * + * TeamTypeClass::~TeamTypeClass -- class destructor * + * TeamTypeClass::Init -- pre-scenario initialization * + * TeamTypeClass::Read_INI -- reads INI data * + * TeamTypeClass::Write_INI -- writes INI data * + * TeamTypeClass::Read_Old_INI -- reads old INI format * + * TeamTypeClass::As_Pointer -- gets ptr for team type with given name * + * TeamTypeClass::Remove -- removes this team from the system * + * TeamTypeClass::Mission_From_Name -- returns mission for given name * + * TeamTypeClass::Name_From_Mission -- returns name for given mission * + * TeamTypeClass::operator new -- 'new' operator * + * TeamTypeClass::operator delete -- 'delete' operator * + * TeamTypeClass::Suggested_New_Team -- Suggests a new team to create. * + * TeamTypeClass::Validate -- validates teamtype pointer * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/* +********************************** Globals ********************************** +*/ +char const * TeamTypeClass::TMissions[TMISSION_COUNT] = { + "Attack Base", + "Attack Units", + "Attack Civil.", + "Rampage", + "Defend Base", +// "Harvest", + "Move", + "Move to Cell", + "Retreat", + "Guard", + "Loop", + "Attack Tarcom", + "Unload", +}; + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * TeamTypeClass::VTable; + + +/*********************************************************************************************** + * TeamTypeClass::Validate -- validates teamtype pointer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int TeamTypeClass::Validate(void) const +{ + int num; + + num = TeamTypes.ID(this); + if (num < 0 || num >= TEAMTYPE_MAX) { + Validate_Error("TEAMTYPE"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*************************************************************************** + * TeamTypeClass::TeamTypeClass -- class constructor * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + *=========================================================================*/ +TeamTypeClass::TeamTypeClass(void) +{ + IsPrebuilt = true; + IsReinforcable = true; + IsRoundAbout = false; + IsLearning = false; + IsSuicide = false; + IsAutocreate = false; + IsTransient = false; + IsMercenary = false; + RecruitPriority = 7; + MaxAllowed = 0; + Fear = 0; + InitNum = 0; + House = HOUSE_NONE; + MissionCount = 0; + IniName[0] = '\0'; + ClassCount = 0; + for (int i = 0; i < MAX_TEAM_CLASSCOUNT; i++) { + Class[i] = NULL; + DesiredNum[i] = 0; + } +} + + +/*************************************************************************** + * TeamTypeClass::Init -- pre-scenario initialization * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + *=========================================================================*/ +void TeamTypeClass::Init(void) +{ + TeamTypeClass *ptr; + + TeamTypes.Free_All(); + + ptr = new TeamTypeClass(); + VTable = ((void **)(((char *)ptr) + sizeof(AbstractTypeClass) - 4))[0]; + delete ptr; +} + + +/*************************************************************************** + * TeamTypeClass::Read_INI -- reads INI data * + * * + * INI entry format: * + * TeamName = Housename,Roundabout,Learning,Suicide,Spy,Mercenary, * + * RecruitPriority,MaxAllowed,InitNum,Fear, * + * ClassCount,Class:Num,Class:Num,..., * + * MissionCount,Mission:Arg,Mission:Arg,Mission:Arg,... * + * * + * INPUT: * + * buffer buffer to hold the INI data * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + * 02/01/1995 BR : No del team if no classes (editor needs empty teams!) * + *=========================================================================*/ +void TeamTypeClass::Read_INI(char *buffer) +{ + TeamTypeClass * team; // Working team pointer. + char *tbuffer; // Accumulation buffer of team names. + int len; // Length of data in buffer. + char buf[500]; // INI entry buffer + + /*------------------------------------------------------------------------ + Set 'tbuffer' to point just past the INI buffer + ------------------------------------------------------------------------*/ + len = strlen(buffer) + 2; + tbuffer = buffer + len; + + /*------------------------------------------------------------------------ + Read all TeamType entry names into 'tbuffer' + ------------------------------------------------------------------------*/ + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); + + /* + ----------------------- Loop for all team entries ------------------------ + */ + while (*tbuffer != '\0') { + /* + ....................... Create a new team type ........................ + */ + team = new TeamTypeClass(); + + /* + ......................... Get the team entry .......................... + */ + WWGetPrivateProfileString(INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, buffer); + + /* + .......................... Fill the team in ........................... + */ + team->Fill_In(tbuffer, buf); + + /* + ...................... Go to the next INI entry ....................... + */ + tbuffer += strlen(tbuffer)+1; + } + + /* + ** If no teams were read in, try reading the old INI format. + */ + if (TeamTypes.Count()==0) { + Read_Old_INI(buffer); + } +} + + +/*********************************************************************************************** + * TeamTypeClass::Fill_In -- fills in trigger from the given INI entry * + * * + * This routine fills in the given teamtype with the given name, and values from * + * the given INI entry. * + * * + * (This routine is used by the scenario editor, to import teams from the MASTER.INI file.) * + * * + * INI entry format: * + * TeamName = Housename,Roundabout,Learning,Suicide,Spy,Mercenary, * + * RecruitPriority,MaxAllowed,InitNum,Fear, * + * ClassCount,Class:Num,Class:Num,..., * + * MissionCount,Mission:Arg,Mission:Arg,Mission:Arg,... * + * * + * INPUT: * + * name mnemonic for the desired trigger * + * entry INI entry to parse * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +void TeamTypeClass::Fill_In(char * name, char *entry) +{ + Validate(); + int num_classes; + char *p1; // parsing pointer + char *p2; // parsing pointer + int i; // loop counter + TechnoTypeClass const *otype; // ptr to type of object + InfantryType i_id; // infantry ID + UnitType u_id; // unit ID + AircraftType a_id; // aircraft ID + TeamMissionStruct mission; + + /* + ------------------------------ Set its name ------------------------------ + */ + Set_Name(name); + + /* + ---------------------------- 1st token: House ---------------------------- + */ + House = HouseTypeClass::From_Name(strtok(entry,",")); + + /* + -------------------------- 2nd token: RoundAbout ------------------------- + */ + IsRoundAbout = atoi(strtok(NULL,",")); + + /* + --------------------------- 3rd token: Learning -------------------------- + */ + IsLearning = atoi(strtok(NULL,",")); + + /* + --------------------------- 4th token: Suicide --------------------------- + */ + IsSuicide = atoi(strtok(NULL,",")); + + /* + ----------------------------- 5th token: Spy ----------------------------- + */ + IsAutocreate = atoi(strtok(NULL,",")); + + /* + -------------------------- 6th token: Mercenary -------------------------- + */ + IsMercenary = atoi(strtok(NULL,",")); + + /* + ----------------------- 7th token: RecruitPriority ----------------------- + */ + RecruitPriority = atoi(strtok(NULL,",")); + + /* + -------------------------- 8th token: MaxAllowed ------------------------- + */ + MaxAllowed = atoi(strtok(NULL,",")); + + /* + --------------------------- 9th token: InitNum --------------------------- + */ + InitNum = atoi(strtok(NULL,",")); + + /* + ------------------------- 10th token: Fear level ------------------------- + */ + Fear = atoi(strtok(NULL,",")); + + /* + ------------------------ 11th token: Class count ------------------------- + */ + num_classes = atoi(strtok(NULL,",")); + + /* + -------------- Loop through entries, setting class ptr & num ------------- + */ + ClassCount = 0; + for (i = 0; i < num_classes; i++) { + p1 = strtok(NULL,",:"); + p2 = strtok(NULL,",:"); + otype = NULL; + + /* + ------------------- See if this is an infantry name ------------------- + */ + i_id = InfantryTypeClass::From_Name(p1); + if (i_id != INFANTRY_NONE) { + otype = &InfantryTypeClass::As_Reference(i_id); + } + + /* + ---------------------- See if this is a unit name --------------------- + */ + u_id = UnitTypeClass::From_Name(p1); + if (u_id != UNIT_NONE) { + otype = &UnitTypeClass::As_Reference(u_id); + } + + /* + ------------------- See if this is an aircraft name ------------------- + */ + a_id = AircraftTypeClass::From_Name(p1); + if (a_id != AIRCRAFT_NONE) { + otype = &AircraftTypeClass::As_Reference(a_id); + } + + /* + --------------- If the name was resolved, add this class -------------- + */ + if (otype) { + Class[ClassCount] = otype; + DesiredNum[ClassCount] = atoi(p2); + ClassCount++; + } + } + + /* + ----------------------- next token: Mission count ------------------------ + */ + MissionCount = atoi(strtok(NULL,",")); + + for (i = 0; i < MissionCount; i++) { + p1 = strtok(NULL,",:"); + p2 = strtok(NULL,",:"); + mission.Mission = Mission_From_Name(p1); + mission.Argument = atoi(p2); + MissionList[i] = mission; + } + + char * ptr = strtok(NULL, ","); + if (ptr) { + IsReinforcable = atoi(ptr); + } + ptr = strtok(NULL, ","); + if (ptr) { + IsPrebuilt = atoi(ptr); + } +} + + +/*************************************************************************** + * TeamTypeClass::Write_INI -- writes INI data * + * * + * INI entry format: * + * TeamName = Housename,Roundabout,Learning,Suicide,Spy,Mercenary, * + * RecruitPriority,MaxAllowed,InitNum,Fear, * + * ClassCount,Class:Num,Class:Num,..., * + * MissionCount,Mission,Arg,Mission,Arg,Mission,Arg,... * + * * + * INPUT: * + * buffer buffer to store INI data in * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + *=========================================================================*/ +void TeamTypeClass::Write_INI(char *buffer, bool refresh) +{ + int index; + int i; + char buf[500]; + TeamTypeClass * team; + char const *hname; + + /*------------------------------------------------------------------------ + First, clear out all existing teamtypes in the old-style format. + ------------------------------------------------------------------------*/ + WWWritePrivateProfileString("Teams", NULL, NULL, buffer); + + /*------------------------------------------------------------------------ + Clear out all existing teamtype data from the INI file. + ------------------------------------------------------------------------*/ + if (refresh) { + WWWritePrivateProfileString(INI_Name(), NULL, NULL, buffer); + } + + /*------------------------------------------------------------------------ + Now write all the team data out + ------------------------------------------------------------------------*/ + buf[0] = 0; + for (index = 0; index < TeamTypes.Count(); index++) { + /* + .................. Get ptr to next active teamtype .................... + */ + team = TeamTypes.Ptr(index); + + /* + .......................... Find house's name .......................... + */ + if (team->House==HOUSE_NONE) { + hname = "None"; + } else { + hname = HouseClass::As_Pointer(team->House)->Class->IniName; + } + + /* + ......................... Generate INI entry .......................... + */ + sprintf(buf,"%s,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + hname, + team->IsRoundAbout, + team->IsLearning, + team->IsSuicide, + team->IsAutocreate, + team->IsMercenary, + team->RecruitPriority, + team->MaxAllowed, + team->InitNum, + team->Fear, + team->ClassCount); + + /*..................................................................... + For every class in the team, record the class's name & desired count + .....................................................................*/ + for (i = 0; i < team->ClassCount; i++) { + sprintf (buf + strlen(buf), ",%s:%d", + team->Class[i]->IniName, + team->DesiredNum[i]); + } + + /*..................................................................... + Record the # of missions, and each mission name & argument value. + .....................................................................*/ + sprintf(buf + strlen(buf),",%d",team->MissionCount); + for (i = 0; i < team->MissionCount; i++) { + sprintf (buf + strlen(buf), ",%s:%d", + Name_From_Mission(team->MissionList[i].Mission), + team->MissionList[i].Argument); + } + + if (team->IsReinforcable) { + strcat(buf, ",1"); + } else { + strcat(buf, ",0"); + } + if (team->IsPrebuilt) { + strcat(buf, ",1"); + } else { + strcat(buf, ",0"); + } + + WWWritePrivateProfileString(INI_Name(), team->IniName, buf, buffer); + } +} + + +/*************************************************************************** + * TeamTypeClass::Read_Old_INI -- reads old INI format * + * * + * INI entry format: * + * TeamName = Housename,Roundabout,Learning,Suicide,Spy,Mercenary, * + * RecruitPriority,MaxAllowed,InitNum,Class:Num,Class:Num,...,Fear * + * * + * INPUT: * + * buffer buffer to hold the INI data * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + * 02/01/1995 BR : No del team if no classes (editor needs empty teams!) * + *=========================================================================*/ +void TeamTypeClass::Read_Old_INI(char *buffer) +{ + TeamTypeClass * team; // Working team pointer. + char *tbuffer; // Accumulation buffer of team names. + int len; // Length of data in buffer. + char buf[256]; // INI entry buffer + char *p1; // parsing pointer + char *p2; // parsing pointer + int index; + TechnoTypeClass const *otype; // ptr to type of object + InfantryType i_id; // infantry ID + UnitType u_id; // unit ID + AircraftType a_id; // infantry ID + + /*------------------------------------------------------------------------ + Set 'tbuffer' to point just past the INI buffer + ------------------------------------------------------------------------*/ + len = strlen(buffer) + 2; + tbuffer = buffer + len; + + /*------------------------------------------------------------------------ + Read all TeamType entry names into 'tbuffer' + ------------------------------------------------------------------------*/ + WWGetPrivateProfileString("Teams", NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); + + /* + ----------------------- Loop for all team entries ------------------------ + */ + while (*tbuffer != '\0') { + /* + ........................ Create a new trigger ......................... + */ + team = new TeamTypeClass(); + + /* + ............................ Set its name ............................. + */ + team->Set_Name(tbuffer); + + /* + ......................... Get the team entry .......................... + */ + WWGetPrivateProfileString("Teams", tbuffer, NULL, buf, sizeof(buf)-1, buffer); + + /* + .......................... 1st token: House ........................... + */ + team->House = HouseTypeClass::From_Name(strtok(buf,",")); + + /* + ........................ 2nd token: RoundAbout ........................ + */ + team->IsRoundAbout = atoi(strtok(NULL,",")); + + /* + ......................... 3rd token: Learning ......................... + */ + team->IsLearning = atoi(strtok(NULL,",")); + + /* + ......................... 4th token: Suicide .......................... + */ + team->IsSuicide = atoi(strtok(NULL,",")); + + /* + ........................... 5th token: Spy ............................ + */ + team->IsAutocreate = atoi(strtok(NULL,",")); + + /* + ........................ 6th token: Mercenary ......................... + */ + team->IsMercenary = atoi(strtok(NULL,",")); + + /* + ..................... 7th token: RecruitPriority ...................... + */ + team->RecruitPriority = atoi(strtok(NULL,",")); + + /* + ........................ 8th token: MaxAllowed ........................ + */ + team->MaxAllowed = atoi(strtok(NULL,",")); + + /* + ......................... 9th token: InitNum .......................... + */ + team->InitNum = atoi(strtok(NULL,",")); + + /* + ....................... 10th token: Mission name ...................... + */ + strtok(NULL,","); // just throw it away + + /* + ............ Loop through entries, setting class ptr & num ............ + */ + index = 0; + p1 = strtok(NULL,",:"); + p2 = strtok(NULL,",:"); + while (p1 && p2) { + otype = NULL; + + /* + ................. See if this is an infantry name .................. + */ + i_id = InfantryTypeClass::From_Name(p1); + if (i_id!=INFANTRY_NONE) { + otype = &InfantryTypeClass::As_Reference(i_id); + } + + /* + .................... See if this is a unit name .................... + */ + u_id = UnitTypeClass::From_Name(p1); + if (u_id != UNIT_NONE) { + otype = &UnitTypeClass::As_Reference(u_id); + } + + /* + ................. See if this is an aircraft name .................. + */ + a_id = AircraftTypeClass::From_Name(p1); + if (a_id != AIRCRAFT_NONE) { + otype = &AircraftTypeClass::As_Reference(a_id); + } + + /* + ............. If the name was resolved, add this class ............. + */ + if (otype) { + team->Class[index] = otype; + team->DesiredNum[index] = atoi(p2); + index++; + team->ClassCount = index; + } + + /* + ................. Go to the next entry on the line ................. + */ + p1 = strtok(NULL,",:"); + p2 = strtok(NULL,",:"); + } + + team->Fear = 0; + + /* + ...................... Go to the next INI entry ....................... + */ + tbuffer += strlen(tbuffer)+1; + } +} + + +/*************************************************************************** + * TeamTypeClass::As_Pointer -- gets ptr for team type with given name * + * * + * INPUT: * + * name name of teamtype * + * * + * OUTPUT: * + * ptr to TeamType with that name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + *=========================================================================*/ +TeamTypeClass * TeamTypeClass::As_Pointer(char *name) +{ + int i; + + if (name == NULL) { + return(NULL); + } + + for (i = 0; i < TeamTypes.Count(); i++) { + if (!stricmp(name, TeamTypes.Ptr(i)->IniName)) { + return(TeamTypes.Ptr(i)); + } + } + + return(NULL); +} + + +/*************************************************************************** + * TeamTypeClass::Remove -- removes this team from the system * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/09/1994 BR : Created. * + *=========================================================================*/ +void TeamTypeClass::Remove(void) +{ + Validate(); + int i; + TriggerClass * trigger; + + /* + ** Remove all trigger references to this team. + */ + for (i = 0; i < Triggers.Count(); i++) { + trigger = Triggers.Ptr(i); + if (trigger->Team == this) { + trigger->Team = NULL; + } + } + + /* + ** Delete myself. + */ + delete this; +} + + +/*************************************************************************** + * TeamTypeClass::Mission_From_Name -- returns team mission for given name * + * * + * INPUT: * + * name name to compare * + * * + * OUTPUT: * + * mission for that name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/13/1994 BR : Created. * + *=========================================================================*/ +TeamMissionType TeamTypeClass::Mission_From_Name(char const *name) +{ + int order; + + if (name) { + for (order = TMISSION_FIRST; order < TMISSION_COUNT; order++) { + if (stricmp(TMissions[order], name) == 0) { + return((TeamMissionType) order); + } + } + } + + return(TMISSION_NONE); +} + + +/*************************************************************************** + * TeamTypeClass::Name_From_Mission -- returns name for given mission * + * * + * INPUT: * + * order mission to get name for * + * * + * OUTPUT: * + * name of mission * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/13/1994 BR : Created. * + *=========================================================================*/ +char const * TeamTypeClass::Name_From_Mission(TeamMissionType order) +{ + if (order <= TMISSION_NONE || order >= TMISSION_COUNT) { + return("None"); + } else { + return(TMissions[order]); + } +} + + +/*************************************************************************** + * TeamTypeClass::operator new -- 'new' operator * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * pointer to new TeamType * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=========================================================================*/ +void * TeamTypeClass::operator new(size_t ) +{ + void * ptr = TeamTypes.Allocate(); + if (ptr) { + ((TeamTypeClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*************************************************************************** + * TeamTypeClass::operator delete -- 'delete' operator * + * * + * INPUT: * + * ptr pointer to delete * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=========================================================================*/ +void TeamTypeClass::operator delete(void *ptr) +{ + if (ptr) { + ((TeamTypeClass *)ptr)->IsActive = false; + } + TeamTypes.Free((TeamTypeClass *)ptr); +} + + + +TeamClass * TeamTypeClass::Create_One_Of(void) const +{ + if (ScenarioInit || TeamClass::Number[TeamTypes.ID(this)] < MaxAllowed) { + return(new TeamClass(this, HouseClass::As_Pointer(House))); + } + return(NULL); +} + + +TARGET TeamTypeClass::As_Target(void) const +{ + Validate(); + return(Build_Target(KIND_TEAMTYPE, TeamTypes.ID(this))); +} + + +void TeamTypeClass::Destroy_All_Of(void) const +{ + for (int index = 0; index < Teams.Count(); index++) { + TeamClass * team = Teams.Ptr(index); + + if (team->Class == this) { + delete team; + index--; + } + } +} + + +/*********************************************************************************************** + * TeamTypeClass::Suggested_New_Team -- Suggests a new team to create. * + * * + * This routine will scan through the team types available and create teams of the * + * type that can best utilize the existing unit mix. * + * * + * INPUT: house -- Pointer to the house that this team is to be created for. * + * * + * utype -- A bit mask of the unit types available for this house. * + * * + * itypes -- A bit mask of the infantry types available for this house. * + * * + * alerted -- Is this house alerted? If true, then the Autocreate teams will be * + * considered in the selection process. * + * * + * OUTPUT: Returns with a pointer to the team type that should be created. If no team should * + * be created, then it returns NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/13/1995 JLB : Created. * + * 07/21/1995 JLB : Will autocreate team even if no members in field. * + *=============================================================================================*/ +TeamTypeClass const * TeamTypeClass::Suggested_New_Team(HouseClass * house, long utypes, long itypes, bool alerted) +{ + TeamTypeClass const * best = NULL; + int bestvalue = 0; + + for (int index = 0; index < TeamTypes.Count(); index++) { + TeamTypeClass const * ttype = TeamTypes.Ptr(index); + + if (ttype && + ttype->House == house->Class->House && + TeamClass::Number[index] < ((alerted || !ttype->IsAutocreate) ? ttype->MaxAllowed : 0)) { + + /* + ** Determine what kind of units this team requires. + */ + long uneeded = 0; + long ineeded = 0; + for (int ctype = 0; ctype < ttype->ClassCount; ctype++) { + switch (ttype->Class[ctype]->What_Am_I()) { + case RTTI_INFANTRYTYPE: + ineeded |= (1 << ((InfantryTypeClass *)ttype->Class[ctype])->Type); + break; + + case RTTI_UNITTYPE: + uneeded |= (1 << ((UnitTypeClass *)ttype->Class[ctype])->Type); + break; + } + } + + /* + ** If this team can use the types required, then consider it a possible + ** team type to create. + */ + int value = 0; + if ((ineeded & itypes) || (uneeded & utypes)) { + value = ttype->RecruitPriority; + } else { + value = ttype->RecruitPriority/2; + } + + if (!best || bestvalue < value) { + bestvalue = value; + best = ttype; + } + } + } + + return(best); +} diff --git a/TEAMTYPE.H b/TEAMTYPE.H new file mode 100644 index 0000000..da2569b --- /dev/null +++ b/TEAMTYPE.H @@ -0,0 +1,259 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\teamtype.h_v 2.18 16 Oct 1995 16:45:40 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEAMTYPE.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 7, 1994 * + * * + * Last Update : December 7, 1994 [BR] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TEAMTYPE_H +#define TEAMTYPE_H + +/* +********************************** Defines ********************************** +*/ +//#define TEAMTYPE_MAX 20 // max # of different team types + +/* +** TeamMissionType: the various missions that a team can have. +*/ +typedef enum TeamMissionType { + TMISSION_NONE=-1, + TMISSION_ATTACKBASE, // Attack nearest enemy base. + TMISSION_ATTACKUNITS, // Attack all enemy units. + TMISSION_ATTACKCIVILIANS, // Attack all civilians + TMISSION_RAMPAGE, // attack & destroy anything that's not mine + TMISSION_DEFENDBASE, // Protect my base. +// TMISSION_HARVEST, // stake out a Tiberium claim, defend & harvest it + TMISSION_MOVE, // moves to waypoint specified. + TMISSION_MOVECELL, // moves to cell # specified. + TMISSION_RETREAT, // order given by superior team, for coordinating + TMISSION_GUARD, // works like an infantry's guard mission + TMISSION_LOOP, // loop back to start of mission list + TMISSION_ATTACKTARCOM, // attack tarcom + TMISSION_UNLOAD, // Unload at current location. + TMISSION_COUNT, + TMISSION_FIRST=0 +} TeamMissionType; + +/* +** Forward declarations. +*/ +class TechnoTypeClass; + + +/* +** This structure contains one team mission value & its argument. +*/ +typedef struct TeamMissionTag +{ + TeamMissionType Mission; + int Argument; +} TeamMissionStruct; + +/* +** TeamTypeClass declaration +*/ +class TeamTypeClass : public AbstractTypeClass +{ + public: + enum TeamTypeClassEnums { + MAX_TEAM_CLASSCOUNT=5, + MAX_TEAM_MISSIONS=20 + }; + + /* + ** Constructor/Destructor + */ + TeamTypeClass(void); + virtual ~TeamTypeClass(void) {}; + + /* + ** Initialization: clears all team types in preparation for new scenario + */ + static void Init(void); + + /* + ** File I/O routines + */ + static void Read_INI(char *buffer); + void Fill_In(char *name, char *entry); + static void Write_INI(char *buffer, bool refresh); + static void Read_Old_INI(char *buffer); + static char * INI_Name(void) {return "TeamTypes";}; + bool Load(FileClass & file); + bool Save(FileClass & file); + void Code_Pointers(void); + void Decode_Pointers(void); + + /* + ** As_Pointer gets a pointer to the trigger object give its name + */ + static TeamTypeClass *As_Pointer(char *name); + + /* + ** Processing routines + */ + void Remove(void); + TeamClass * Create_One_Of(void) const; + void Destroy_All_Of(void) const; + + /* + ** Utility routines + */ + static char const * Name_From_Mission(TeamMissionType order); + static TeamMissionType Mission_From_Name(char const *name); + static TeamTypeClass const * Suggested_New_Team(HouseClass * house, long utypes, long itypes, bool alerted); + + TARGET As_Target(void) const; + + /* + ** Overloaded operators + */ + void * operator new(size_t ); + void operator delete(void *ptr); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + /* + ** If this teamtype object is active, then this flag will be true. + ** TeamType objects that are not active are either not yet created or have + ** been deleted after fulfilling their action. + */ + unsigned IsActive:1; + + /* + ** If RoundAbout, the team avoids high-threat areas + */ + unsigned IsRoundAbout:1; + + /* + ** If Learning, the team learns from mistakes + */ + unsigned IsLearning:1; + + /* + ** If Suicide, the team won't stop until it achieves its mission or it's + ** dead + */ + unsigned IsSuicide:1; + + /* + ** Is this team type allowed to be created automatically by the computer + ** when the appropriate trigger indicates? + */ + unsigned IsAutocreate:1; + + /* + ** Mercenaries will change sides if they start to lose. + */ + unsigned IsMercenary:1; + + /* + ** This flag tells the computer that it should build members to fill + ** a team of this type regardless of whether there actually is a team + ** of this type active. + */ + unsigned IsPrebuilt:1; + + /* + ** If this team should allow recruitment of new members, then this flag + ** will be true. A false value results in a team that fights until it + ** is dead. This is similar to IsSuicide, but they will defend themselves. + */ + unsigned IsReinforcable:1; + + /* + ** A transient team type was created exclusively to bring on reinforcements + ** as a result of some special event. As soon as there are no teams + ** existing of this type, then this team type should be deleted. + */ + unsigned IsTransient:1; + + /* + ** Priority given the team for recruiting purposes; higher priority means + ** it can steal members from other teams (scale: 0 - 15) + */ + int RecruitPriority; + + /* + ** Initial # of this type of team + */ + unsigned char InitNum; + + /* + ** Max # of this type of team allowed at one time + */ + unsigned char MaxAllowed; + + /* + ** Fear level of this team + */ + unsigned char Fear; + + /* + ** House the team belongs to + */ + HousesType House; + + /* + ** The mission list for this team + */ + int MissionCount; + TeamMissionStruct MissionList[MAX_TEAM_MISSIONS]; + + /* + ** Number of different classes in the team + */ + unsigned char ClassCount; + + /* + ** Array of object types comprising the team + */ + TechnoTypeClass const * Class[MAX_TEAM_CLASSCOUNT]; + + /* + ** Desired # of each type of object comprising the team + */ + unsigned char DesiredNum[MAX_TEAM_CLASSCOUNT]; + + private: + static char const * TMissions[TMISSION_COUNT]; + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; +}; + +#endif diff --git a/TECHNO.CPP b/TECHNO.CPP new file mode 100644 index 0000000..96f5443 --- /dev/null +++ b/TECHNO.CPP @@ -0,0 +1,3996 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\techno.cpv 2.13 02 Aug 1995 17:01:08 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BASE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 8, 1994 * + * * + * Last Update : August 23, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TechnoClass::AI -- Handles AI processing for techno object. * + * TechnoClass::Assign_Destination -- Assigns movement destination to the object. * + * TechnoClass::Assign_Target -- Assigns the targeting computer with specified target. * + * TechnoClass::Base_Is_Attacked -- Handle panic response to base being attacked. * + * TechnoClass::Can_Fire -- Determines if this techno object can fire. * + * TechnoClass::Can_Player_Fire -- Determines if the player can give this object a fire order* + * TechnoClass::Can_Player_Move -- Determines if the object can move be moved by player. * + * TechnoClass::Can_Repair -- Determines if the object can and should be repaired. * + * TechnoClass::Captured -- Handles capturing this object. * + * TechnoClass::Clicked_As_Target -- Sets the flash count for this techno object. * + * TechnoClass::Crew_Type -- Fetches the kind of crew this object contains. * + * TechnoClass::Debug_Dump -- Displays the base class data to the monochrome screen. * + * TechnoClass::Desired_Load_Dir -- Fetches loading parameters for this object. * + * TechnoClass::Detach -- Handles removal of target from tracking system. * + * TechnoClass::Do_Cloak -- Start the object into cloaking stage. * + * TechnoClass::Do_Shimmer -- Causes this object to shimmer if it is cloaked. * + * TechnoClass::Do_Uncloak -- Cause the stealth tank to uncloak. * + * TechnoClass::Draw_It -- Draws the health bar (if necessary). * + * TechnoClass::Draw_Pips -- Draws the transport pips and other techno graphics. * + * TechnoClass::Enter_Idle_Mode -- Object enters its default idle condition. * + * TechnoClass::Evaluate_Cell -- Determine the value and object of specified cell. * + * TechnoClass::Evaluate_Object -- Determines score value of specified object. * + * TechnoClass::Exit_Object -- Causes specified object to leave this object. * + * TechnoClass::Find_Docking_Bay -- Searches for a close docking bay. * + * TechnoClass::Find_Exit_Cell -- Finds an appropriate exit cell for this object. * + * TechnoClass::Fire_At -- Fires projectile at target specified. * + * TechnoClass::Fire_Direction -- Fetches the direction projectile fire will take. * + * TechnoClass::Get_Ownable -- Fetches the ownable bits for this object. * + * TechnoClass::Greatest_Threat -- Determines best target given search criteria. * + * TechnoClass::Hidden -- Returns the object back into the hidden state. * + * TechnoClass::In_Range -- Determines if specified target is within weapon range. * + * TechnoClass::In_Range -- Determines if specified target is within weapon range. * + * TechnoClass::In_Range -- Determines if the specified coordinate is within range. * + * TechnoClass::Is_Techno -- Confirms that this is a TechnoClass object. * + * TechnoClass::Is_Weapon_Equipped -- Determines if this object has a combat weapon. * + * TechnoClass::Kill_Cargo -- Destroys any cargo attached to this object. * + * TechnoClass::Mark -- Handles marking of techno objects. * + * TechnoClass::Nearby_Location -- Radiates outward looking for clear cell nearby. * + * TechnoClass::Owner -- Who is the owner of this object? * + * TechnoClass::Per_Cell_Process -- Handles once-per-cell operations for techno type objects.* + * TechnoClass::Pip_Count -- Fetches the number of pips to display on this object. * + * TechnoClass::Player_Assign_Mission -- Assigns a mission as result of player input. * + * TechnoClass::Random_Animate -- Performs some idle animation for the object. * + * TechnoClass::Rearm_Delay -- Calculates the delay before firing can occur. * + * TechnoClass::Receive_Message -- Handles inbound message as appropriate. * + * TechnoClass::Record_The_Kill -- Records the death of this object. * + * TechnoClass::Remap_Table -- Fetches the appropriate remap table to use. * + * TechnoClass::Response_Attack -- Handles the voice response when given attack order. * + * TechnoClass::Response_Move -- Handles the voice repsonse to a movement request. * + * TechnoClass::Response_Select -- Handles the voice response when selected. * + * TechnoClass::Revealed -- Handles revealing an object to the house specified. * + * TechnoClass::Risk -- Fetches the risk associated with this object. * + * TechnoClass::Scatter -- Causes the object to scatter to an adjacent cell. * + * TechnoClass::Select -- Selects object and checks to see if can be selected. * + * TechnoClass::Set_Mission -- Forced mission set (used by editor). * + * TechnoClass::Stun -- Prepares the object for removal from the game. * + * TechnoClass::Take_Damage -- Records damage assessed to this object. * + * TechnoClass::Target_Something_Nearby -- Handles finding and assigning a nearby target. * + * TechnoClass::TechnoClass -- Constructor for techno type objects. * + * TechnoClass::TechnoClass -- Default constructor for techno objects. * + * TechnoClass::Techno_Draw_Object -- General purpose draw object routine. * + * TechnoClass::Threat_Range -- Returns the range to scan based on threat control. * + * TechnoClass::Tiberium_Load -- Fetches the current tiberium load percentage. * + * TechnoClass::Unlimbo -- Performs unlimbo process for all techno type objects. * + * TechnoClass::Value -- Fetches the target value for this object. * + * TechnoClass::Visual_Character -- Determine the visual character of the object. * + * TechnoClass::Weapon_Range -- Determines the maximum range for the weapon. * + * TechnoClass::What_Action -- Determines action to perform if cell is clicked on. * + * TechnoClass::What_Action -- Determines what action to perform if object is selected. * + * TechnoTypeClass::Cost_Of -- Fetches the cost of this object type. * + * TechnoTypeClass::Get_Cameo_Data -- Fetches the cameo image for this object type. * + * TechnoTypeClass::Get_Ownable -- Fetches the ownable bits for this object type. * + * TechnoTypeClass::Max_Passengers -- Fetches the maximum passengers allowed. * + * TechnoTypeClass::Repair_Cost -- Fetches the cost to repair one step. * + * TechnoTypeClass::Repair_Step -- Fetches the health to repair one step. * + * TechnoTypeClass::TechnoTypeClass -- Constructor for techno type objects. * + * TechnoTypeClass::Time_To_Build -- Fetches the time to build this object. * + * TechnoTypeClass::Raw_Cost -- Fetches the raw (base) cost of the object. * + * TechnoClass::Refund_Amount -- Returns with the money to refund if this object is sold. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** +** Cloaking control values. +*/ +#define MAX_UNCLOAK_STAGE 38 +#define UNCLOAK_VIS_TIME (1*TICKS_PER_SECOND) + + +/*************************************************************************** +** Which shape to use depending on which facing is controlled by these arrays. +*/ +int const TechnoClass::BodyShape[32] = {0,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1}; + + +/*********************************************************************************************** + * TechnoTypeClass::TechnoTypeClass -- Constructor for techno type objects. * + * * + * This is the normal constructor for techno type objects. It is called in the process of * + * constructing all the object type (constant) data for the various techno type objects. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +TechnoTypeClass::TechnoTypeClass( + int name, + char const *ininame, + unsigned char level, + long pre, + bool is_leader, + bool is_scanner, + bool is_nominal, + bool is_transporter, + bool is_flammable, + bool is_crushable, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_theater, + bool is_twoshooter, + bool is_turret_equipped, + bool is_repairable, + bool is_buildable, + bool is_crew, + int ammo, + unsigned short strength, + MPHType maxspeed, + int sightrange, + int cost, + int scenario, + int risk, + int reward, + int ownable, + WeaponType primary, + WeaponType secondary, + ArmorType armor) : + ObjectTypeClass( true, + is_flammable, + is_crushable, + is_stealthy, + is_selectable, + is_legal_target, + is_insignificant, + is_immune, + name, + ininame, + armor, + strength) +{ + Level = level; + Pre = pre; + MaxAmmo = ammo; + MaxSpeed = maxspeed; + CameoData = NULL; + Primary = primary, + Secondary = secondary, + Cost = cost; + IsLeader = is_leader; + IsScanner = is_scanner; + IsTransporter = is_transporter; + IsTwoShooter = is_twoshooter; + IsBuildable = is_buildable; + IsCrew = is_crew; + IsTheater = is_theater; + IsRepairable = is_repairable; + IsTurretEquipped= is_turret_equipped; + IsNominal = is_nominal; + Ownable = ownable; + Reward = reward; + Scenario = scenario; + SightRange = sightrange; + + /* + ** Units risk value is based on the type of weapon he has and the + ** rate of fire it shoots at. + */ + risk = risk; + Risk = 0; + if (primary != WEAPON_NONE) { + Risk = (Weapons[primary].Attack * (Weapons[primary].Range >> 4)) / Weapons[primary].ROF; + } +} + + +/*********************************************************************************************** + * TechnoTypeClass::Raw_Cost -- Fetches the raw (base) cost of the object. * + * * + * This routine is used to find the underlying cost for this object. The underlying cost * + * does not include any free items that normally come with the object when purchased * + * directly. Example: The raw cost of a refinery is the normal cost minus the cost of a * + * harvester. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the credit cost of the base object type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Raw_Cost(void) const +{ + return(Cost); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Get_Ownable -- Fetches the ownable bits for this object type. * + * * + * This routine will return the ownable bits for this object type. The ownable bits are * + * a bitflag composite of the houses that can own (build) this object type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the ownable bits for this object type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +unsigned short TechnoTypeClass::Get_Ownable(void) const +{ + return(Ownable); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Time_To_Build -- Fetches the time to build this object. * + * * + * This routine will return the time it takes to construct this object. Usually the time * + * to produce is directly related to cost. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the time to produce this object type. The time is expressed in the * + * form of game ticks. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Time_To_Build(HousesType house) const +{ + int cost = Raw_Cost(); + + /* + ** For computer controlled buildings, slow down production on + ** cheaper buildings. + */ + if (!Special.IsDifficult && + GameToPlay == GAME_NORMAL && + What_Am_I() == RTTI_BUILDINGTYPE && + PlayerPtr->Class->House != house) { + + cost = (cost + (Special.IsEasy ? 4000 : 2000)) / 2; + } + + /* + ** Fudge factor, so that Nod builds a bit faster if the object must be delivered to + ** an airfield. + */ + if (What_Am_I() == RTTI_UNITTYPE && !(Ownable & HOUSEF_GOOD)) { + return (cost - (cost/4)); + } + + return(cost); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Cost_Of -- Fetches the cost of this object type. * + * * + * This routine will return the cost to produce an object of this type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the cost to produce one object of this type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Cost_Of(void) const +{ + return(Cost); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Get_Cameo_Data -- Fetches the cameo image for this object type. * + * * + * This routine will fetch the cameo (sidebar small image) shape of this object type. * + * If there is no cameo data available (typical for non-producable units), then NULL will * + * be returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the cameo data for this object type if present. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void const * TechnoTypeClass::Get_Cameo_Data(void) const +{ + return(CameoData); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Repair_Cost -- Fetches the cost to repair one step. * + * * + * This routine will return the cost to repair one step. At the TechnoTypeClass level, * + * this merely serves as a placeholder function. The derived classes will provide a * + * functional version of this routine. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the cost to repair one step. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Repair_Cost(void) const +{ + return(0); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Repair_Step -- Fetches the health to repair one step. * + * * + * This routine merely serves as placeholder virtual function. The various type classes * + * will override this routine to return the number of health points to repair in one * + * "step". * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of health points to repair in one step. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Repair_Step(void) const +{ + return(0); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * TechnoClass::Debug_Dump -- Displays the base class data to the monochrome screen. * + * * + * This routine is used to dump the status of the object class to the monochrome screen. * + * This display can be used to track down or prevent bugs. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Debug_Dump(MonoClass *mono) const +{ + mono->Set_Cursor(0,0);mono->Printf("(%04X)p=%d,d=%d", House->Power_Fraction(), House->Power, House->Drain); +// mono->Set_Cursor(0,0);mono->Printf("(%d)", House->Blockage); + mono->Text_Print("X", 16 + (IsALoaner?2:0), 11); + mono->Text_Print("X", 16 + (IsLocked?2:0), 9); + + mono->Text_Print("X", 16 + (IsInRecoilState?2:0), 17); + mono->Text_Print("X", 16 + (IsTethered?2:0), 8); + mono->Text_Print("X", 16 + (IsOwnedByPlayer?2:0), 5); + mono->Text_Print("X", 16 + (IsDiscoveredByPlayer?2:0), 6); +// mono->Text_Print("X", 16 + (IsALemon?2:0), 9); + mono->Set_Cursor(28, 7);mono->Printf("%2d", Arm); + mono->Set_Cursor(34, 1);mono->Printf("%04X", TarCom); + mono->Set_Cursor(29, 3);mono->Printf("%02X", PrimaryFacing.Current()); + + FlasherClass::Debug_Dump(mono); + StageClass::Debug_Dump(mono); + RadioClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * TechnoClass::TechnoClass -- Default constructor for techno objects. * + * * + * This default constructor for techno objects is used to reset all internal variables to * + * their default state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +TechnoClass::TechnoClass(void) : + TarCom(TARGET_NONE), + House(0) +{ + Arm = 0; + Ammo = -1; + PurchasePrice = 0; + IsTickedOff = false; + Cloak = UNCLOAKED; + CloakingDevice.Set_Stage(1); + CloakingDevice.Set_Rate(0); + IsCloakable = false; + IsALemon = false; + IsALoaner = false; + IsDiscoveredByPlayer = false; + IsDiscoveredByComputer = false; + IsInRecoilState = false; + IsLeader = false; + IsLocked = false; + IsOwnedByPlayer = false; + IsSecondShot = true; + IsTethered = false; + SuspendedTarCom = TARGET_NONE; + PrimaryFacing.Set(DIR_N); +} + + +/*********************************************************************************************** + * TechnoClass::Revealed -- Handles revealing an object to the house specified. * + * * + * When a unit moves out from under the shroud or when it gets delivered into already * + * explored terrain, then it must be "revealed". Objects that are revealed may be * + * announced to the player. The discovered bit updated accordingly. * + * * + * INPUT: house -- The house that this object is revealed to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + * 12/27/1994 JLB : Discovered trigger event processing. * + *=============================================================================================*/ +bool TechnoClass::Revealed(HouseClass * house) +{ + if (house == PlayerPtr && IsDiscoveredByPlayer) return(false); + if (house != PlayerPtr && IsDiscoveredByComputer) return(false); + + if (RadioClass::Revealed(house)) { + + /* + ** An enemy object that is discovered will go into hunt mode if + ** its current mission is to ambush. + */ + if (!house->IsHuman && Mission == MISSION_AMBUSH) { + Assign_Mission(MISSION_HUNT); + } + + if (house == PlayerPtr) { + + if (!IsOwnedByPlayer) { + + /* + ** If there is a trigger event associated with this object, then process + ** it for discovery purposes. + */ + if (Trigger) { + Trigger->Spring(EVENT_DISCOVERED, this); + } + + /* + ** Alert the enemy house to presence of the friendly side. + */ + House->IsDiscovered = true; + + } else { + + /* + ** A newly revealed object will always perform a look operation. + */ + if (house == PlayerPtr) IsDiscoveredByPlayer = true; + if (house != PlayerPtr) IsDiscoveredByComputer = true; + Look(); + } + } + + if (house == PlayerPtr) IsDiscoveredByPlayer = true; + if (house != PlayerPtr) IsDiscoveredByComputer = true; + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Hidden -- Returns the object back into the hidden state. * + * * + * This routine is called for every object that returns to the darkness shroud or when * + * it gets destroyed. This also occurs when an object enters another (such as infantry * + * entering a transport helicopter). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Hidden(void) +{ + if (!IsDiscoveredByPlayer) return; + if (!House->IsHuman) { + IsDiscoveredByPlayer = false; + } +} + + +/*********************************************************************************************** + * TechnoClass::Mark -- Handles marking of techno objects. * + * * + * On the Techno-level, marking handles transmission of the redraw command to any object * + * that it is 'connected' with. This only occurs during loading and unloading. * + * * + * INPUT: mark -- The marking method. This routine just passes this on to base classes. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Mark(MarkType mark) +{ + if (RadioClass::Mark(mark)) { + /* + ** When redrawing an object, if there is another object teathered to this one, + ** redraw it as well. + */ + if (IsTethered) { + Transmit_Message(RADIO_REDRAW); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Receive_Message -- Handles inbound message as appropriate. * + * * + * This routine is used to handle inbound radio messages. It only handles those messages * + * that deal with techno objects. Typically, these include loading and unloading. * + * * + * INPUT: from -- Pointer to the originator of the radio message. * + * * + * message -- The inbound radio message. * + * * + * param -- Reference to optional parameter that might be used to transfer * + * more information than is possible with the simple radio message * + * type. * + * * + * OUTPUT: Returns with the radio response. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 06/17/1995 JLB : Handles tether contact messages. * + *=============================================================================================*/ +RadioMessageType TechnoClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + switch (message) { + + /* + ** Just received instructions to attack the specified target. + */ + case RADIO_ATTACK_THIS: + if (Techno_Type_Class()->Primary != WEAPON_NONE) { + Assign_Target(param); + Assign_Mission(MISSION_ATTACK); + return(RADIO_ROGER); + } + break; + + /* + ** Establish a tethered connection to the object in radio contact. + */ + case RADIO_TETHER: + if (!IsTethered) { + IsTethered = true; + Transmit_Message(RADIO_TETHER, from); + return(RADIO_ROGER); + } + break; + + /* + ** Break the tethered connection to the object in radio contact. + */ + case RADIO_UNTETHER: + if (IsTethered) { + IsTethered = false; + Transmit_Message(RADIO_UNTETHER, from); + return(RADIO_ROGER); + } + break; + + /* + ** A teathered unit has reached it's destination. All is + ** clear, so radio contact can be broken. + */ + case RADIO_UNLOADED: + Transmit_Message(RADIO_UNTETHER, from); + return(Transmit_Message(RADIO_OVER_OUT, from)); + + /* + ** When this message is received, it means that the other object + ** has already turned its radio off. Turn this radio off as well. + */ + case RADIO_OVER_OUT: + Transmit_Message(RADIO_UNTETHER, from); + RadioClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + /* + ** Request to be informed when unloaded. This message is transmitted + ** by the transport unit to the transported unit as it is about to be + ** unloaded. It is saying, "All is clear. Drive off now." + */ + case RADIO_UNLOAD: + case RADIO_BACKUP_NOW: + case RADIO_HOLD_STILL: + Transmit_Message(RADIO_TETHER, from); + RadioClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + /* + ** Handle reloading one ammo point for this unit. + */ + case RADIO_RELOAD: + if (Ammo == Techno_Type_Class()->MaxAmmo) return(RADIO_NEGATIVE); + Ammo++; + return(RADIO_ROGER); + + /* + ** Handle repair of this unit. + */ + case RADIO_REPAIR: + if (/*param > 0 &&*/ Health_Ratio() < 0x0100) { + int cost = Techno_Type_Class()->Repair_Cost(); +// int cost = Fixed_To_Cardinal(Techno_Type_Class()->Repair_Cost(), param); + cost = MAX(cost, 1); + int step = Techno_Type_Class()->Repair_Step(); +// int step = Fixed_To_Cardinal(Techno_Type_Class()->Repair_Step(), param); + step = MAX(step, 1); + if (House->Available_Money() >= cost) { +#ifdef OBSOLETE + if (Health_Ratio() >= 0x0100) { + Strength = Class_Of().MaxStrength; + from->Scatter(true); + return(RADIO_NEGATIVE); + } +#endif + House->Spend_Money(cost); + Strength += step; + return(RADIO_ROGER); + } + } + break; + + default: + break; + } + return(RadioClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * TechnoClass::TechnoClass -- Constructor for techno type objects. * + * * + * This constructor sets the owner of this object and its strength. Any object not owned * + * by the player is marked as a loaner. This is a special flag that indicates off-map * + * movement is allowed. The flag is cleared when the object finally enters the map. * + * * + * INPUT: house -- The house (owner) of this object. * + * * + * strength -- The strength to assign to this object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +TechnoClass::TechnoClass(HousesType house) : + House(HouseClass::As_Pointer(house)), + TarCom(TARGET_NONE) +{ + Arm = 0; + Ammo = -1; + PurchasePrice = 0; + IsTickedOff = false; + Cloak = UNCLOAKED; + CloakingDevice.Set_Stage(1); + CloakingDevice.Set_Rate(0); + IsCloakable = false; + IsALemon = false; + IsALoaner = false; + IsDiscoveredByComputer = false; + IsOwnedByPlayer = (house == PlayerPtr->Class->House); + IsDiscoveredByPlayer = false; + IsInRecoilState = false; + IsLeader = false; + IsLocked = false; + IsSecondShot = false; + IsTethered = false; + + SuspendedTarCom = TARGET_NONE; + + PrimaryFacing.Set(DIR_N); + + /* + ** There is a chance that a vehicle will be a "lemon". + */ + if (Random_Pick(0, 255) < House->Class->Lemon) { + IsALemon = true; + } +} + + +/*********************************************************************************************** + * TechnoClass::Per_Cell_Process -- Handles once-per-cell operations for techno type objects. * + * * + * This routine handles marking a game object as not a loaner. It is set only if the unit * + * is not player owned and is on the regular map. This is necessary so that enemy objects * + * can exist off-map but as soon as they move onto the map, are flagged so that can never * + * leave it again. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 10/26/94 JLB : Handles scanner units. * + * 12/27/1994 JLB : Checks for an processes any trigger in cell. * + *=============================================================================================*/ +void TechnoClass::Per_Cell_Process(bool ) +{ + CELL cell = Coord_Cell(Center_Coord()); + + /* + ** When enemy units enter the proper map area from off map, they are + ** flagged so that they won't travel back off the map again. + */ + if (Map.In_Radar(cell)) { + + if (What_Am_I() == RTTI_UNIT) { + UnitClass * u = (UnitClass *)this; + + if (*u != UNIT_HOVER && *u != UNIT_GUNBOAT) { + IsLocked = true; + } + } else { + IsLocked = true; + } + } + + /* + ** If this object somehow moves into mapped terrain, but is not yet + ** discovered, then flag it to be discovered. + */ + if (!IsDiscoveredByPlayer && Map[cell].IsVisible) { + Revealed(PlayerPtr); + } +} + + +/*********************************************************************************************** + * TechnoClass::Draw_It -- Draws the health bar (if necessary). * + * * + * This routine will draw the common elements for techno type objects. This element is * + * the health bar. The main game object has already been rendered by the time this * + * routine is called. * + * * + * INPUT: x,y -- The coordinate of the center of the unit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 10/26/94 JLB : Knows about radar scanned cells. * + * 12/13/1994 JLB : Clips health bar against map edge. * + * 01/23/1995 JLB : Dynamic selected object rectangle. * + *=============================================================================================*/ +void TechnoClass::Draw_It(int x, int y, WindowNumberType window) +{ + Clear_Redraw_Flag(); + if (IsSelected || Special.IsBarOn) { + GraphicViewPortClass draw_window( LogicPage->Get_Graphic_Buffer(), + WindowList[window][WINDOWX] << 3 + LogicPage->Get_XPos(), + WindowList[window][WINDOWY] + LogicPage->Get_YPos(), + WindowList[window][WINDOWWIDTH] << 3, + WindowList[window][WINDOWHEIGHT]); + + + /* + ** The infantry select box should be a bit higher than normal. + */ + if (What_Am_I() == RTTI_INFANTRY) { + y -= 6; + } + + if (What_Am_I() == RTTI_BUILDING && ((BuildingTypeClass const &)Class_Of()).Type == STRUCT_BARRACKS) { + y -= 5; + } + + /* + ** Fetch the dimensions of the object. These dimensions will be used to draw + ** the selection box and the health bar. + */ + int width,height; + Class_Of().Dimensions(width, height); + + if (Strength && (House->Is_Ally(PlayerPtr) || Special.IsHealthBar)) { + unsigned ratio = Health_Ratio(); + int pwidth; // Pixel width of bar interior. + int color; // The color to give the interior of the bargraph. + + int xx = x-width/2; + int yy = y-(height/2); + + /* + ** Draw the outline of the bargraph. + */ + draw_window.Remap(xx+1, yy+1, width-1, 3-1, Map.FadingShade); + draw_window.Draw_Rect(xx, yy, xx+width-1, yy+3, BLACK); + + /* + ** Determine the width of the interior strength + ** graph. + */ + pwidth = Fixed_To_Cardinal(width-2, ratio); + + pwidth = Bound(pwidth, 1, width-2); + + color = LTGREEN; + if (ratio < 0x7F) { + color = YELLOW; + } + if (ratio < 0x3F) { + color = RED; + } + draw_window.Fill_Rect(xx+1, yy+1, xx+pwidth, yy+(3-1), color); + } + + /* + ** Draw the selected object graphic. + */ + if (IsSelected) { + int lx = width/2; + int ly = height/2; + int dx = width/5; + int dy = height/5; + int fudge = (House->Is_Ally(PlayerPtr) || Special.IsHealthBar) ? 4 : 0; + + // Upper left corner. + draw_window.Draw_Line(x-lx, fudge+y-ly, x-lx+dx, fudge+y-ly, WHITE); + draw_window.Draw_Line(x-lx, fudge+y-ly, x-lx, fudge+y-ly+dy, WHITE); + + // Upper right corner. + draw_window.Draw_Line(x+lx, fudge+y-ly, x+lx-dx, fudge+y-ly, WHITE); + draw_window.Draw_Line(x+lx, fudge+y-ly, x+lx, fudge+y-ly+dy, WHITE); + + // Lower right corner. + draw_window.Draw_Line(x+lx, y+ly, x+lx-dx, y+ly, WHITE); + draw_window.Draw_Line(x+lx, y+ly, x+lx, y+ly-dy, WHITE); + + // Lower left corner. + draw_window.Draw_Line(x-lx, y+ly, x-lx+dx, y+ly, WHITE); + draw_window.Draw_Line(x-lx, y+ly, x-lx, y+ly-dy, WHITE); + + if (House->Is_Ally(PlayerPtr)) { + Draw_Pips((x-lx)+5, y+ly-3, window); + } + } + } +} + + +/*********************************************************************************************** + * TechnoClass::Unlimbo -- Performs unlimbo process for all techno type objects. * + * * + * This routine handles the common operation between techno objects when they are * + * unlimboed. This includes revealing the map. * + * * + * INPUT: coord -- The coordinate to unlimbo object at. * + * * + * dir (optional) -- initial facing direction for this object * + * * + * OUTPUT: bool; Was the unlimbo successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1994 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Unlimbo(COORDINATE coord, DirType dir) +{ + if (RadioClass::Unlimbo(coord, dir)) { + PrimaryFacing = dir; + Enter_Idle_Mode(true); + Commence(); + + IsLocked = Map.In_Radar(Coord_Cell(coord)); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::In_Range -- Determines if specified target is within weapon range. * + * * + * This routine is used to compare the distance to the specified target with the range * + * of the weapon. If the target is outside of weapon range, then false is returned. * + * * + * INPUT: target -- The target to check if it is within weapon range. * + * * + * which -- Which weapon to use in determining range. 0=primary, 1=secondary. * + * * + * OUTPUT: bool; Is the specified target within weapon range? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1994 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::In_Range(TARGET target, int which) const +{ + if (IsLocked && Target_Legal(target)) { + int range = Weapon_Range(which); + BuildingClass const * building = As_Building(target); + if (building) { + range += ((building->Class->Width() + building->Class->Height()) * (ICON_LEPTON_W / 4)); + } + if (::Distance(Fire_Coord(which), As_Coord(target)) <= range) { + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::In_Range -- Determines if specified target is within weapon range. * + * * + * This routine will determine if the pointer to the target object passed into this * + * routine is within weapon range. * + * * + * INPUT: target -- Pointer to the target object to check if within weapon range. * + * * + * which -- Which weapon to use in determining range. 0=primary, 1=secondary. * + * * + * OUTPUT: bool; Is the target within weapon range? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1994 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::In_Range(ObjectClass const * target, int which) const +{ + if (IsLocked && target) { + int range = Weapon_Range(which); + if (target->What_Am_I() == RTTI_BUILDING) { + BuildingClass const * building = (BuildingClass const *)target; + range += ((building->Class->Width() + building->Class->Height()) * (ICON_LEPTON_W / 4)); + } + + if (::Distance(Fire_Coord(which), target->Center_Coord()) <= range) { + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::In_Range -- Determines if the specified coordinate is within range. * + * * + * Use this routine to determine if the specified coordinate is within weapon range. * + * * + * INPUT: coord -- The coordinate to examine against the object to determine range. * + * * + * which -- The weapon to consider when determining range. 0=primary, 1=secondary. * + * * + * OUTPUT: bool; Is the weapon within range? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/16/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::In_Range(COORDINATE coord, int which) const +{ + return(IsLocked && ::Distance(Fire_Coord(which), coord) <= Weapon_Range(which)); +} + + +/*********************************************************************************************** + * TechnoClass::Evaluate_Object -- Determines score value of specified object. * + * * + * This routine is used to determing the score value (value as a potential target) of the * + * object specified. This routine will check the specified object for all the various * + * legality checks that threat scanning requires. This is the main workhorse routine for * + * target searching. * + * * + * INPUT: method -- The threat method requested. This is a combined bitflag value that * + * not only specified the kind of targets to consider, but how far away * + * they are allowed to be. * + * * + * mask -- This is an RTTI mask to use for quickly eliminating object types. * + * The mask is created outside of this routine because this routine is * + * usually called from within a loop and this value is constant in that * + * context. * + * * + * range -- The range at which potential target objects are rejected. * + * 0 = must be within weapon range. * + * >0 = must be within this lepton distance. * + * <0 = range doesn't matter. * + * * + * object -- Pointer to the object itself. * + * * + * value -- Reference to the value variable that this routine will fill in. The * + * higher the value the more likely this object will be selected as best. * + * * + * OUTPUT: Did the target pass all legality checks? If this value is returned true, then the * + * value parameter will be filled in correctly. * + * * + * WARNINGS: This routine is time consuming. Don't call unless necessary. * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + * 07/14/1995 JLB : Forces SAM site to not fire on landed aircraft. * + *=============================================================================================*/ +bool TechnoClass::Evaluate_Object(ThreatType method, int mask, int range, TechnoClass const * object, int & value) const +{ + /* + ** An object in limbo can never be a valid target. + */ + if (object->IsInLimbo) return(false); + + /* + ** Friendly units are never considered a good target. Bail if this + ** object is a friend. + */ + if (House->Is_Ally(object)) return(false); + + /* + ** If the object is farther away than allowed, bail. + */ + int dist = Distance(object); + if (range > 0 && dist > range) return(false); + if (range == 0 && !In_Range(object, 0) && !In_Range(object, 1)) return(false); + + /* + ** If the object is not visible, then bail. Human controled units + ** are always considered to be visible. + */ + if (!object->IsOwnedByPlayer && !object->IsDiscoveredByPlayer && GameToPlay == GAME_NORMAL && object->What_Am_I() != RTTI_AIRCRAFT) { + return(false); + } + + /* + ** Quickly eliminate all unit types that are not allowed according to the mask + ** value. + */ + RTTIType otype = object->What_Am_I(); + if (!((1 << otype) & mask)) return(false); // Mask failure. + + /* + ** If the object is cloaked, then it isn't a legal target. + */ + if (object->Cloak == CLOAKED) return(false); + + /* + ** Determine if the target is theoretically allowed to be a target. If + ** not, then bail. + */ + TechnoTypeClass const * tclass = object->Techno_Type_Class(); + if (!tclass->IsLegalTarget) return(false); // Legality failure. + + /* + ** Never consider agent Delphi a valid target. + */ + if (otype == RTTI_INFANTRY && ((InfantryTypeClass const *)tclass)->Type == INFANTRY_DELPHI) { + return(false); + } + + /* + ** Special case so that SAM site doesn't fire on aircraft that are landed. + */ + if (otype == RTTI_AIRCRAFT && What_Am_I() == RTTI_BUILDING && *((BuildingClass *)this) == STRUCT_SAM) { + if (((AircraftClass *)object)->Altitude == 0) return(false); + } + + /* + ** If only allowed to attack civilians, then eliminate all other types. + */ + if ((method & THREAT_CIVILIANS) && object->Owner() != HOUSE_NEUTRAL) { + return(false); + } + + /* + ** If the scan is limited to capturable buildings only, then bail if the examined + ** object isn't a capturable building. + */ + if ((method & THREAT_CAPTURE) && (otype != RTTI_BUILDING || !((BuildingTypeClass const *)tclass)->IsCaptureable)) { + return(false); + } + + /* + ** If not allowed to attack boats, then eliminate them from consideration. + */ + if (!(method & THREAT_BOATS) && + otype == RTTI_UNIT && + (((UnitTypeClass const *)tclass)->Speed == SPEED_HOVER || ((UnitTypeClass const *)tclass)->Speed == SPEED_FLOAT)) { + return(false); + } + + /* + ** SPECIAL CASE: Friendly units won't automatically fire on buildings + ** if the building is not aggressive. + */ + if (House->IsHuman && otype == RTTI_BUILDING && tclass->Primary == WEAPON_NONE) return(false); + + /* + ** If the search is restricted to Tiberium processing objects, then + ** perform the special qualification check now. + */ + if (method & THREAT_TIBERIUM) { + switch (otype) { + case RTTI_UNIT: + if (!((UnitTypeClass const *)tclass)->IsToHarvest) return(false); + break; + + case RTTI_BUILDING: + if (!((BuildingTypeClass const *)tclass)->Capacity) return(false); + break; + + default: + return(false); + } + } + + /* + ** If this target value is better than the previously recorded best + ** target value then record this target for possible return as the + ** best target available. + */ + int rawval = object->Value(); + value = rawval + object->Kills; + +#ifdef ADVANCED + /* + ** Lessen threat as a factor of distance. + */ + if (rawval) { + + value = (value * 32000) / (((dist/ICON_LEPTON_W)*(dist/ICON_LEPTON_W))+1); + + //value = Fixed_To_Cardinal(value, Cardinal_To_Fixed(MAP_CELL_W*2, (MAP_CELL_W*2) - (dist/ICON_LEPTON_W))); + //value = MAX(value, 2); + + if (value < MAP_CELL_W*2) value = dist/ICON_LEPTON_W; + value = MAX(value, 1); + return(true); + } + value = 0; + return(false); + +#else + + /* + ** Lessen threat as a factor of distance. + */ + int modifier = dist; + int crange = range / ICON_LEPTON_W; + if (crange) modifier /= crange; + if (modifier) value /= modifier; + if (rawval) { + value = MAX(value, 2); + } + return(true); +#endif +} + + +/*********************************************************************************************** + * TechnoClass::Evaluate_Cell -- Determine the value and object of specified cell. * + * * + * This routine will examine the specified cell and return with the potential target * + * object it contains and the value of it. Use this routine when searching for threats. * + * * + * INPUT: method -- The scan method to use for target searching. * + * * + * mask -- Prebuilt mask of object RTTI types acceptable for scanning. * + * * + * range -- Scan range limit to use for elimination purposes. This ensures that * + * objects in the "corner" of a square scan get properly discarded. * + * * + * object -- Pointer to object pointer to be filled in with the object at this * + * cell as a valid target. * + * * + * value -- Reference to the value of the object in this cell. It will be set * + * according to the object's value. * + * * + * OUTPUT: Was a valid potential target found in this cell? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Evaluate_Cell(ThreatType method, int mask, CELL cell, int range, TechnoClass const * * object, int & value) const +{ + *object = NULL; + value = 0; + + /* + ** If the cell is not on the legal map, then always ignore it. + */ + if (cell & 0xF000) return(false); + if (!Map.In_Radar(cell)) return(false); + + /* + ** Fetch the techno object from the cell. If there is no + ** techno object there, then bail. + */ + CellClass * cellptr = &Map[cell]; + TechnoClass const * tentative = (TechnoClass const *)cellptr->Cell_Occupier(); + while (tentative) { + if (tentative->Is_Techno() && !House->Is_Ally(tentative)) break; + tentative = (TechnoClass const *)tentative->Next; + } + + if (!tentative) return(false); +// if (!tentative->Is_Techno()) return(false); + *object = tentative; + + return(Evaluate_Object(method, mask, range, tentative, value)); +} + + +/*********************************************************************************************** + * TechnoClass::Greatest_Threat -- Determines best target given search criteria. * + * * + * This routine will scan game objects looking for the best target. It is used by the * + * general target searching processes. The type of target scan to perform is controlled * + * by the method control parameter. * + * * + * INPUT: method -- The method control parameter is used to control the type of target * + * scan performed. It consists of a series of bit flags (see ThreatType) * + * that are combined to form the target scan desired. * + * * + * OUTPUT: Returns the target value of a suitable target. If no target was found then the * + * value TARGET_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1994 JLB : Created. * + * 06/20/1995 JLB : Greatly optimized scan method. * + *=============================================================================================*/ +TARGET TechnoClass::Greatest_Threat(ThreatType method) const +{ + ObjectClass const * bestobject = NULL; + int bestval = -1; + + /* + ** Build a quick elimination mask. If the RTTI of the object doesn't + ** qualify with this mask, then we KNOW that it shouldn't be considered. + */ + int mask = 0; + if (method & THREAT_CIVILIANS) mask |= ((1 << RTTI_BUILDING) | (1 << RTTI_INFANTRY) | (1 << RTTI_UNIT)); + if (method & THREAT_AIR) mask |= (1 << RTTI_AIRCRAFT); + if (method & THREAT_CAPTURE) mask |= (1 << RTTI_BUILDING); + if (method & THREAT_BUILDINGS) mask |= (1 << RTTI_BUILDING); + if (method & THREAT_INFANTRY) mask |= (1 << RTTI_INFANTRY); + if (method & THREAT_VEHICLES) mask |= (1 << RTTI_UNIT); + + /* + ** Limit area target scans use a method where the actual map cells are + ** examined for occupants. The occupant is then examined in turn. The + ** best target within the area is returned as a target. + */ + if (method & (THREAT_AREA|THREAT_RANGE)) { + int range = Threat_Range((method & THREAT_RANGE) ? 0 : 1); + +// int range = MAX(Weapon_Range(0), Weapon_Range(1)); +// if (!(method & THREAT_RANGE)) range *= 2; +// range = Bound(range, 0x0100, 0x1400); // Limit maximum scan distance. + int crange = range / ICON_LEPTON_W; + if (range == 0) { + crange = MAX(Weapon_Range(0), Weapon_Range(1)) / ICON_LEPTON_W; + crange++; + } + CELL cell = Coord_Cell(Fire_Coord(0)); +// CELL cell = Coord_Cell(Center_Coord()); + + /* + ** If aircraft are a legal target, then scan through all of them at this time. + ** Scanning by cell is not possible for aircraft since they are not recorded + ** at the cell level. + */ + if (method & THREAT_AIR) { + for (int index = 0; index < Aircraft.Count(); index++) { + TechnoClass * object = Aircraft.Ptr(index); + + int value = 0; + if (Evaluate_Object(method, mask, range, object, value)) { + if (value > bestval) { + bestobject = object; + bestval = value; + } + } + } + } + + /* + ** When scanning the ground, always consider landed aircraft as a valid + ** potential target. This is only true if vehicles are considered a + ** valid target. A landed aircraft is considered a vehicle. + */ + if (method & THREAT_VEHICLES) { + mask |= (1 << RTTI_AIRCRAFT); + } + + /* + ** Radiate outward from the object's location, looking for the best + ** target. + */ + TechnoClass const * object; + int value; + for (int radius = 1; radius < crange; radius++) { + + /* + ** Scan the top and bottom rows of the "box". + */ + for (int x = -radius; x <= radius; x++) { + CELL newcell; + + if ((Cell_X(cell) + x) < Map.MapCellX) continue; + if ((Cell_X(cell) + x) >= (Map.MapCellX+Map.MapCellWidth)) continue; + + if ((Cell_Y(cell) - radius) >= Map.MapCellY) { + newcell = XY_Cell(Cell_X(cell) + x, Cell_Y(cell)-radius); + if (Evaluate_Cell(method, mask, newcell, range, &object, value)) { + if (bestval < value) { + bestobject = object; + } + } + } + + if ((Cell_Y(cell) + radius) < (Map.MapCellY+Map.MapCellHeight)) { + newcell = XY_Cell(Cell_X(cell)+x, Cell_Y(cell)+radius); + if (Evaluate_Cell(method, mask, newcell, range, &object, value)) { + if (bestval < value) { + bestobject = object; + } + } + } + } + + /* + ** Scan the left and right columns of the "box". + */ + for (int y = -(radius-1); y < radius; y++) { + CELL newcell; + + if ((Cell_Y(cell) + y) < Map.MapCellY) continue; + if ((Cell_Y(cell) + y) >= (Map.MapCellY+Map.MapCellHeight)) continue; + + if ((Cell_X(cell) - radius) >= Map.MapCellX) { + newcell = XY_Cell(Cell_X(cell)-radius, Cell_Y(cell)+y); + if (Evaluate_Cell(method, mask, newcell, range, &object, value)) { + if (bestval < value) { + bestobject = object; + } + } + } + + if ((Cell_X(cell) + radius) < (Map.MapCellX+Map.MapCellWidth)) { + newcell = XY_Cell(Cell_X(cell)+radius, Cell_Y(cell)+y); + if (Evaluate_Cell(method, mask, newcell, range, &object, value)) { + if (bestval < value) { + bestobject = object; + } + } + } + } + + /* + ** Bail early if a target has already been found and the range is at + ** one of the breaking points (i.e., normal range or range * 2). + */ + if (bestobject) { + if (radius == crange/4) { + return(bestobject->As_Target()); + } + if (radius == crange/2) { + return(bestobject->As_Target()); + } + } + } + + } else { + + /* + ** A full map scan was requested. First scan through aircraft. The top map layer + ** is NOT scanned since that layer will probably contain more bullets and animations + ** than aircraft. + */ + for (int index = 0; index < Aircraft.Count(); index++) { + TechnoClass * object = Aircraft.Ptr(index); + + int value = 0; + if (Evaluate_Object(method, mask, -1, object, value)) { + if (value > bestval) { + bestobject = object; + bestval = value; + } + } + } + + /* + ** Now scan through the entire ground layer. This is painful, but what other + ** choice is there? + */ + for (index = 0; index < Map.Layer[LAYER_GROUND].Count(); index++) { + ObjectClass const * object = Map.Layer[LAYER_GROUND][index]; + + int value = 0; + if (object->Is_Techno() && Evaluate_Object(method, mask, -1, (TechnoClass const *)object, value)) { + if (value > bestval) { + bestobject = object; + bestval = value; + } + } + } + } + + /* + ** If a good target object was found, then return with the target value + ** of it. + */ + if (bestobject) { + return(bestobject->As_Target()); + } + return(TARGET_NONE); +} + + +/*********************************************************************************************** + * TechnoClass::Owner -- Who is the owner of this object? * + * * + * Use this routine to examine this object and return who the owner is. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the house number of the owner of this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +HousesType TechnoClass::Owner(void) const +{ + return(House->Class->House); +} + + +/*********************************************************************************************** + * TechnoClass::Clicked_As_Target -- Sets the flash count for this techno object. * + * * + * Use this routine to set the flash count for the object. This flash count is the number * + * of times the object will "flash". Typically it is called as a result of the player * + * clicking on this object in order to make it the target of a move or attack. * + * * + * INPUT: count -- The number of times the object should flash. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Clicked_As_Target(int count) +{ + FlashCount = count; +} + + +/*********************************************************************************************** + * TechnoClass::AI -- Handles AI processing for techno object. * + * * + * This routine handles AI processing for techno objects. Typically, this merely dispatches * + * to the appropriate AI routines for the base classes. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Make sure that this routine is only called ONCE per game tick. * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::AI(void) +{ + CargoClass::AI(); + RadioClass::AI(); + DoorClass::AI(); + + /* + ** Handle decision to re-cloak here. Process the cloaking/decloaking operation. + */ + if (IsCloakable) { + + /* + ** If this object is uncloaked, but it can be cloaked and it thinks that it + ** is a good time do so, then begin cloaking. + */ + if (Cloak == UNCLOAKED) { + if (IsOwnedByPlayer) Mark(MARK_CHANGE); + CloakingDevice.Graphic_Logic(); + if (!Arm && CloakingDevice.Fetch_Stage()) { + if (Health_Ratio() > 0x0040) { + Do_Cloak(); + } else { + if (Random_Pick(0, 25) == 1) { + Do_Cloak(); + } + } + } + } else { + + VisualType pre = Visual_Character(true); + CloakingDevice.Graphic_Logic(); + switch (Cloak) { + + /* + ** Handle the uncloaking process. Always mark to redraw + ** the object and when cloaking is complete, stabilize into + ** the normal uncloaked state. + */ + case UNCLOAKING: + Mark(MARK_CHANGE); + if (Visual_Character(true) == VISUAL_NORMAL) { + CloakingDevice.Set_Rate(UNCLOAK_VIS_TIME); + CloakingDevice.Set_Stage(0); // re-start the stage counter + Cloak = UNCLOAKED; + } + break; + + /* + ** Handle the cloaking process. Always mark to redraw the object + ** and when the cloaking process is complete, stabilize into the + ** normal cloaked state. + */ + case CLOAKING: + Mark(MARK_CHANGE); + switch (Visual_Character(true)) { + + /* + ** If badly damaged, then it can never fully cloak. + */ + case VISUAL_DARKEN: + if (Health_Ratio() < 0x0040 && Random_Pick(1, 3) == 1) { + Cloak = UNCLOAKING; + } + break; + +#ifdef NEVER + case VISUAL_SHADOWY: + if (pre != Visual_Character(true)) { + Detach_All(false); + } + break; +#endif + + case VISUAL_HIDDEN: + Cloak = CLOAKED; + CloakingDevice.Set_Rate(0); + + /* + ** Special check to ensure that if the unit is carring a captured + ** flag, it will never fully cloak. + */ + if (What_Am_I() == RTTI_UNIT && ((UnitClass *)this)->Flagged != HOUSE_NONE) { + Do_Shimmer(); + } else { + Detach_All(false); + } + + /* + ** A computer controlled unit will try to scatter if possible so + ** that it will be much harder to locate. + */ + if (What_Am_I() == RTTI_UNIT && !House->IsHuman) { + Scatter(0, true); + } + break; + } + break; + + /* + ** A cloaked object will always be redrawn if it is owned by the + ** player. This ensures that the shimmering effect will animate. + */ + case CLOAKED: + if (IsOwnedByPlayer) { + Mark(MARK_CHANGE); + } + break; + } + } + } + + /* + ** Arming delay always counts down to zero. + */ + if (Arm) Arm--; + + /* + ** Update the animation timer system. If the animation stage + ** changes, then flag the object to be redrawn as well as determine + ** if the current animation process needs to change. + */ + if (What_Am_I() != RTTI_BUILDING) { + if (StageClass::Graphic_Logic() || Time_To_Redraw()) { + Mark(MARK_CHANGE); + } + } + + /* + ** If the object is flashing and a change of flash state has occured, then mark the + ** object to be redrawn. + */ + if (FlasherClass::Process()) { + Mark(MARK_CHANGE); + } +} + + +/*********************************************************************************************** + * TechnoClass::Select -- Selects object and checks to see if can be selected. * + * * + * This function checks to see if this techno object can be selected. If it can, then it * + * is selected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/11/1994 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Select(void) +{ + if (!IsDiscoveredByPlayer && !IsOwnedByPlayer && !Debug_Unshroud) { + return(false); + } + + if (RadioClass::Select()) { + + /* + ** Speak a confirmation of selection. + */ + if (IsOwnedByPlayer && AllowVoice) { + Response_Select(); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Can_Fire -- Determines if this techno object can fire. * + * * + * This performs a simple check to make sure that this techno object can fire. At this * + * level, the only thing checked for is the rearming delay. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the fire legality control code. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +FireErrorType TechnoClass::Can_Fire(TARGET target, int which) const +{ + /* + ** Don't allow firing if the target is illegal. + */ + if (!Target_Legal(target)) { + return(FIRE_ILLEGAL); + } + + ObjectClass * object = As_Object(target); + + /* + ** If the object is completely cloaked, then you can't fire on it. + */ +//Mono_Printf("Units[0]=%p.\n", Units.Raw_Ptr(0)); +//Mono_Printf("Infantry[0]=%p.\n", Infantry.Raw_Ptr(0)); +//Mono_Printf("Buildings[0]=%p.\n", Buildings.Raw_Ptr(0)); +//Mono_Printf("Aircraft[0]=%p.\n", Aircraft.Raw_Ptr(0)); +//Mono_Printf("object=%p, Strength=%d, IsActive=%d, IsInLimbo=%d.\n", object, (long)object->Strength, object->IsActive, object->IsInLimbo);Get_Key(); + if (object && /*(object->IsActive || GameToPlay != GAME_NORMAL) &&*/ object->Is_Techno() && ((TechnoClass *)object)->Cloak == CLOAKED) { + return(FIRE_CANT); + } + + /* + ** If there is no weapon, then firing is not allowed. + */ + WeaponType weap = (which == 0) ? Techno_Type_Class()->Primary : Techno_Type_Class()->Secondary; + if (weap == WEAPON_NONE) { + return(FIRE_CANT); + } + + /* + ** Can only fire anti-aircraft weapons against aircraft unless the aircraft is + ** sitting on the ground. + */ + if (object && object->What_Am_I() == RTTI_AIRCRAFT && + !BulletTypeClass::As_Reference(Weapons[weap].Fires).IsAntiAircraft && + ((AircraftClass *)object)->Altitude > 0) { + + return(FIRE_CANT); + } + + /* + ** Don't allow firing if still rearming. + */ + if (Arm) return(FIRE_REARM); + + /* + ** The target must be within range in order to allow firing. + */ + if (!In_Range(target, which)) { + return(FIRE_RANGE); + } + + /* + ** If there is no ammo left, then it can't fire. + */ + if (!Ammo) { + return(FIRE_AMMO); + } + + /* + ** If cloaked, then firing is disabled. + */ + if (Cloak != UNCLOAKED) { + return(FIRE_CLOAKED); + } + + return(FIRE_OK); +} + + +/*********************************************************************************************** + * TechnoClass::Stun -- Prepares the object for removal from the game. * + * * + * This routine handles cleaning up this techno object from the game system so that when * + * it is subsequently removed, it doesn't leave any loose ends. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Stun(void) +{ + Assign_Target(TARGET_NONE); + Assign_Destination(TARGET_NONE); + Transmit_Message(RADIO_OVER_OUT); + Detach_All(); + Unselect(); +} + + +/*********************************************************************************************** + * TechnoClass::Assign_Target -- Assigns the targeting computer with specified target. * + * * + * Use this routine to set the targeting computer for this object. It checks to make sure * + * that targeting of itself is prohibited. * + * * + * INPUT: target -- The target for this object to attack. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Assign_Target(TARGET target) +{ + if (target == TarCom) return; + + if (!Target_Legal(target)) { + target = TARGET_NONE; + } else { + + /* + ** Prevent targeting of self. + */ + if (target == As_Target()) { + target = ::As_Target(Coord_Cell(Coord)); + } else { + + /* + ** Make sure that the target is not already dead. + */ + ObjectClass * object = As_Object(target); + if (object && (object->IsActive == false || object->Strength == 0)) { + target = TARGET_NONE; + } + } + } + + /* + ** Set the unit's targeting computer. + */ + TarCom = target; +} + + +/*********************************************************************************************** + * TechnoClass::Rearm_Delay -- Calculates the delay before firing can occur. * + * * + * This function calculates the delay between shots. It determines this from the standard * + * rate of fire (ROF) of the base class and modifies it according to game speed and * + * whether this is the first or second shot. All single shot attackers consider their * + * shots to be "second" since the second shot is the one handled normally. The first shot * + * usually gets assigned a much shorter delay time before the next shot can fire. * + * * + * INPUT: second -- bool; Is this the second of a two shot salvo? * + * * + * OUTPUT: Returns with the number of game frames to delay before the next shot may fire. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Rearm_Delay(bool second) const +{ + if (second) { + return(Weapons[Techno_Type_Class()->Primary].ROF + 3); + } + return(9); +} + + +/*********************************************************************************************** + * TechnoClass::Fire_At -- Fires projectile at target specified. * + * * + * This is the main projectile firing code. Buildings, units, and infantry route fire * + * requests through this function. * + * * + * INPUT: target -- The target that the projectile is to be fired at. * + * * + * which -- Which weapon to fire. * + * * + * OUTPUT: Returns with a pointer to the projectile object that was fired. If no projectile * + * could be created or there was some other illegality detected, the return value * + * will be NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + * 07/03/1995 JLB : Moving platforms fire inaccurate projectiles. * + *=============================================================================================*/ +BulletClass * TechnoClass::Fire_At(TARGET target, int which) +{ + BulletClass *bullet; // Projectile. + DirType dir; // The facing to impart upon the projectile. + COORDINATE target_coord; // Coordinate of the target. + COORDINATE fire_coord; // Coordinate of firing position. + TechnoTypeClass const & tclass = *Techno_Type_Class(); + ObjectClass *object; + WeaponTypeClass const *weapon = (which == 0) ? &Weapons[tclass.Primary] : &Weapons[tclass.Secondary]; + BulletTypeClass const &btype = BulletTypeClass::As_Reference(weapon->Fires); + + /* + ** Perform a quick legality check to see if firing can occur. + */ + if (Debug_Map || weapon->Fires == BULLET_NONE || !Target_Legal(target)) { + return(NULL); + } + + /* + ** Fetch the target coordinate for the target specified. + */ + object = As_Object(target); + if (object) { + target_coord = object->Target_Coord(); + } else { + target_coord = As_Coord(target); + } + + /* + ** Get the location where the projectile should appear. + */ + fire_coord = Fire_Coord(which); + + /* + ** If the projectile is a homing type (such as a missile), then it will + ** launch in the direction the turret is facing, NOT necessarily the same + ** direction as the target. + */ + if (btype.IsHoming || btype.IsDropping) { + dir = Fire_Direction(); + if (btype.IsDropping) { + fire_coord = Center_Coord(); + } + } else { + dir = ::Direction(fire_coord, target_coord); + } + + /* + ** Create the projectile. Then process any special operations that + ** need to be performed according to the style of projectile + ** created. + */ + bullet = new BulletClass(weapon->Fires); + if (bullet) { + bullet->Assign_Target(target); + bullet->Payback = this; + bullet->Strength = weapon->Attack; + + /* + ** If this is firing from a moving platform, then the projectile is inaccurate. + */ + if (Special.IsDefenderAdvantage && What_Am_I() != RTTI_BUILDING && ((FootClass const *)this)->IsDriving) { + bullet->IsInaccurate = true; + } + + if (bullet->Unlimbo(fire_coord, dir)) { +//Mono_Printf("Units[0]=%p.\n", Units.Raw_Ptr(0)); +//Mono_Printf("Infantry[0]=%p.\n", Infantry.Raw_Ptr(0)); +//Mono_Printf("Buildings[0]=%p.\n", Buildings.Raw_Ptr(0)); +//Mono_Printf("Aircraft[0]=%p.\n", Aircraft.Raw_Ptr(0)); +//Mono_Printf("object=%p, Strength=%d, IsActive=%d, IsInLimbo=%d.\n", object, (long)object->Strength, object->IsActive, object->IsInLimbo);Get_Key(); + bullet->Payback = this; + bullet->Strength = weapon->Attack; + } else { + delete bullet; + } + if (!bullet->Class->IsFueled) { + IsInRecoilState = true; + } + Arm = Rearm_Delay(IsSecondShot); + if (tclass.IsTwoShooter) { + IsSecondShot = (IsSecondShot == false); + } + + /* + ** Perform any animation effect for this weapon. + */ + AnimType a = weapon->Anim; + switch (a) { + case ANIM_GUN_N: + case ANIM_CHEM_N: + case ANIM_FLAME_N: + a = (AnimType)(a + Dir_Facing(Fire_Direction())); + break; + } + + /* + ** If there is a special firing animation, then create and attach it + ** now. + */ + if (a != ANIM_NONE) { + AnimClass * anim = new AnimClass(a, Fire_Coord(which)); + if (anim) { + anim->Attach_To(this); + } + } + + /* + ** Reduce ammunition for this object. + */ + if (Ammo > 0) { + Ammo--; + } + + /* + ** Firing will in all likelihood, require the unit to be redraw. Flag it to be + ** redrawn here. + */ + Mark(MARK_CHANGE); + + /* + ** If a projectile was fired from a unit that is hidden in the darkness, + ** reveal that unit and a little area around it. + ** For multiplayer games, only reveal the unit if the target is the + ** local player. + */ + if ((!IsOwnedByPlayer && !IsDiscoveredByPlayer) || !Map[Coord_Cell(Center_Coord())].IsMapped) { + if (GameToPlay == GAME_NORMAL) { + Map.Sight_From(Coord_Cell(Center_Coord()), 1, false); + } else { + ObjectClass *obj = As_Object(target); + if (obj) { + HousesType tgt_owner = obj->Owner(); + + if (PlayerPtr->Class->House == tgt_owner) { + Map.Sight_From(Coord_Cell(Center_Coord()), 1, false); + } + } + } + } + } + + return(bullet); +} + + +/*********************************************************************************************** + * TechnoClass::Player_Assign_Mission -- Assigns a mission as result of player input. * + * * + * This routine is called when the mission for an object needs to change as a result of * + * player input. The basic operation would be to queue the event and let the action * + * occur at the frame dictated by the queing system. However, if a voice response is * + * indicated, then perform it at this time. This will give a greater illusion of * + * immediate response. * + * * + * INPUT: order -- The mission order to assign to this object. * + * * + * target -- The target of this object. This will be used for combat and attack. * + * * + * destination -- The movement destination for this object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Player_Assign_Mission(MissionType mission, TARGET target, TARGET destination) +{ + if (AllowVoice) { + if (mission == MISSION_ATTACK) { + Response_Attack(); + } else { + Response_Move(); + } + } + Queue_Mission(As_Target(), mission, target, destination); +} + + +/*********************************************************************************************** + * TechnoClass::What_Action -- Determines what action to perform if object is selected. * + * * + * This routine will examine the object specified and return with the action that will * + * be performed if the mouse button were clicked over the object. * + * * + * INPUT: object -- The object that the mouse button might be clicked on. * + * * + * OUTPUT: Returns with the action that will be performed if the object was clicked on. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 03/21/1995 JLB : Special target control for trees. * + *=============================================================================================*/ +ActionType TechnoClass::What_Action(ObjectClass * object) const +{ + if (object) { + + /* + ** Return the ACTION_SELF flag if clicking on itself. However, if this + ** object cannot do anything special with itself, then just return with + ** the no action flag. + */ + if (object == this && CurrentObject.Count() == 1 && House == PlayerPtr) { + return(ACTION_SELF); + } + + bool altdown = (Keyboard::Down(KN_LALT) || Keyboard::Down(KN_RALT)); + bool ctrldown = (Keyboard::Down(KN_LCTRL) || Keyboard::Down(KN_RCTRL)); + bool shiftdown = (Keyboard::Down(KN_LSHIFT) || Keyboard::Down(KN_RSHIFT)); + + /* + ** Special guard area mission is possible if both the control and the + ** alt keys are held down. + */ + if (IsOwnedByPlayer && ctrldown && altdown && Can_Player_Move() && Can_Player_Fire()) { + return(ACTION_GUARD_AREA); + } + + /* + ** Special override to force a move regardless of what is occupying the location. + */ + if (altdown) { + if (IsOwnedByPlayer && Can_Player_Move()) { + return(ACTION_MOVE); + } + } + + /* + ** Override so that toggled select state can be performed while the key + ** is held down. + */ + if (shiftdown) { + if (IsOwnedByPlayer && !IsALoaner) { + return(ACTION_TOGGLE_SELECT); + } + } + + /* + ** If firing is possible and legal, then return this action potential. + */ + bool control = Keyboard::Down(KN_LCTRL) || Keyboard::Down(KN_RCTRL); + if (IsOwnedByPlayer && (ctrldown || !House->Is_Ally(object)) && (ctrldown || object->Class_Of().IsLegalTarget || (Special.IsTreeTarget && object->What_Am_I() == RTTI_TERRAIN))) { + if (Can_Player_Move() || In_Range(object, 0)) { + return(ACTION_ATTACK); + } + } + + /* + ** Possibly try to select the specified object, if that is warranted. + */ + if (!Is_Weapon_Equipped() || !IsOwnedByPlayer || object->Owner() == Owner()) { + if ((!IsALoaner || !IsOwnedByPlayer) && object->Class_Of().IsSelectable && !object->IsSelected) { + return(ACTION_SELECT); + } + return(ACTION_NONE); + } + } + return(ACTION_NONE); +} + + +/*********************************************************************************************** + * TechnoClass::What_Action -- Determines action to perform if cell is clicked on. * + * * + * Use this routine to determine what action will be performed if the specified cell * + * is clicked on. Usually this action is either a ACTION_MOVE or ACTION_NOMOVE. The action * + * nomove is used to perform special case checking for nearby cells if in fact the mouse * + * is clicked over the cell. * + * * + * INPUT: cell -- The cell to check for being clicked over. * + * * + * OUTPUT: Returns with the action that will occur if the cell is clicked on. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 07/10/1995 JLB : Force fire for buildings is explicitely disabled. * + *=============================================================================================*/ +ActionType TechnoClass::What_Action(CELL cell) const +{ + CellClass const * cellptr = &Map[cell]; + OverlayTypeClass const * optr = NULL; + + bool ctrldown = Keyboard::Down(KN_LCTRL) || Keyboard::Down(KN_RCTRL); + bool shiftdown = Keyboard::Down(KN_LSHIFT) || Keyboard::Down(KN_RSHIFT); + bool altdown = (Keyboard::Down(KN_LALT) || Keyboard::Down(KN_RALT)); + + /* + ** Disable recognizing the key forced fire option when dealing with buildings. + */ + if (What_Am_I() == RTTI_BUILDING) ctrldown = false; + + if (cellptr->Overlay != OVERLAY_NONE) { + optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + } + + /* + ** Special guard area mission is possible if both the control and the + ** alt keys are held down. + */ + if (IsOwnedByPlayer && ctrldown && altdown && Can_Player_Move() && Can_Player_Fire()) { + return(ACTION_GUARD_AREA); + } + + if (IsOwnedByPlayer && Techno_Type_Class()->Primary != WEAPON_NONE && (ctrldown || (optr && optr->IsLegalTarget))) { + WarheadTypeClass const * whead = &Warheads[BulletTypeClass::As_Reference(Weapons[Techno_Type_Class()->Primary].Fires).Warhead]; + if (!optr || (optr->IsWall && (whead->IsWallDestroyer || (whead->IsWoodDestroyer && optr->IsWooden)))) { + if (Can_Player_Move() || In_Range(::As_Target(cell), 0)) { + return(ACTION_ATTACK); + } + } + } + + if (IsOwnedByPlayer && Can_Player_Move()) { + + /* + ** Special override to force a move regardless of what is occupying the location. + */ + if (shiftdown) { + return(ACTION_MOVE); + } + + /* + ** If the object can enter the cell specified, then allow + ** movement to it. + */ + if (Can_Enter_Cell(cell) <= MOVE_CLOAK) { + return(ACTION_MOVE); + } + return(ACTION_NOMOVE); + } + return(ACTION_NONE); +} + + +/*********************************************************************************************** + * TechnoClass::Can_Player_Move -- Determines if the object can move be moved by player. * + * * + * Use this routine to determine whether a movement order can be given to this object. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can this object be given a movement order by the player? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Can_Player_Move(void) const +{ + return(PlayerPtr == House); +} + + +/*********************************************************************************************** + * TechnoClass::Can_Player_Fire -- Determines if the player can give this object a fire order. * + * * + * Call this routine to determine if this object can be given a fire order by the player. * + * Such objects will affect the mouse cursor accordingly -- usually causes the targeting * + * cursor to appear. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can this object be given firing orders by the player? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Can_Player_Fire(void) const +{ + if (House->IsHuman && Is_Techno() && Techno_Type_Class()->Primary != WEAPON_NONE) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Is_Weapon_Equipped -- Determines if this object has a combat weapon. * + * * + * Use this routine to determine if this object is equipped with a combat weapon. Such * + * determination is used by the AI system to gauge the threat potential of the object. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this object equipped with a combat weapon? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_Weapon_Equipped(void) const +{ + return(Techno_Type_Class()->Primary != WEAPON_NONE); +} + + +/*********************************************************************************************** + * TechnoClass::Can_Repair -- Determines if the object can and should be repaired. * + * * + * Use this routine to determine if the specified object is a candidate for repair. In * + * order to qualify, the object must be allowed to be repaired (in theory) and it must * + * be below full strength. If these conditions are met, then it can be repaired. * + * * + * INPUT: none * + * * + * OUTPUT: bool; May this unit be repaired? A return value of false may mean that the object * + * is not allowed to be repaired, or it might be full strength already. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Can_Repair(void) const +{ + /* + ** Temporary hack to disable repair cursor over non-buildings. + */ + if (What_Am_I() == RTTI_UNIT || What_Am_I() == RTTI_INFANTRY || What_Am_I() == RTTI_AIRCRAFT) { + return(false); + } + return(Techno_Type_Class()->IsRepairable && Strength != Class_Of().MaxStrength); +} + + +/*********************************************************************************************** + * TechnoClass::Weapon_Range -- Determines the maximum range for the weapon. * + * * + * Use this routine to determine the maximum range for the weapon indicated. * + * * + * INPUT: which -- Which weapon to use when determining the range. 0=primary, 1=secondary. * + * * + * OUTPUT: Returns with the range of the weapon (in leptons). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Weapon_Range(int which) const +{ + WeaponType weapon = WEAPON_NONE; + TechnoTypeClass const & ttype = *Techno_Type_Class(); + + switch (which) { + case 0: + weapon = ttype.Primary; + break; + + case 1: + weapon = ttype.Secondary; + break; + } + if (weapon != WEAPON_NONE) { + if (weapon == WEAPON_NIKE && GameToPlay == GAME_NORMAL) { + return(Weapons[weapon].Range*2); + } + return(Weapons[weapon].Range); + } + return(0); +} + + +/*************************************************************************** + * TechnoClass::Override_Mission -- temporarily overides a units mission * + * * + * * + * * + * INPUT: MissionType mission - the mission we want to overide * + * TARGET tarcom - the new target we want to overide * + * TARGET navcom - the new navigation point to overide * + * * + * OUTPUT: none * + * * + * WARNINGS: If a mission is already overidden, the current mission is * + * just re-assigned. * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=========================================================================*/ +void TechnoClass::Override_Mission(MissionType mission, TARGET tarcom, TARGET navcom) +{ + SuspendedTarCom = TarCom; + RadioClass::Override_Mission(mission, tarcom, navcom); + Assign_Target(tarcom); +} + + +/*************************************************************************** + * TechnoClass::Restore_Mission -- Restores an overidden mission * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=========================================================================*/ +bool TechnoClass::Restore_Mission(void) +{ + if (RadioClass::Restore_Mission()) { + Assign_Target(SuspendedTarCom); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Captured -- Handles capturing this object. * + * * + * This routine is called when this object gets captured by the house specified. It handles * + * removing this object from any targeting computers and then changes the ownership of * + * the object to the new house. * + * * + * INPUT: newowner -- Pointer to the house that is now the new owner. * + * * + * OUTPUT: Was the object captured? Failure would mean that it is already under control of * + * the house specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Captured(HouseClass * newowner) +{ + if (newowner != House) { + + /* + ** Capture attempt springs any "entered" trigger. The entered trigger + ** occurs first since there may be a special trigger attached to this + ** object that flags a capture as a win and a destroy as a loss. This + ** order is necessary because the object is recorded as a kill as well. + */ + if (Trigger /*&& Trigger->House == newowner->Class->House*/) { + Trigger->Spring(EVENT_PLAYER_ENTERED, this); + } + + /* + ** Record this as a kill. + */ + Record_The_Kill(NULL); + + /* + ** Special kill record logic for capture process. + */ + switch (What_Am_I()) { + case RTTI_BUILDING: + if (newowner) newowner->BuildingsKilled[Owner()]++; + break; + + case RTTI_AIRCRAFT: + case RTTI_INFANTRY: + case RTTI_UNIT: + if (newowner) newowner->UnitsKilled[Owner()]++; + break; + + default: + break; + } + House->WhoLastHurtMe = newowner->Class->House; + + /* + ** Remove from targeting computers. + */ + Detach_All(false); + +#ifdef NEVER + /* + ** Break off any radio contact. + */ + Transmit_Message(RADIO_OVER_OUT); +#endif + + /* + ** Change ownership now. + */ + House = newowner; + IsOwnedByPlayer = (House == PlayerPtr); + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Take_Damage -- Records damage assessed to this object. * + * * + * This routine is called when this object has taken damage. It handles recording whether * + * this object has been destroyed. If it has, then mark the appropriate kill records as * + * necessary. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/20/1995 JLB : Created. * + *=============================================================================================*/ +ResultType TechnoClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source) +{ + ResultType result = ObjectClass::Take_Damage(damage, distance, warhead, source); + + switch (result) { + case RESULT_DESTROYED: + Transmit_Message(RADIO_OVER_OUT); + Stun(); + break; + + /* + ** If some damage was received and this object is cloaked, shimmer + ** the cloak a bit. + */ + default: + if (source && !House->Is_Ally(source)) { + IsTickedOff = true; + } + Do_Shimmer(); + break; + + case RESULT_NONE: + break; + } + return(result); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Max_Passengers -- Fetches the maximum passengers allowed. * + * * + * This routine will return with the maximum number of passengers allowed in this * + * transport. This typically applies to APCs and possibley transport helicopters. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of passengers this transport can carry. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Max_Passengers(void) const +{ + if (IsTransporter) { + return(5); + } + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Record_The_Kill -- Records the death of this object. * + * * + * This routine is used to record the death of this object. It will handle updating the * + * owner house with the kill record as well as springing any trigger events associated with * + * this object's death. * + * * + * INPUT: source -- Pointer to the source of this object's death (if there is a source). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + * 08/23/1995 JLB : Building loss is only counted if it received damage. * + *=============================================================================================*/ +void TechnoClass::Record_The_Kill(TechnoClass * source) +{ + /* + ** Handle any trigger event associated with this object. + */ + if (Trigger && source) Trigger->Spring(EVENT_ATTACKED, this); + + if (Trigger && source) Trigger->Spring(EVENT_DISCOVERED, this); + + if (Trigger) Trigger->Spring(EVENT_DESTROYED, this); + + if (source) { + /* + ** Call the explicity cast versions of the Made_A_Kill function. This + ** is necessary because we don't want to add a virtual function to the + ** CrewClass. Doing so would complicate the save/load process. + */ + switch (source->What_Am_I()) { + case RTTI_INFANTRY: + ((InfantryClass *)source)->Made_A_Kill(); + break; + + case RTTI_UNIT: + ((UnitClass *)source)->Made_A_Kill(); + break; + + case RTTI_BUILDING: + ((BuildingClass *)source)->Made_A_Kill(); + break; + + case RTTI_AIRCRAFT: + ((AircraftClass *)source)->Made_A_Kill(); + break; + } + + House->WhoLastHurtMe = source->Owner(); + } + + switch (What_Am_I()) { + case RTTI_BUILDING: + if ( ((BuildingClass *)this)->WhoLastHurtMe != HOUSE_NONE) { + House->BuildingsLost++; + } + if (source){ + if (GameToPlay == GAME_INTERNET){ + source->House->DestroyedBuildings->Increment_Unit_Total( ((BuildingClass*)this)->Class->Type ); + } + source->House->BuildingsKilled[Owner()]++; + } + + /* + ** If the map is displaying the multiplayer player names & their + ** # of kills, tell it to redraw. + */ + if (Map.Is_Player_Names()) { + Map.Player_Names(true); + } + break; + + + case RTTI_AIRCRAFT: + House->UnitsLost++; + if (source){ + if (GameToPlay == GAME_INTERNET){ + source->House->DestroyedAircraft->Increment_Unit_Total( ((AircraftClass*)this)->Class->Type ); + } + source->House->UnitsKilled[Owner()]++; + } + /* + ** If the map is displaying the multiplayer player names & their + ** # of kills, tell it to redraw. + */ + if (Map.Is_Player_Names()) { + Map.Player_Names(true); + } + break; + + + case RTTI_INFANTRY: + House->UnitsLost++; + if (source){ + if (GameToPlay == GAME_INTERNET){ + source->House->DestroyedInfantry->Increment_Unit_Total( ((InfantryClass*)this)->Class->Type ); + } + source->House->UnitsKilled[Owner()]++; + } + /* + ** If the map is displaying the multiplayer player names & their + ** # of kills, tell it to redraw. + */ + if (Map.Is_Player_Names()) { + Map.Player_Names(true); + } + break; + + + case RTTI_UNIT: + House->UnitsLost++; + if (source){ + if (GameToPlay == GAME_INTERNET){ + source->House->DestroyedUnits->Increment_Unit_Total( ((UnitClass*)this)->Class->Type ); + } + source->House->UnitsKilled[Owner()]++; + } + + /* + ** If the map is displaying the multiplayer player names & their + ** # of kills, tell it to redraw. + */ + if (Map.Is_Player_Names()) { + Map.Player_Names(true); + } + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * TechnoClass::Nearby_Location -- Radiates outward looking for clear cell nearby. * + * * + * This routine is used to find a nearby location from center of this object. It can lean * + * toward finding a location closest to an optional object. * + * * + * INPUT: object -- Optional object that the finding algorithm will try to find a close * + * spot to. * + * * + * OUTPUT: Returns with the cell that is closest to this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1995 JLB : Created. * + *=============================================================================================*/ +CELL TechnoClass::Nearby_Location(TechnoClass const * ) const +{ + /* + ** Radiate outward from the object's location, looking for the best + ** target. + */ + CELL best = 0; + CELL cell = Coord_Cell(Center_Coord()); + for (int radius = 0; radius < MAP_CELL_W/2; radius++) { + + /* + ** Scan the top and bottom rows of the "box". + */ + for (int x = -radius; x <= radius; x++) { + CELL newcell = cell + XY_Cell(x, -radius); + if (Map.In_Radar(newcell) && Map[newcell].Is_Generally_Clear()) { + best = newcell; + } + + newcell = cell + XY_Cell(x, radius); + if (Map.In_Radar(newcell) && Map[newcell].Is_Generally_Clear()) { + best = newcell; + } + } + + /* + ** Scan the left and right columns of the "box". + */ + for (int y = -(radius-1); y < radius; y++) { + CELL newcell = cell + XY_Cell(-radius, y); + if (Map.In_Radar(newcell) && Map[newcell].Is_Generally_Clear()) { + best = newcell; + } + + newcell = cell + XY_Cell(radius, y); + if (Map.In_Radar(newcell) && Map[newcell].Is_Generally_Clear()) { + best = newcell; + } + } + + if (best) break; + } + + return(best); +} + + +/*********************************************************************************************** + * TechnoClass::Do_Uncloak -- Cause the stealth tank to uncloak. * + * * + * This routine will start the stealth tank to uncloak. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Do_Uncloak(void) +{ + if (IsCloakable && (Cloak == CLOAKED || Cloak == CLOAKING)) { + Sound_Effect(VOC_CLOAK, Coord); + Cloak = UNCLOAKING; + CloakingDevice.Set_Stage(0); + CloakingDevice.Set_Rate(1); + } +} + + +/*********************************************************************************************** + * TechnoClass::Do_Cloak -- Start the object into cloaking stage. * + * * + * This routine will start the object into its cloaking state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Do_Cloak(void) +{ + if (IsCloakable && (Cloak == UNCLOAKED || Cloak == UNCLOAKING)) { + Sound_Effect(VOC_CLOAK, Coord); + Detach_All(false); + Cloak = CLOAKING; + CloakingDevice.Set_Stage(0); + CloakingDevice.Set_Rate(1); + } +} + + +/*********************************************************************************************** + * TechnoClass::Do_Shimmer -- Causes this object to shimmer if it is cloaked. * + * * + * This routine is called when this object should shimmer. If the object is cloaked, then * + * a shimmering effect (partial decloak) occurs. For objects that are not cloaked, no * + * affect occurs. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Do_Shimmer(void) +{ + if (IsCloakable && Cloak == CLOAKED) { + Cloak = CLOAKING; + CloakingDevice.Set_Stage(MAX_UNCLOAK_STAGE/2); + CloakingDevice.Set_Rate(1); + } +} + + +/*********************************************************************************************** + * TechnoClass::Visual_Character -- Determine the visual character of the object. * + * * + * This routine will determine how this object should be drawn. Typically, this is the * + * unmodified visible state, but cloaked objects have a different character. * + * * + * INPUT: raw -- Should the check be based on the unmodified cloak condition of the * + * object? If false, then an object owned by the player will never become * + * completely invisible. * + * * + * OUTPUT: Returns with the visual character to use when displaying this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/07/1995 JLB : Created. * + *=============================================================================================*/ +VisualType TechnoClass::Visual_Character(bool raw) +{ + /* + ** When uncloaked or in map editor mode, always draw the object normally. + */ + if (Cloak == UNCLOAKED || Debug_Map) return(VISUAL_NORMAL); + + /* + ** A cloaked unit will not be visible at all unless it is owned + ** by the player. + */ + if (Cloak == CLOAKED) { + if (!raw && IsOwnedByPlayer) return(VISUAL_SHADOWY); + return(VISUAL_HIDDEN); + } + + int stage = CloakingDevice.Fetch_Stage(); + if (Cloak == UNCLOAKING) stage = MAX_UNCLOAK_STAGE - stage; + if (stage <= 0) { + return(VISUAL_NORMAL); + } + + stage = Cardinal_To_Fixed(MAX_UNCLOAK_STAGE, stage); + + if (stage < 0x0040) return(VISUAL_INDISTINCT); + if (stage < 0x0080) return(VISUAL_DARKEN); + if (stage < 0x00C0) return(VISUAL_SHADOWY); + if (!raw && IsOwnedByPlayer) return(VISUAL_SHADOWY); + if (stage < 0x00FF) return(VISUAL_RIPPLE); + return(VISUAL_HIDDEN); +} + + +/*********************************************************************************************** + * TechnoClass::Techno_Draw_Object -- General purpose draw object routine. * + * * + * This routine is used to draw the object. It will handle any remapping or cloaking * + * effects required. This logic is isolated here since all techno object share the same * + * render logic when it comes to remapping and cloaking. * + * * + * INPUT: shapefile -- Pointer to the shape file that the shape will be drawn from. * + * * + * shapenum -- The shape number of the object in the file to use. * + * * + * x,y -- Center pixel coordinate to use for rendering this object. * + * * + * window -- The clipping window to use when rendering. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Techno_Draw_Object(void const * shapefile, int shapenum, int x, int y, WindowNumberType window) +{ + if (shapefile) { + VisualType visual = Visual_Character(); + void const * remap = Remap_Table(); + + if (visual != VISUAL_HIDDEN && visual != VISUAL_RIPPLE) { + if (visual == VISUAL_SHADOWY) { + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_PREDATOR, NULL, Map.FadingShade); + } else { + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_GHOST, remap, Map.UnitShadow); + } + if (visual == VISUAL_DARKEN) { + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, remap, Map.FadingShade); + } + } + if (visual != VISUAL_NORMAL && visual != VISUAL_HIDDEN) { + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL); + } + } +} + + +/*********************************************************************************************** + * TechnoClass::Remap_Table -- Fetches the appropriate remap table to use. * + * * + * This routine is used to fetch the appropriate remap table to use for this object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the remap table to use for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void const * TechnoClass::Remap_Table(void) +{ + return(House->Remap_Table(IsBlushing, true)); +} + + +/*********************************************************************************************** + * TechnoClass::Detach -- Handles removal of target from tracking system. * + * * + * This routine is called when the specified object is about to be removed from the game * + * system. The target object is removed from any tracking computers that this object may * + * have. * + * * + * INPUT: target -- The target object (as a target value) that is being removed from the * + * game. * + * * + * all -- Is the target about to die? A false value might indicate that the * + * object is merely cloaking. In such a case, radio contact will not * + * be affected. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Detach(TARGET target, bool all) +{ + RadioClass::Detach(target, all); + + if (SuspendedMission != MISSION_NONE && SuspendedTarCom == target) { + SuspendedMission = MISSION_NONE; + SuspendedTarCom = TARGET_NONE; + } + + /* + ** If the targeting computer is assigned to the target, then the targeting + ** computer must be cleared. + */ + if (TarCom == target) { + Assign_Target(TARGET_NONE); + Restore_Mission(); + } + + /* + ** If it is in radio contact with another object, then that radio contact + ** must be broken. + */ + if (all && In_Radio_Contact() && Contact_With_Whom()->As_Target() == target) { + Transmit_Message(RADIO_OVER_OUT); + } +} + + +/*********************************************************************************************** + * TechnoClass::Kill_Cargo -- Destroys any cargo attached to this object. * + * * + * This routine handles the destruction of any cargo this object may contain. Typical of * + * this would be when a transport helicopter gets destroyed. * + * * + * INPUT: source -- The source of the destruction of the cargo. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Kill_Cargo(TechnoClass * source) +{ + while (Is_Something_Attached()) { + FootClass * foot = Detach_Object(); + if (foot) { + foot->Record_The_Kill(source); + delete foot; + } + } +} + + +/*********************************************************************************************** + * TechnoClass::Crew_Type -- Fetches the kind of crew this object contains. * + * * + * This routine is called when generating survivors to this object. This routine returns * + * the type of survivor to generate. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the infantry type of a survivor. * + * * + * WARNINGS: This routine is designed to be called repeatedly. Once for each survivor to * + * generate. * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +InfantryType TechnoClass::Crew_Type(void) const +{ + InfantryType infantry = INFANTRY_E1; + if (House->ActLike == HOUSE_NEUTRAL) { + infantry = Random_Pick(INFANTRY_C1, INFANTRY_C9); + } else { + if (Techno_Type_Class()->Primary == WEAPON_NONE && Random_Pick(0, 6) == 1) { + if (Random_Pick(0, 1) == 0) { + infantry = INFANTRY_C1; + } else { + infantry = INFANTRY_C7; + } + } + } + return(infantry); +} + + +/*********************************************************************************************** + * TechnoClass::Value -- Fetches the target value for this object. * + * * + * This routine is used to fetch the target value for this object. The greater the value * + * returned, the better this object is as a target. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the target value for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + * 08/16/1995 JLB : Adjusted for early mission lame-out. * + *=============================================================================================*/ +int TechnoClass::Value(void) const +{ + int value = 0; + + /* + ** In early missions, contents of transports are not figured + ** into the total value. - 8/16/95 + */ + if (BuildLevel > 8 || GameToPlay != GAME_NORMAL) { + if (Is_Something_Attached()) { + FootClass * object = Attached_Object(); + + while (object) { + value += object->Value(); + object = (FootClass *)object->Next; + } + } + } + return Risk() + Techno_Type_Class()->Reward + value; +} + + +/*********************************************************************************************** + * TechnoClass::Threat_Range -- Returns the range to scan based on threat control. * + * * + * This routine will return the range to scan based on the control value specified. The * + * value returned by this routine is typically used when scanning for enemies. * + * * + * INPUT: control -- The range control parameter. * + * 0 = Use weapon range (zero is returned in this special case). * + * -1 = Scan without range restrictions (-1 is returned in this case). * + * 1 = Scan up to twice weapon range. * + * * + * OUTPUT: Returns with a range (or special value) that can be used in the threat scan * + * process. If zero is returned, then always check threat against In_Range(). If * + * -1 is returned, then no range limitation restriction exists. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Threat_Range(int control) const +{ + if (control == -1) return(-1); + if (control == 0) return(0); + + int range = MAX(Weapon_Range(0), Weapon_Range(1)); + range *= 2; + range = Bound(range, 0x0000, 0x0A00); + return(range); +} + + +/*********************************************************************************************** + * TechnoClass::Base_Is_Attacked -- Handle panic response to base being attacked. * + * * + * This routine is called when the base is being attacked. It will pull units off of the * + * field and send them back to defend the base. This routine will make taking an enemy * + * base much more difficult. * + * * + * INPUT: enemy -- Pointer to the enemy object that did the damage on the base. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine can drastically affect the game play. The computer will probably * + * call off its attacks as a result. * + * * + * HISTORY: * + * 06/25/1995 JLB : Commented. * + *=============================================================================================*/ +void TechnoClass::Base_Is_Attacked(TechnoClass const *enemy) +{ + FootClass *defender[6]; + int value[6]; + int count = 0; + int weakest = 0; + int desired = enemy->Risk() * 2; + int risktotal = 0; + + /* + ** Humans have to deal with their own base is attacked problems. + */ + if (!enemy || House->Is_Ally(enemy) || House->IsHuman) { + return; + } + + /* + ** Don't overreact if this building can defend itself. + */ + if (Techno_Type_Class()->Primary != WEAPON_NONE) return; + + /* + ** If the enemy is not an infantry or a unit there is not much we can + ** do about it. + */ + if (enemy->What_Am_I() != RTTI_INFANTRY && enemy->What_Am_I() != RTTI_UNIT ) { + return; + } + + /* + ** If the enemy is a gunboat, then don't do anything. + */ + // This should allow helicopters to retaliate however. Hmmm. + if (enemy->What_Am_I() == RTTI_UNIT && (*((UnitClass const *)enemy) == UNIT_GUNBOAT || *((UnitClass const *)enemy) == UNIT_HOVER)) { + return; + } + + /* + ** If the threat has already been dealt with then we don't need to do + ** any work. Check for that here. + */ + if (!((FootClass *)enemy)->BaseAttackTimer.Expired()) { + return; + } + + /* + ** We will need units to defend our base. We need to suspend teams until + ** the situation has been dealt with. + */ + TeamClass::Suspend_Teams(20); + + /* + ** Loop through the infantry looking for those who are capable of going + ** on a rescue mission. + */ + for (int index = 0; index < Infantry.Count() && desired > 0; index++) { + InfantryClass * infantry = Infantry.Ptr(index); + if (infantry && infantry->Owner() == Owner()) { + + /* + ** Never recruite sticky guard units to defend a base. + */ + if (infantry->Mission == MISSION_STICKY || infantry->Mission == MISSION_SLEEP) continue; + + /* + ** Find the amount of threat that this unit can apply to the + ** enemy. + */ + int threat = infantry->Rescue_Mission(enemy->As_Target()); + + /* + ** If it can't apply any threat then do just skip it and do not + ** add it to the list. + */ + if (!threat) { + continue; + } + + /* + ** If the value returned is negative then this unit is already + ** assigned to fighting the enemy, so subtract its value from + ** the enemys desired value. + */ + if (threat < 0) { + desired += threat; + continue; + } + + if (count < 6) { + defender[count] = (FootClass *)infantry; + value[count] = threat; + count++; + continue; + } + + if (threat > weakest) { + int newweakest = threat; + + for (int lp = 0; lp < count; lp ++) { + if (value[lp] == weakest) { + value[lp] = threat; + defender[lp] = (FootClass *) infantry; + continue; + } + if (value[count] < newweakest) { + newweakest = value[lp]; + } + } + weakest = newweakest; + } + } + } + + /* + ** Loop through the units looking for those who are capable of going + ** on a rescue mission. + */ + for (index = 0; index < Units.Count() && desired > 0; index++) { + UnitClass * unit = Units.Ptr(index); + if (unit && unit->Owner() == Owner()) { + + /* + ** Never recruite sticky guard units to defend a base. + */ + if (unit->Mission == MISSION_STICKY || unit->Mission == MISSION_SLEEP) continue; + + /* + ** Find the amount of threat that this unit can apply to the + ** enemy. + */ + int threat = unit->Rescue_Mission(enemy->As_Target()); + + /* + ** If it can't apply any threat then do just skip it and do not + ** add it to the list. + */ + if (!threat) { + continue; + } + + /* + ** If the value returned is negative then this unit is already + ** assigned to fighting the enemy, so subtract its value from + ** the enemys desired value. + */ + if (threat < 0) { + desired += threat; + continue; + } + + if (count < 6) { + defender[count] = (FootClass *)unit; + value[count] = threat; + count++; + continue; + } + if (threat > weakest) { + int newweakest = threat; + + for (int lp = 0; lp < count; lp ++) { + if (value[lp] == weakest) { + value[lp] = threat; + defender[lp] = (FootClass *) unit; + continue; + } + if (value[count] < newweakest) { + newweakest = value[lp]; + } + } + weakest = newweakest; + } + } + } + + if (desired > 0) { + + /* + ** Sort the defenders by value, this doesn't take very long and will + ** help the closest defenders to respond. + */ + for (int lp = 0; lp < count - 1; lp ++) { + for (int lp2 = lp + 1; lp2 < count; lp2++) { + if (value[lp] < value[lp2]) { + + value[lp] ^= value[lp2]; + value[lp2] ^= value[lp]; + value[lp] ^= value[lp2]; + + FootClass *temp; + temp = defender[lp]; + defender[lp] = defender[lp2]; + defender[lp2] = temp; + } + } + } + + for (lp = 0; lp < count; lp ++) { + defender[lp]->Assign_Mission(MISSION_RESCUE); + defender[lp]->Assign_Target(enemy->As_Target()); + risktotal += defender[lp]->Risk(); + if (risktotal > desired) { + break; + } + } + } + + if (risktotal > desired) { + ((FootClass *)enemy)->BaseAttackTimer.Set(15 * 15); + } +} + + +/*********************************************************************************************** + * TechnoClass::Get_Ownable -- Fetches the ownable bits for this object. * + * * + * This routine will return the ownable bits for this object. The ownable bits represent * + * the houses that are allowed to own this object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the ownable bits for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +unsigned char TechnoClass::Get_Ownable(void) const +{ + return ((TechnoTypeClass const &)Class_Of()).Ownable; +} + + +/*********************************************************************************************** + * TechnoClass::Is_Techno -- Confirms that this is a TechnoClass object. * + * * + * This routine is called to confirm if this object is derived from the TechnoClass * + * object. At this level, the return value will always be true. * + * * + * INPUT: none * + * * + * OUTPUT: Is this object a TechnoClass or derived from it? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_Techno(void) const +{ + return(true); +} + + +/*********************************************************************************************** + * TechnoClass::Risk -- Fetches the risk associated with this object. * + * * + * This routine is called when the risk value for this object needs to be determined. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the risk value for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Risk(void) const +{ + return(Techno_Type_Class()->Risk); +} + + +/*********************************************************************************************** + * TechnoClass::Tiberium_Load -- Fetches the current tiberium load percentage. * + * * + * This routine will return the current Tiberium load (expressed as a fixed point fraction) * + * that this object currently contains. Typical implementor of this function would be * + * the harvester. Any object that can return a non-zero value should derive from this * + * function in order to return the appropriate value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current Tiberium load expressed as a fixed point number. * + * 0x0000 = empty * + * 0x0080 = half full * + * 0x0100 = full * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Tiberium_Load(void) const +{ + return(0x0000); +} + + +/*********************************************************************************************** + * TechnoClass::Desired_Load_Dir -- Fetches loading parameters for this object. * + * * + * This routine is called when an object desires to load up on this object. The object * + * desiring to load is specified. The cell that the loading object should move to is * + * determined. The direction that this object should face is also calculated. This routine * + * will be overridden by those objects that can actually load up passengers. * + * * + * INPUT: object -- The object that is desiring to load up. * + * * + * moveto -- Reference to the cell that the loading object should move to before * + * the final load process occurs (this value will be filled in). * + * * + * OUTPUT: Returns with the direction that the transport object should face. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +DirType TechnoClass::Desired_Load_Dir(ObjectClass * , CELL & moveto) const +{ + moveto = 0; + return(DIR_N); +} + + +/*********************************************************************************************** + * TechnoClass::Pip_Count -- Fetches the number of pips to display on this object. * + * * + * This routine will return the number of pips to display on this object when the object * + * is selected. The default condition is to return no pips at all. This routine is * + * derived for those objects that can have pips. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of pips to display on this object when selected. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Pip_Count(void) const +{ + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Fire_Direction -- Fetches the direction projectile fire will take. * + * * + * This routine will fetch the direction that a fired projectile will take. This is * + * usually the facing of the object's weapon. This routine will be derived for the objects * + * that have their weapon barrel facing a different direction than the body. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the direction a fired projectile will take. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +DirType TechnoClass::Fire_Direction(void) const +{ + return(PrimaryFacing.Current()); +} + + +/*********************************************************************************************** + * TechnoClass::Response_Select -- Handles the voice response when selected. * + * * + * This routine is called when a voice reponse to a select action is desired. This routine * + * should be overridden for any object that actually has a voice response. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine can generate an audio response. * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Response_Select(void) +{ +} + + +/*********************************************************************************************** + * TechnoClass::Response_Move -- Handles the voice repsonse to a movement request. * + * * + * This routine is called when a voice response to a movement order is desired. This * + * routine should be overridden for any object that actually has a voice response. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This can generate an audio repsonse. * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Response_Move(void) +{ +} + + +/*********************************************************************************************** + * TechnoClass::Response_Attack -- Handles the voice response when given attack order. * + * * + * This routine is called when a voice response to an attack order is desired. This routine * + * should be overridden for any object that actually have a voice response. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This can generate an audio response. * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Response_Attack(void) +{ +} + + +/*********************************************************************************************** + * TechnoClass::Target_Something_Nearby -- Handles finding and assigning a nearby target. * + * * + * This routine will search for a nearby target and assign it to this object's TarCom. * + * The method to use when scanning for a target is controlled by the parameter passed. * + * * + * INPUT: threat -- The threat control parameter used to control the range searched. The * + * only values recognized are THREAT_RANGE and THREAT_AREA. * + * * + * OUTPUT: Was a suitable target aquired and assigned to the TarCom? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Target_Something_Nearby(ThreatType threat) +{ + threat = threat & (THREAT_RANGE|THREAT_AREA); + + /* + ** Determine that if there is an existing target it is still legal + ** and within range. + */ + if (Target_Legal(TarCom)) { + if ((threat & THREAT_RANGE) && !In_Range(TarCom)) { + Assign_Target(TARGET_NONE); + } + } + + /* + ** If there is no target, then try to find one and assign it as + ** the target for this unit. + */ + if (!Target_Legal(TarCom)) { + Assign_Target(Greatest_Threat(threat)); + } + + /* + ** Return with answer to question: Does this unit have a target? + */ + return(Target_Legal(TarCom)); +} + + +/*********************************************************************************************** + * TechnoClass::Exit_Object -- Causes specified object to leave this object. * + * * + * This routine is called when there is an attached object that should detach and leave * + * this object. Typical of this would be the refinery and APC. * + * * + * INPUT: object -- The object that is trying to leave this object. * + * * + * OUTPUT: Was the object successfully launched from this object? Failure might indicate that * + * there is insufficient room to detach the specified object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Exit_Object(TechnoClass *) +{ + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Random_Animate -- Performs some idle animation for the object. * + * * + * This is a maintenance routine that is called when the object might want to check to see * + * if it should go into some idle animation. Infantry are a good example of objects that * + * perform idle animations. This routine must be overridden by the derived object types * + * in order to give it some functionality. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Random_Animate(void) +{ +} + + +/*********************************************************************************************** + * TechnoClass::Assign_Destination -- Assigns movement destination to the object. * + * * + * This routine is called when the object needs to have a new movement destination * + * assigned. This routine must be overridden since at this level, movement is not allowed. * + * * + * INPUT: destination -- The destination to assign to this object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Assign_Destination(TARGET ) +{ +} + + +/*********************************************************************************************** + * TechnoClass::Scatter -- Causes the object to scatter to an adjacent cell. * + * * + * This routine is called when the object needs to get out of the way. This might be as a * + * result of combat or findpath reasons. * + * * + * INPUT: coord -- The source of the reason to scatter. The object should try to run * + * away from this coordinate. * + * * + * forced -- Is the scatter a forced scatter? If false, then this is merely a * + * request that scattering might be a good idea. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Scatter(COORDINATE , bool ) +{ +} + + +/*********************************************************************************************** + * TechnoClass::Enter_Idle_Mode -- Object enters its default idle condition. * + * * + * This routine is called when the object should intelligently revert to an idle state. * + * Typically this routine is called after some mission has completed. This routine must * + * be overridden by the various object types. It is located at this level merely to provide * + * a virtual function entry point. * + * * + * INPUT: initial -- Is this called when the unit just leaves a factory or is initially * + * or is initially placed on the map? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Enter_Idle_Mode(bool ) +{ +} + + +/*********************************************************************************************** + * TechnoClass::Draw_Pips -- Draws the transport pips and other techno graphics. * + * * + * This routine is used to render the small transportation pip (occupant feedback graphic) * + * used for transporter object. It will also display if the techno object is "primary" * + * if necessary. * + * * + * INPUT: x,y -- The pixel coordinate for the center of the first pip. Subsiquent pips * + * are drawn rightward. * + * * + * window-- The window that pip clipping is relative to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Draw_Pips(int x, int y, WindowNumberType window) +{ + /* + ** Transporter type objects have a different graphic representation for the pips. The + ** pip color represents the type of occupant. + */ + if (Techno_Type_Class()->IsTransporter) { + ObjectClass const * object = Attached_Object(); + + for (int index = 0; index < Class_Of().Max_Pips(); index++) { + PipEnum pip = PIP_EMPTY; + + if (object) { + pip = PIP_FULL; + if (object->What_Am_I() == RTTI_INFANTRY) { + if (*((InfantryClass *)object) == INFANTRY_RAMBO) { + pip = PIP_COMMANDO; + } + if (*((InfantryClass *)object) == INFANTRY_E7) { + pip = PIP_ENGINEER; + } + if (((InfantryClass *)object)->Class->IsCivilian) { + pip = PIP_CIVILIAN; + } + } + object = object->Next; + } + CC_Draw_Shape(Class_Of().PipShapes, pip, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + + } else { + + /* + ** Display number of how many attached objects there are. This is also used + ** to display the fullness rating for a harvester. + */ + int pips = Pip_Count(); + for (int index = 0; index < Class_Of().Max_Pips(); index++) { + CC_Draw_Shape(Class_Of().PipShapes, (index < pips) ? PIP_FULL : PIP_EMPTY, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + } + + /* + ** Display whether this unit is a leader unit or not. + */ + if (IsLeader) { + CC_Draw_Shape(Class_Of().PipShapes, PIP_PRIMARY, x-2, y-3, window, /*SHAPE_CENTER|*/SHAPE_WIN_REL); + } +} + + +/*********************************************************************************************** + * TechnoClass::Find_Docking_Bay -- Searches for a close docking bay. * + * * + * This routine will be used to find a building that can serve as a docking bay. The * + * closest building that qualifies will be returned. If no building could be found then * + * return with NULL. * + * * + * INPUT: b -- The structure type to look for. * + * * + * friendly -- Allow searching for allied buildings as well. * + * * + * OUTPUT: Returns with a pointer to the building that can serve as the best docking bay. * + * * + * WARNINGS: This routine might return NULL even if there are buildings of the specified * + * type available. This is the case when the building(s) are currently busy and * + * cannot serve as a docking bay at the moment. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + * 08/13/1995 JLB : Recognizes the "IsLeader" method of building preference. * + *=============================================================================================*/ +BuildingClass * TechnoClass::Find_Docking_Bay(StructType b, bool friendly) const +{ + BuildingClass * best = 0; + + /* + ** First check to see if there are ANY buildings of the specified + ** type in thi house's inventory. If not, then don't bother to scan + ** for one. + */ + if (House->BScan & (1L << b)) { + int bestval = -1; + + /* + ** Loop through all the buildings and find the one that matches the specification + ** and is willing to dock with this object. + */ + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + /* + ** Check to see if the building qualifies (preliminary scan). + */ + if (building && + (friendly ? building->House->Is_Ally(this) : building->House == House) && + !building->IsInLimbo && + *building == b && + ((TechnoClass *)this)->Transmit_Message(RADIO_CAN_LOAD, building) == RADIO_ROGER) { + + /* + ** If the building qualifies and this building is better than the + ** last qualifying building (as rated by distance), then record + ** this building and keep scanning. + */ + if (bestval == -1 || Distance(building) < bestval || building->IsLeader) { + best = building; + bestval = Distance(building); + } + } + } + } + return(best); +} + + +/*********************************************************************************************** + * TechnoClass::Find_Exit_Cell -- Finds an appropriate exit cell for this object. * + * * + * This routine is called when an object would like to exit from this (presumed) transport. * + * A suitable cell should be returned by this routine. The specified object will probably * + * be unloaded at that cell. * + * * + * INPUT: techno -- Pointer to the object that would like to unload. This is used to * + * determine suitability for placement. * + * * + * OUTPUT: Returns with the cell that is recommended for object exit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1995 JLB : Created. * + *=============================================================================================*/ +CELL TechnoClass::Find_Exit_Cell(TechnoClass const * ) const +{ + return(Coord_Cell(Docking_Coord())); +} + + +/*********************************************************************************************** + * TechnoClass::Refund_Amount -- Returns with the money to refund if this object is sold. * + * * + * This routine is used by the selling back mechanism in order to credit the owning house * + * with some refund credits. The value returned is the credits to refund to the owner. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the credits to refund to the owner. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Refund_Amount(void) const +{ + int cost = Techno_Type_Class()->Raw_Cost(); + + /* + ** If the object is carrying Tiberium directly (i.e., the harvester), then + ** account for the credits of the load. + */ + cost += Fixed_To_Cardinal(UnitTypeClass::FULL_LOAD_CREDITS, Tiberium_Load())/2; + + if (House->IsHuman) { + cost /= 2; + } + return(cost); +} diff --git a/TECHNO.H b/TECHNO.H new file mode 100644 index 0000000..5aa2376 --- /dev/null +++ b/TECHNO.H @@ -0,0 +1,319 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\techno.h_v 2.17 16 Oct 1995 16:46:58 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TECHNO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TECHNO_H +#define TECHNO_H + +#include "radio.h" +#include "stage.h" +#include "cargo.h" +#include "flasher.h" +#include "house.h" +#include "target.h" +#include "bullet.h" +#include "door.h" +#include "crew.h" + +/**************************************************************************** +** This is the common data between building and units. +*/ +class TechnoClass : public RadioClass, + public FlasherClass, + public StageClass, + public CargoClass, + public DoorClass, + public CrewClass +{ + public: + + /* + ** This flag will be true if the object has been damaged with malace. + ** Damage received due to friendly fire or wear and tear does not count. + ** The computer is not allowed to sell a building unless it has been + ** damaged with malace. + */ + unsigned IsTickedOff:1; + + /* + ** If this object has inherited the ability to cloak, then this bit will + ** be set to true. + */ + unsigned IsCloakable:1; + + /* + ** If this object is designated as special then this flag will be true. For + ** buildings, this means that it is the primary factory. For units, it means + ** that the unit is the team leader. + */ + unsigned IsLeader:1; + + /* + ** Certain units are flagged as "loaners". These units are typically transports that + ** are created solely for the purpose of delivering reinforcements. Such "loaner" + ** units are not owned by the player and thus cannot be directly controlled. These + ** units will leave the game as soon as they have fulfilled their purpose. + */ + unsigned IsALoaner:1; + + /* + ** Once a unit enters the map, then this flag is set. This flag is used to make + ** sure that a unit doesn't leave the map once it enters the map. + */ + unsigned IsLocked:1; + + /* + ** Buildings and units with turrets usually have a recoil animation when they + ** fire. If this flag is true, then the next rendering of the object will be + ** in the "recoil state". The flag will then be cleared pending the next + ** firing event. + */ + unsigned IsInRecoilState:1; + + /* + ** If this unit is "loosely attached" to another unit it is given special + ** processing. A unit is in such a condition when it is in the process of + ** unloading from a transport type object. During the unloading process + ** the transport object must stay still until the unit is free and clear. + ** At that time it radios the transport object and the "tether" is broken - + ** freeing both the unit and the transport object. + */ + unsigned IsTethered:1; + + /* + ** Is this object owned by the player? If not, then it is owned by the computer + ** or remote opponent. This flag facilitates the many logic differences when dealing + ** with player's or computer's units or buildings. + */ + unsigned IsOwnedByPlayer:1; + + /* + ** The more sophisticated game objects must keep track of whether they are discovered + ** or not. This is because the state of discovery can often control how the object + ** behaves. In addition, this fact is used in radar and user I/O processing. + */ + unsigned IsDiscoveredByPlayer:1; + + /* + ** This is used to control the computer recognizing this object. + */ + unsigned IsDiscoveredByComputer:1; + + /* + ** Some game objects can be of the "lemon" variety. This means that they take damage + ** even when everything is ok. This adds a little variety to the game. + */ + unsigned IsALemon:1; + + /* + ** This flag is used to control second shot processing for those units or buildings + ** that fire two shots in quick succession. When this flag is true, it indicates that + ** the second shot is ready to fire. After this shot is fired, regular rearm timing + ** is used rather than the short rearm time. + */ + unsigned IsSecondShot:1; + + /* + ** This is the house that the unit belongs to. + */ + HouseClass * House; + + /* + ** This records the current cloak state for this vehicle. + */ + CloakType Cloak; + StageClass CloakingDevice; + + /* (Targeting Computer) + ** This is the target value for the item that this vehicle should ATTACK. If this + ** is a vehicle with a turret, then it may differ from its movement destination. + */ + TARGET TarCom; + TARGET SuspendedTarCom; + + /* + ** This is the visible facing for the unit or building. + */ + FacingClass PrimaryFacing; + + /* + ** This is the arming countdown. It represents the time necessary + ** to reload the weapon. + */ + unsigned char Arm; + + /* + ** The number of shot this object can fire before running out of ammo. If this + ** value is zero, then firing is not allowed. If -1, then there is no ammunition + ** limit. + */ + int Ammo; + + /* + ** This is the amount of money spent to produce this object. This value really + ** only comes into play for the case of buildings that have special "free" + ** objects available when purchased at the more expensive rate. + */ + int PurchasePrice; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + TechnoClass(void); + TechnoClass(HousesType house); + virtual ~TechnoClass(void) {}; + + /* + ** Query functions. + */ + virtual int Refund_Amount(void) const; + virtual CELL Find_Exit_Cell(TechnoClass const * techno) const; + virtual BuildingClass * Find_Docking_Bay(StructType b, bool friendly) const; + virtual int Threat_Range(int control) const; + virtual InfantryType Crew_Type(void) const; + TechnoTypeClass const * Techno_Type_Class(void) const {return((TechnoTypeClass const *)&Class_Of());}; + CELL Nearby_Location(TechnoClass const * from=NULL) const; + virtual unsigned char Get_Ownable(void) const; + virtual bool Can_Player_Fire(void) const; + virtual bool Can_Player_Move(void) const; + virtual bool Is_Weapon_Equipped(void) const; + virtual bool Can_Repair(void) const; + virtual bool Is_Techno(void) const; + virtual HousesType Owner(void) const; + virtual int Risk(void) const; + virtual int Value(void) const; + virtual int Rearm_Delay(bool second=true) const; + virtual ActionType What_Action(ObjectClass * target) const; + virtual ActionType What_Action(CELL cell) const; + virtual int Tiberium_Load(void) const; + virtual DirType Desired_Load_Dir(ObjectClass * , CELL & moveto) const; + virtual int Pip_Count(void) const; + virtual DirType Fire_Direction(void) const; + + /* + ** User I/O. + */ + virtual void Clicked_As_Target(int count=7); + virtual bool Select(void); + virtual void Response_Select(void); + virtual void Response_Move(void); + virtual void Response_Attack(void); + virtual void Player_Assign_Mission(MissionType order, TARGET target=TARGET_NONE, TARGET destination=TARGET_NONE); + + /* + ** Combat related. + */ + void Base_Is_Attacked(TechnoClass const *enemy); + void Kill_Cargo(TechnoClass * source); + virtual void Record_The_Kill(TechnoClass * source); + virtual bool Target_Something_Nearby(ThreatType threat=THREAT_NORMAL); + virtual void Stun(void); + virtual bool In_Range(COORDINATE coord, int which=0) const; + virtual bool In_Range(TARGET target, int which=0) const; + virtual bool In_Range(ObjectClass const * target, int which=0) const; + virtual void Death_Announcement(TechnoClass const * source=0) const = 0; + virtual FireErrorType Can_Fire(TARGET target, int which=0) const; + virtual TARGET Greatest_Threat(ThreatType threat) const; + virtual void Assign_Target(TARGET target); + virtual void Override_Mission(MissionType mission, TARGET tarcom, TARGET navcom); + virtual bool Restore_Mission(void); + virtual BulletClass * Fire_At(TARGET target, int which=0); + virtual int Weapon_Range(int which) const; + virtual bool Captured(HouseClass * newowner); + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source); + bool Evaluate_Cell(ThreatType method, int mask, CELL cell, int range, TechnoClass const ** object, int & value) const; + bool Evaluate_Object(ThreatType method, int mask, int range, TechnoClass const * object, int & value) const; + + /* + ** AI. + */ + virtual void AI(void); + virtual bool Revealed(HouseClass * house); + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void const * Remap_Table(void); + VisualType Visual_Character(bool raw = false); + void Techno_Draw_Object(void const * shapefile, int shapenum, int x, int y, WindowNumberType window); + virtual void Draw_It(int x, int y, WindowNumberType window); + virtual void Draw_Pips(int x, int y, WindowNumberType window); + virtual void Hidden(void); + virtual bool Mark(MarkType mark); + virtual int Exit_Object(TechnoClass *); + virtual void Do_Uncloak(void); + virtual void Do_Cloak(void); + virtual void Do_Shimmer(void); + + /* + ** Movement and animation. + */ + virtual void Random_Animate(void); + virtual void Assign_Destination(TARGET target); + virtual void Scatter(COORDINATE source = NULL, bool forced=false); + virtual void Per_Cell_Process(bool); + virtual void Enter_Idle_Mode(bool initial=false); + + /* + ** Map entry and exit logic. + */ + virtual bool Unlimbo(COORDINATE , DirType facing=DIR_N); + virtual void Detach(TARGET target, bool all); + + /* + ** Facing translation tables that fix the flaw with 3D studio when + ** it renders 45 degree angles. + */ + static int const BodyShape[32]; +// static int const TurretShape[32]; +}; + +#endif diff --git a/TEMP.CPP b/TEMP.CPP new file mode 100644 index 0000000..052327f --- /dev/null +++ b/TEMP.CPP @@ -0,0 +1,41 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + + int hfile; + long length; + + hfile = _lopen(lpstrFileName, OF_READ | OF_SHARE_DENY_WRITE); + if (hfile == -1) { + return(0); + } + + hfile = _lopen(lpstrFileName, OF_READ | OF_SHARE_EXCLUSIVE); + if (hfile == -1) { + hfile = _lcreat(lpstrFileName, 0); + if (hfile == -1) { + return(0); + } + } + length = sizeof(MobileClass); + if (length != _lwrite(hfile, this, lenght)) { + _lclose(hfile); + return(0); + } + _lclose(hfile); + return(TRUE); +} \ No newline at end of file diff --git a/TEMPLATE.CPP b/TEMPLATE.CPP new file mode 100644 index 0000000..14d31d3 --- /dev/null +++ b/TEMPLATE.CPP @@ -0,0 +1,411 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\template.cpv 2.18 16 Oct 1995 16:51:46 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEMPLATE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 17, 1994 * + * * + * Last Update : January 23, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TemplateClass::As_Target -- Converts a template object into a target number. * + * TemplateClass::Init -- Resets the template object system. * + * TemplateClass::Mark -- Lifts or drops a template object. * + * TemplateClass::Read_INI -- Reads the scenario control INI file. * + * TemplateClass::Select -- Select the template object. * + * TemplateClass::TemplateClass -- Default constructor for template class objects. * + * TemplateClass::TemplateClass -- Template object constructor. * + * TemplateClass::Unlimbo -- Places a template object into the game/map system. * + * TemplateClass::Write_INI -- Writes the template objects to the INI file. * + * TemplateClass::delete -- Returns a template object to the pool. * + * TemplateClass::new -- Allocates a template object from pool * + * TemplateClass::Validate -- validates template pointer * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "template.h" + + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * TemplateClass::VTable; + + +/*********************************************************************************************** + * TemplateClass::Validate -- validates template pointer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int TemplateClass::Validate(void) const +{ + int num; + + num = Templates.ID(this); + if (num < 0 || num >= TEMPLATE_MAX) { + Validate_Error("TEMPLATE"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * TemplateClass::Read_INI -- Reads the scenario control INI file. * + * * + * This routine reads the scenario control INI file and creates all * + * templates specified therein. * + * * + * INPUT: buffer -- Pointer to the loaded scenario control INI file. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void TemplateClass::Read_INI(char *buffer) +{ + char *tbuffer; // Accumulation buffer of unit IDs. + int len; // Size of data in buffer. + CELL cell; // Cell of building. + char buf[128]; // Working string staging buffer. + + len = strlen(buffer) + 2; + tbuffer = buffer + len; + + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); + while (*tbuffer != '\0') { + TemplateType temp; // Terrain type. + + cell = atoi(tbuffer); + WWGetPrivateProfileString(INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, buffer); + temp = TemplateTypeClass::From_Name(strtok(buf, ",\r\n")); + if (temp != TEMPLATE_NONE) { + new TemplateClass(temp, cell); + } + tbuffer += strlen(tbuffer)+1; + } +} + + +/*********************************************************************************************** + * TemplateClass::Write_INI -- Writes the template objects to the INI file. * + * * + * This routine is used to write all the template objects out to the INI file specified. * + * It is used by the scenario editor when saving the game. * + * * + * INPUT: buffer -- Pointer to the INI file staging buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void TemplateClass::Write_INI(char *buffer) +{ + char uname[10]; + char buf[127]; + char *tbuffer; // Accumulation buffer of unit IDs. + + /* + ** First, clear out all existing template data from the ini file. + */ + tbuffer = buffer + strlen(buffer) + 2; + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-strlen(buffer), buffer); + while (*tbuffer != '\0') { + WWWritePrivateProfileString(INI_Name(), tbuffer, NULL, buffer); + tbuffer += strlen(tbuffer)+1; + } + + /* + ** Find all templates and write them to the file. + */ + for (int index = 0; index < MAP_CELL_TOTAL; index++) { + CellClass * ptr; + + ptr = &Map[index]; + if (ptr->TType != TEMPLATE_NONE && ptr->TIcon == 0) { + sprintf(uname, "%03d", index); + sprintf(buf, "%s", TemplateTypeClass::As_Reference(ptr->TType).IniName); + WWWritePrivateProfileString(INI_Name(), uname, buf, buffer); + } + } +} + + +/*********************************************************************************************** + * TemplateClass::TemplateClass -- Default constructor for template class objects. * + * * + * This is the default constructor for a template class object. This construction method * + * should NEVER be used by the game except as a consequence of declaring an array of * + * uninitialized template objects. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +TemplateClass::TemplateClass(void) : + Class(0) +{ +} + + +/*********************************************************************************************** + * TemplateClass::As_Target -- Converts a template object into a target number. * + * * + * This routine will convert a template object into a target number. Because templates * + * never exist as a template object in the game system, this routine will never be called. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 09/25/1994 JLB : Created. * + *=============================================================================================*/ +TARGET TemplateClass::As_Target(void) const +{ + Validate(); + return(Build_Target(KIND_TEMPLATE, Templates.ID(this))); +} + + +/*********************************************************************************************** + * TemplateClass::Init -- Resets the template object system. * + * * + * This routine resets the template object system. It is called * + * prior to loading a new scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void TemplateClass::Init(void) +{ + TemplateClass *ptr; + + Templates.Free_All(); + + ptr = new TemplateClass(); + VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0]; + delete ptr; +} + + +/*********************************************************************************************** + * TemplateClass::Mark -- Lifts or drops a template object. * + * * + * This routine handles placing or removing a template object. This * + * entails marking the map as appropriate and redisplaying affected * + * cells. * + * * + * INPUT: mark -- The marking operation to perform. * + * * + * OUTPUT: bool; Was the template successfully marked? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + * 12/23/1994 JLB : Examines low level legality before processing. * + *=============================================================================================*/ +bool TemplateClass::Mark(MarkType mark) +{ + Validate(); + static bool noup = false; + void const * iset = Class->Get_Image_Data(); + if (iset && ObjectClass::Mark(mark)) { + + void * map = Get_Icon_Set_Map(iset); + + for (int y = 0; y < Class->Height; y++) { + for (int x = 0; x < Class->Width; x++) { + CELL cell = Coord_Cell(Coord) + y*MAP_CELL_W + x; + if (Map.In_Radar(cell)) { + CellClass * cellptr = &Map[cell]; + int number = y*Class->Width + x; + + /* + ** Determine if this logical icon actually maps to a real icon. If no real + ** icon is associated with this logical position, then don't do any action + ** since none is required. + */ + char * mapptr = (char*)map; + bool real = (mapptr[number] != -1); + + if (real) { + /* + ** Lift the terrain object from the map. + */ + if (mark == MARK_UP && !noup) { + if (cellptr->TType == Class->Type && cellptr->TIcon == number) { + cellptr->TType = TEMPLATE_NONE; + cellptr->TIcon = 0; + } + } + + /* + ** Place the terrain object down. + */ + if (mark == MARK_DOWN) { + if (*this == TEMPLATE_CLEAR1) { + cellptr->TType = TEMPLATE_NONE; + cellptr->TIcon = 0; + } else { + cellptr->TType = Class->Type; + // cellptr->TIcon = real; + cellptr->TIcon = number; + } + } + + cellptr->Redraw_Objects(); + cellptr->Recalc_Attributes(); + } + } + } + } + + /* + ** When marking this template down onto the map, the map template numbers are update + ** but the template is removed from existence. Make sure that the deletion of the + ** template object doesn't also lift the template numbers up from the map. + */ + if (mark == MARK_DOWN) { + noup = true; + delete this; + noup = false; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TemplateClass::new -- Allocates a template object from pool * + * * + * This routine is used to allocate a template object from the * + * template object pool. * + * * + * INPUT: size -- The size of a template object (not used). * + * * + * OUTPUT: Returns with a pointer to an available template object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +void * TemplateClass::operator new(size_t ) +{ + void * ptr = Templates.Allocate(); + if (ptr) { + ((TemplateClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * TemplateClass::delete -- Returns a template object to the pool. * + * * + * This routine will return a template object to the template object * + * pool. A template so returned is available for allocation again. * + * * + * INPUT: ptr -- Pointer to the object to be returned. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +void TemplateClass::operator delete(void *ptr) +{ + if (ptr) { + ((TemplateClass *)ptr)->IsActive = false; + } + Templates.Free((TemplateClass *)ptr); +} + + +/*********************************************************************************************** + * TemplateClass::TemplateClass -- Template object constructor. * + * * + * This is the constructor for a template object. * + * * + * INPUT: type -- The template object this is to become. * + * * + * pos -- The position on the map to place the object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +TemplateClass::TemplateClass(TemplateType type, CELL pos) : + Class(&TemplateTypeClass::As_Reference(type)) +{ + if (pos != -1) { + Unlimbo(Cell_Coord(pos)); + } +} diff --git a/TEMPLATE.H b/TEMPLATE.H new file mode 100644 index 0000000..cd3eaaa --- /dev/null +++ b/TEMPLATE.H @@ -0,0 +1,120 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\template.h_v 2.18 16 Oct 1995 16:45:20 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEMPLATE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 17, 1994 * + * * + * Last Update : May 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TEMPLATE_H +#define TEMPLATE_H + +#include "object.h" +#include "type.h" + +/****************************************************************************** +** This class controls the template object. Template objects function congruously +** to carpet on a floor. They have no depth, but merely control the icon to be rendered +** as the cell's bottom most layer. +*/ +class TemplateClass : public ObjectClass +{ + public: + /*------------------------------------------------------------------- + ** Constructors and destructors. + */ + static void * operator new(size_t size); + static void operator delete(void *ptr); + TemplateClass(void); + TemplateClass(TemplateType type, CELL pos=-1); + virtual ~TemplateClass(void) {if (GameActive) TemplateClass::Limbo();}; + operator TemplateType(void) const {return Class->Type;}; + virtual RTTIType What_Am_I(void) const {return RTTI_TEMPLATE;}; + + static void Init(void); + + /* + ** Query functions. + */ + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + int Icon_Number(CELL cell); + + /* + ** Object entry and exit from the game system. + */ + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void Draw_It(int , int , WindowNumberType ) {}; + virtual bool Mark(MarkType mark); + + /* + ** User I/O. + */ + + /* + ** Combat related. + */ + virtual TARGET As_Target(void) const; + + /* + ** File I/O. + */ + static void Read_INI(char *buffer); + static void Write_INI(char *buffer); + static char *INI_Name(void) {return "TEMPLATE";}; + bool Load(FileClass & file); + bool Save(FileClass & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + private: + + /* + ** This is a pointer to the template object's class. + */ + TemplateTypeClass const * const Class; + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; +}; + +#endif diff --git a/TERRAIN.CPP b/TERRAIN.CPP new file mode 100644 index 0000000..4682951 --- /dev/null +++ b/TERRAIN.CPP @@ -0,0 +1,900 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\terrain.cpv 2.16 16 Oct 1995 16:51:44 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TERRAIN.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : May 8, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TerrainClass::AI -- Process the terrain object AI. * + * TerrainClass::As_Target -- Converts the terrain object into a target number. * + * TerrainClass::Can_Enter_Cell -- Determines if the terrain object can exist in the cell. * + * TerrainClass::Catch_Fire -- Catches the terrain object on fire. * + * TerrainClass::Center_Coord -- Fetches the center point coordinate for terrain object. * + * TerrainClass::Debug_Dump -- Displays the status of the terrain object. * + * TerrainClass::Draw_It -- Renders the terrain object at the location specified. * + * TerrainClass::Fire_Out -- Handles when fire has gone out. * + * TerrainClass::Heath_Ratio -- Determines the health ratio for the terrain object. * + * TerrainClass::Init -- Initialize the terrain object tracking system. * + * TerrainClass::Limbo -- Handles terrain specific limbo action. * + * TerrainClass::Mark -- Marks the terrain object on the map. * + * TerrainClass::Radar_Icon -- Fetches pointer to radar icon to use. * + * TerrainClass::Read_INI -- Reads terrain objects from INI file. * + * TerrainClass::Start_To_Crumble -- Initiates crumbling of terrain (tree) object. * + * TerrainClass::Take_Damage -- Damages the terrain object as specified. * + * TerrainClass::TerrainClass -- Constructor for a terrain class object. * + * TerrainClass::TerrainClass -- This is the constructor for a terrain object. * + * TerrainClass::Unlimbo -- Unlimbo terrain object onto the map. * + * TerrainClass::Write_INI -- Writes all terrain objects to the INI file. * + * TerrainClass::delete -- Deletes a terrain object. * + * TerrainClass::new -- Creates a new terrain object. * + * TerrainClass::~TerrainClass -- Default destructor for terrain class objects. * + * TerrainClass::Validate -- validates terrain pointer * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "terrain.h" + +#define BARNACLE_STAGE 22 +#define FIRST_SPORE_STAGE 30 +#define FIRST_SPORABLE_LEVEL 7 + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * TerrainClass::VTable; + + +/*********************************************************************************************** + * TerrainClass::Validate -- validates terrain pointer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int TerrainClass::Validate(void) const +{ + int num; + + num = Terrains.ID(this); + if (num < 0 || num >= TERRAIN_MAX) { + Validate_Error("TERRAIN"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * TerrainClass::~TerrainClass -- Default destructor for terrain class objects. * + * * + * This is the default destructor for terrain objects. It will remove the object from the * + * map and tracking systems, but only if the game is running. Otherwise, it does nothing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +TerrainClass::~TerrainClass(void) +{ + if (GameActive && Class) { + TerrainClass::Limbo(); + } +} + + +/*********************************************************************************************** + * TerrainClass::Take_Damage -- Damages the terrain object as specified. * + * * + * This routine is called when damage is to be inflicted upon the terrain object. It is * + * through this routine that terrain objects are attacked and thereby destroyed. Not all * + * terrain objects can be damaged by this routine however. * + * * + * INPUT: damage -- The damage points to inflict (raw). * + * * + * warhead -- The warhead type the indicates the kind of damage. This is used to * + * determine if the terrain object is damaged and if so, by how much. * + * * + * OUTPUT: bool; Was the terrain object destroyed by this damage? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 11/22/1994 JLB : Shares base damage handler for techno objects. * + * 12/11/1994 JLB : Shortens attached burning animations. * + *=============================================================================================*/ +ResultType TerrainClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source) +{ + Validate(); + ResultType res = RESULT_NONE; + + /* + ** Small arms can never destroy a terrain element. + */ + if ((!IsOnFire || warhead == WARHEAD_FIRE) && warhead != WARHEAD_SA && !Class->IsImmune) { + + res = ObjectClass::Take_Damage(damage, distance, warhead, source); + + if (damage && warhead == WARHEAD_FIRE) { + Catch_Fire(); + } + + /* + ** If the terrain object is destroyed by this damage, then only remove it if it + ** currently isn't on fire and isn't in the process of crumbling. + */ + if (res == RESULT_DESTROYED) { + + /* + ** Remove this terrain object from the targeting computers of all other + ** game objects. No use beating a dead horse. + */ + Detach_All(); + + if (IsOnFire) { + + /* + ** Attached flame animation should be shortened as much as possible so that + ** crumbling can begin soon. + */ + Shorten_Attached_Anims(this); + } else { + Start_To_Crumble(); + } + } + } + return(res); +} + + +/*********************************************************************************************** + * TerrainClass::As_Target -- Converts the terrain object into a target number. * + * * + * This routine will take the terrain object and generate a unique targeting number. This * + * number is used for the NavCom and TarCom of other units. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the target number of this terrain object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +TARGET TerrainClass::As_Target(void) const +{ + Validate(); + return(Build_Target(KIND_TERRAIN, Terrains.ID(this))); +} + + +/*********************************************************************************************** + * TerrainClass::new -- Creates a new terrain object. * + * * + * This routine creates a new terrain object by grabbing a free slot * + * out of the terrain object pool. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the terrain object allocated. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + *=============================================================================================*/ +void * TerrainClass::operator new(size_t) +{ + void * ptr = Terrains.Allocate(); + if (ptr) { + ((TerrainClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * TerrainClass::delete -- Deletes a terrain object. * + * * + * This routine deletes a terrain object by returning it to the * + * terrain object pool. * + * * + * INPUT: ptr -- Pointer to the terrain object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::operator delete(void *ptr) +{ + if (ptr) { + ((TerrainClass *)ptr)->IsActive = false; + } + Terrains.Free((TerrainClass *)ptr); +} + + +/*********************************************************************************************** + * TerrainClass::TerrainClass -- This is the constructor for a terrain object * + * * + * This constructor for a terrain object will initialize the terrain * + * object with it's proper type and insert it into the access * + * tracking system. * + * * + * INPUT: type -- The terrain object type. * + * * + * cell -- The location of the terrain object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + *=============================================================================================*/ +TerrainClass::TerrainClass(TerrainType type, CELL cell) : + Class(&TerrainTypeClass::As_Reference(type)) +{ + IsBlossoming = false; + IsBarnacled = false; + IsSporing = false; + IsCrumbling = false; + IsOnFire = false; + Strength = Class->MaxStrength; + if (cell != -1) { + if (!Unlimbo(Cell_Coord(cell))) { + delete this; + } + } + Set_Rate(0); // turn off animation +} + + +/*********************************************************************************************** + * TerrainClass::Mark -- Marks the terrain object on the map. * + * * + * This routine will mark or remove the terrain object from the map * + * tracking system. This is typically called when the terrain object * + * is first created, when it is destroyed, and whenever it needs to be * + * redrawn. * + * * + * INPUT: mark -- The marking operation to perform. * + * * + * OUTPUT: bool; Was the terrain object successfully marked? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + * 12/23/1994 JLB : Performs low level legality check before proceeding. * + *=============================================================================================*/ +bool TerrainClass::Mark(MarkType mark) +{ + Validate(); + if (ObjectClass::Mark(mark)) { + short const *overlap = Class->Overlap_List(); + short const *occupy = Class->Occupy_List(); + CELL cell = Coord_Cell(Coord); + + switch (mark) { + case MARK_UP: + Map.Pick_Up(cell, this); + break; + + case MARK_DOWN: + Map.Place_Down(cell, this); + break; + + default: + Map.Refresh_Cells(cell, overlap); + Map.Refresh_Cells(cell, occupy); + break; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TerrainClass::Draw_It -- Renders the terrain object at the location specified. * + * * + * This routine is used to render the terrain object at the location specified and * + * clipped to the window specified. This is the gruntwork drawing routine for the * + * terrain objects as they are displayed on the map. * + * * + * INPUT: x,y -- The coordinate to draw the terrain object at (centered). * + * * + * window -- The clipping window to draw to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1994 JLB : Created. * + * 11/09/1994 JLB : Changed selected terrain highlight method. * + *=============================================================================================*/ +void TerrainClass::Draw_It(int x, int y, WindowNumberType window) +{ + Validate(); + void const * shapedata; + + shapedata = Class->Get_Image_Data(); + if (shapedata) { + int shapenum = 0; + + /* + ** Determine the animation stage to render the terrain object. If it is crumbling, then + ** it will display the crumbling animation. + */ + if (IsCrumbling || Class->IsTransformable) { + shapenum = Fetch_Stage()+IsCrumbling; + } else { + if (Strength < 2) { + shapenum++; + } + } + + ShapeFlags_Type flags = SHAPE_NORMAL; + if (IsSelected && Debug_Map) flags = flags | SHAPE_FADING; + + IsTheaterShape = true; + CC_Draw_Shape(shapedata, shapenum, x, y, window, flags|SHAPE_WIN_REL|SHAPE_GHOST, Map.FadingLight, Map.UnitShadow); + IsTheaterShape = false; + } +} + + +/*********************************************************************************************** + * TerrainClass::Init -- Initialize the terrain object tracking system. * + * * + * This routine will clear out the terrain object system so that no terrain objects will * + * exists. It is called prior to loading or starting a scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Init(void) +{ + TerrainClass *ptr; + + Terrains.Free_All(); + + ptr = new TerrainClass(); + VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0]; + delete ptr; +} + + +/*********************************************************************************************** + * TerrainClass::Can_Enter_Cell -- Determines if the terrain object can exist in the cell. * + * * + * This routine will examine the cell specified and determine if the the terrain object * + * can legally exist there. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: If the terrain object can be placed in the cell specified, then a value less than * + * 256 will be returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 01/01/1995 JLB : Actually works now. * + *=============================================================================================*/ +MoveType TerrainClass::Can_Enter_Cell(CELL cell, FacingType) const +{ + Validate(); + short const *offset; // Pointer to cell offset list. + + if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO); + + offset = Occupy_List(); + while (*offset != REFRESH_EOL) { + if (!Map[(CELL)(cell + *offset++)].Is_Generally_Clear()) { + return(MOVE_NO); + } + } + return(MOVE_OK); +} + + +/*********************************************************************************************** + * TerrainClass::Catch_Fire -- Catches the terrain object on fire. * + * * + * This routine is called if the terrain object is supposed to catch on fire. The routine * + * performs checking to make sure that only flammable terrain objects that aren't already * + * on fire get caught on fire. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the terrain object caught on fire by this routine? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1994 JLB : Created. * + * 12/11/1994 JLB : Don't catch fire if already on fire or crumbling. * + *=============================================================================================*/ +bool TerrainClass::Catch_Fire(void) +{ + Validate(); + if (!IsCrumbling && !IsOnFire && Class->IsFlammable) { + AnimClass * anim = new AnimClass(ANIM_BURN_BIG, Coord_Add(Sort_Y(), 0xFFB00000L)); + if (anim) { + anim->Attach_To(this); + } + anim = new AnimClass(ANIM_BURN_MED, Coord_Add(Sort_Y(), 0xFF200000L), 15); + if (anim) { + anim->Attach_To(this); + } + IsOnFire = true; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TerrainClass::Fire_Out -- Handles when fire has gone out. * + * * + * When the fire has gone out on a burning terrain object, this routine is called. The * + * animation has already been terminated prior to calling this routine. All this routine * + * needs to perform is any necessary local flag updating. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Fire_Out(void) +{ + Validate(); + if (IsOnFire) { + IsOnFire = false; + if (!IsCrumbling && !Strength) { + Detach_All(); + Mark(); + Start_To_Crumble(); + new AnimClass(ANIM_SMOKE_M, Coord_Add(Coord, Class->CenterBase)); + } + } +} + + +/*********************************************************************************************** + * TerrainClass::AI -- Process the terrain object AI. * + * * + * This is used to handle any AI processing necessary for terrain objects. This might * + * include animation effects. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1994 JLB : Created. * + * 09/28/1994 JLB : Crumbling animation. * + *=============================================================================================*/ +void TerrainClass::AI(void) +{ + Validate(); + ObjectClass::AI(); + + if (StageClass::Graphic_Logic()) { + Mark(); + + /* + ** If the terrain object is in the process of crumbling, then when at the + ** last stage of the crumbling animation, delete the terrain object. + */ + if (IsCrumbling && Fetch_Stage() == Get_Build_Frame_Count(Class->Get_Image_Data())-1) { + delete this; + } + } + + /* + ** if this is a blossom tree, let's update it at this time + */ + if (Class->IsTransformable) { + // If it's already blossomed, is it at barnacled stage? + if (IsBlossoming) { + // if it's not barnacled yet, check if we're at that stage + if (!IsBarnacled) { + if (Fetch_Stage() == BARNACLE_STAGE) { + IsBarnacled = true; + Set_Rate(0); // turn off animation + } + } else { + /* + ** If it's barnacled, see if it's pulsing and spore-ing + */ + if (IsSporing) { + if (Fetch_Stage() >= Get_Build_Frame_Count(Class->Get_Image_Data())-1) { + Explosion_Damage(Sort_Y(), 5, NULL, WARHEAD_SPORE); + Set_Stage(FIRST_SPORE_STAGE); + if (Random() & 1) { + IsSporing = false; + StageClass::Set_Rate(0); + } + } + } else { + if (Random() == 255) { // is it time to start sporing? + IsSporing = true; + StageClass::Set_Rate(Options.Normalize_Delay(1)); + } + } + } + } else { + + // If it hasn't tried to blossom yet, can it do so now? + if (Random_Picky((int)1, (int)5000, (char*)NULL, (int)0) == 1) { + IsBlossoming = true; + StageClass::Set_Stage(1); + StageClass::Set_Rate(Options.Normalize_Delay(1)); + } + } + } +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * TerrainClass::Debug_Dump -- Displays the status of the terrain object. * + * * + * This debugging support routine is used to display the status of the terrain object to * + * the debug screen. * + * * + * INPUT: mono -- The mono screen to display the status to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Debug_Dump(MonoClass *mono) const +{ + Validate(); + ObjectClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * TerrainClass::Unlimbo -- Unlimbo terrain object onto the map. * + * * + * This routine is used to unlimbo the terrain object onto a location on the map. Normal * + * unlimbo procedures are sufficient except that the coordinate location of a terrain * + * object is based on the upper left corner of a cell rather than the center. Mask the * + * coordinate value so that it snaps to the upper left corner and then proceed with a * + * normal unlimbo process. * + * * + * INPUT: coord -- The coordinate to mark as the terrain's location. * + * * + * dir -- unused * + * * + * OUTPUT: bool; Was the terrain object successful in the unlimbo process? Failure could be * + * the result of illegal positioning. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1994 JLB : Created. * + * 11/16/1994 JLB : Checks for theater legality. * + *=============================================================================================*/ +bool TerrainClass::Unlimbo(COORDINATE coord, DirType dir) +{ + Validate(); + if (Class->Theater & (1 << Map.Theater)) { + return(ObjectClass::Unlimbo(coord, dir)); + } + return(false); +} + + +/*********************************************************************************************** + * TerrainClass::Start_To_Crumble -- Initiates crumbling of terrain (tree) object. * + * * + * This routine is used to start the crumbling process for terrain object. This only * + * applies to trees. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Start_To_Crumble(void) +{ + Validate(); + if (!IsCrumbling) { + IsCrumbling = true; + Set_Rate(2); + Set_Stage(0); + } +} + + +/*********************************************************************************************** + * TerrainClass::Limbo -- Handles terrain specific limbo action. * + * * + * This routine (called as a part of the limbo process) will remove the terrain occupation * + * flag in the cell it occupies. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the terrain object unlimboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool TerrainClass::Limbo(void) +{ + Validate(); + if (!IsInLimbo) { + CELL cell = Coord_Cell(Coord); + Map[cell].Flag.Occupy.Monolith = false; + } + return(ObjectClass::Limbo()); +} + + +/*********************************************************************************************** + * TerrainClass::Center_Coord -- Fetches the center point coordinate for terrain object. * + * * + * Use this routine to fetch the center point terrain * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE TerrainClass::Center_Coord(void) const +{ + Validate(); + return(Coord_Add(Coord, Class->CenterBase)); +} + + +/*********************************************************************************************** + * TerrainClass::TerrainClass -- Constructor for a terrain class object. * + * * + * This is the default constructor for a terrain class object. It basically initializes * + * the object to a null -- do nothing -- state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +TerrainClass::TerrainClass(void) : + Class(0) +{ + IsOnFire = false; + IsCrumbling = false; + IsBlossoming = false; + IsBarnacled = false; + IsSporing = false; + Strength = 0; +} + + +/*********************************************************************************************** + * TerrainClass::Radar_Icon -- Fetches pointer to radar icon to use. * + * * + * This routine will return with a pointer to the radar icon to use for the cell number * + * specified. * + * * + * INPUT: cell -- The cell number to use when determine what icon pointer to return. * + * * + * OUTPUT: Returns with a pointer to the 9 pixel values that make up the icon of this * + * terrain object located at the cell specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +unsigned char * TerrainClass::Radar_Icon(CELL cell) +{ + Validate(); + unsigned char *icon = (unsigned char *)Class->Get_Radar_Data(); // get a pointer to radar icons + int width = *icon++; // extract the width from data + int height = *icon++; // extract the width from data + + /* + ** Icon number that we need can be found by converting the cell and base + ** cell to and x and y offset from the upper left of the cell, and then + ** multiplying it by the width of the terrain in icons, which we + ** conveniantly stored out as the first byte of every icon we made. + */ + int basecell = Coord_Cell(Coord); // find the base cell of terrain + int ydiff = Cell_Y(cell) - Cell_Y(basecell); + int xdiff = Cell_X(cell) - Cell_X(basecell); + if (xdiff < width && ydiff < height) { + int iconnum = (ydiff * width) + xdiff; + return(icon + (iconnum * 9)); + } + return(NULL); +} + + +/*********************************************************************************************** + * TerrainClass::Read_INI -- Reads terrain objects from INI file. * + * * + * This routine reads a scenario control INI file and creates all * + * terrain objects specified therein. Objects so created are placed * + * upon the map. * + * * + * INI entry format: * + * cellnum = TypeName, Triggername * + * * + * INPUT: buffer -- Pointer to the loaded scenario INI file data. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Read_INI(char *buffer) +{ + char *tbuffer; // Accumulation buffer of unit IDs. + int len; // Size of data in buffer. + char buf[128]; + TerrainClass * tptr; + + len = strlen(buffer) + 2; + tbuffer = buffer + len; + + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); + while (*tbuffer != '\0') { + TerrainType terrain; // Terrain type. + CELL cell; + + cell = atoi(tbuffer); + WWGetPrivateProfileString(INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, buffer); + terrain = TerrainTypeClass::From_Name(strtok(buf, ",")); + if (terrain != TERRAIN_NONE) { + tptr = new TerrainClass(terrain, cell); + tptr->Trigger = TriggerClass::As_Pointer(strtok(NULL,",")); + if (tptr->Trigger) + tptr->Trigger->AttachCount++; + } + tbuffer += strlen(tbuffer)+1; + } +} + + +/*********************************************************************************************** + * TerrainClass::Write_INI -- Writes all terrain objects to the INI file. * + * * + * This routine is used to write all the terrain objects out to the INI file specified. * + * It is used by the scenario editor to write out the map data. * + * * + * INI entry format: * + * cellnum = TypeName, Triggername * + * * + * INPUT: buffer -- Pointer to the INI file staging area. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Write_INI(char *buffer) +{ + int index; + char uname[10]; + char buf[127]; + char *tbuffer; // Accumulation buffer of unit IDs. + + /* + ** First, clear out all existing terrain data from the ini file. + */ + tbuffer = buffer + strlen(buffer) + 2; + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-strlen(buffer), buffer); + while (*tbuffer != '\0') { + WWWritePrivateProfileString(INI_Name(), tbuffer, NULL, buffer); + tbuffer += strlen(tbuffer)+1; + } + + /* + ** Write the terrain data out. + */ + for (index = 0; index < Terrains.Count(); index++) { + TerrainClass * terrain; + + terrain = Terrains.Ptr(index); + if (!terrain->IsInLimbo && terrain->IsActive) { + + sprintf(uname, "%d", Coord_Cell(terrain->Coord)); + sprintf(buf, "%s,%s", + terrain->Class->IniName, + terrain->Trigger ? terrain->Trigger->Get_Name() : "None" ); + WWWritePrivateProfileString(INI_Name(), uname, buf, buffer); + } + } +} diff --git a/TERRAIN.H b/TERRAIN.H new file mode 100644 index 0000000..a4db80b --- /dev/null +++ b/TERRAIN.H @@ -0,0 +1,177 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\terrain.h_v 2.16 16 Oct 1995 16:47:48 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TERRAIN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : April 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TERRAIN_H +#define TERRAIN_H + +#include "object.h" +#include "type.h" + + +/**************************************************************************** +** Each type of terrain has certain pieces of static information associated +** with it. This class elaborates this data. +*/ +class TerrainClass : public ObjectClass, public StageClass +{ + public: + TerrainTypeClass const * const Class; + operator TerrainType(void) const {return Class->Type;}; + + /* + ** Constructor for terrain object class. + */ + static void * TerrainClass::operator new(size_t size); + static void TerrainClass::operator delete(void *ptr); + TerrainClass(void); + TerrainClass(TerrainType id, CELL cell); + virtual ~TerrainClass(void); + virtual RTTIType What_Am_I(void) const {return RTTI_TERRAIN;}; + + static void Init(void); + + /* + ** Terrain specific support functions. + */ + void Start_To_Crumble(void); + + /* + ** Query functions. + */ + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Center_Coord(void) const; + virtual COORDINATE Render_Coord(void) const {return Coord;}; + virtual COORDINATE Sort_Y(void) const {return Coord_Add(Coord, Class->CenterBase);}; + virtual COORDINATE Target_Coord(void) const {return Sort_Y();}; + + /* + ** Object entry and exit from the game system. + */ + virtual bool Unlimbo(COORDINATE coord, DirType dir=DIR_N); + virtual bool Limbo(void); + virtual MoveType Can_Enter_Cell(CELL cell, FacingType facing = FACING_NONE) const; + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void Draw_It(int x, int y, WindowNumberType window); + virtual bool Mark(MarkType mark=MARK_CHANGE); + unsigned char *Radar_Icon(CELL cell); + + /* + ** User I/O. + */ + virtual void Clicked_As_Target(int) {}; + + /* + ** Combat related. + */ + virtual void Fire_Out(void); + virtual bool Catch_Fire(void); + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source); + virtual TARGET As_Target(void) const; + + /* + ** AI. + */ + virtual void AI(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** File I/O. + */ + static void Read_INI(char *buffer); + static void Write_INI(char *buffer); + static char *INI_Name(void) {return "TERRAIN";}; + bool Load(FileClass & file); + bool Save(FileClass & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + private: + + /* + ** If this terrain object is on fire, then this flag will be true. + */ + unsigned IsOnFire:1; + + /* + ** Is this a terrain object that undergoes crumbling animation and it is + ** in fact crumbling at this time? + */ + unsigned IsCrumbling:1; + + /* + ** If this is a tree that becomes a blossom tree, is it currently doing so? + */ + unsigned IsBlossoming:1; + + /* + ** If this is a blossom tree, is it barnacled? + */ + unsigned IsBarnacled:1; + + /* + ** If this is a blossom tree that is barnacled, is it pulsing and spewing + ** out spores? + */ + unsigned IsSporing:1; + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; +}; + +#endif diff --git a/TEXTBLIT.H b/TEXTBLIT.H new file mode 100644 index 0000000..907daab --- /dev/null +++ b/TEXTBLIT.H @@ -0,0 +1,53 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + + +#define MAX_ENTRIES 128 + +class TextBlitClass { + + public: + + TextBlitClass(void); + ~TextBlitClass(void){}; + + void Add (int x, int y, int dx, int dy, int w, int h); + void Clear (void); + void Update (void); + + + private: + + typedef struct { + int SourceX; + int SourceY; + int DestX; + int DestY; + int Width; + int Height; + } BlitEntryType; + + BlitEntryType BlitListo [MAX_ENTRIES]; + int Count; + +}; + + +extern GraphicBufferClass *TextPrintBuffer; +extern TextBlitClass BlitList; + diff --git a/TEXTBTN.CPP b/TEXTBTN.CPP new file mode 100644 index 0000000..f875feb --- /dev/null +++ b/TEXTBTN.CPP @@ -0,0 +1,377 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\textbtn.cpv 2.18 16 Oct 1995 16:49:16 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEXTBTN.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 19, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TextButtonClass::Draw_Background -- Draws the background to the text button. * + * TextButtonClass::Draw_Me -- Draws the text buttons as indicated. * + * TextButtonClass::Draw_Text -- This draws the text for the text button. * + * TextButtonClass::Set_Text -- Assigns a new text string to this button. * + * TextButtonClass::Set_Text -- Sets the text for this text button. * + * TextButtonClass::TextButtonClass -- Normal constructor for a text button. * + * TextButtonClass::TextButtonClass -- Normal constructor for a text button. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "textbtn.h" + + +/*********************************************************************************************** + * TextButtonClass::TextButtonClass -- Normal constructor for a text button. * + * * + * This is the constructor for text buttons if the text is provided as a string pointer. * + * * + * INPUT: id -- The ID number to assign to this button. * + * text -- Pointer to the text string to display on top of the button. * + * x,y -- Pixel coordinate of button's upper left corner. * + * w,h -- Dimensions of the button. If these are not filled in (or with -1), then * + * the dimensions are adapted to fit the text assigned to the button. * + * style -- The print style for the text in the button. These are the TPF_ flags * + * used by Fancy_Text_Print(). * + * border-- If the button is to be surrounded by a black border, then this flag * + * should be set to true. * + * * + * OUTPUT: none * + * WARNINGS: Call Set_Text & Set_Style, & init X,Y,Width,Height,ID before using this button. * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +TextButtonClass::TextButtonClass(unsigned id, char const * text, TextPrintType style, int x, int y, int w, int h, int blackborder) : + ToggleClass(id, x, y, w, h), + String(text) +{ + PrintFlags = style; + IsBlackBorder = blackborder; + + if (w == -1 || h == -1) { + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, PrintFlags); + if (w == -1) { + Width = String_Pixel_Width(String)+8; +// if (SeenBuff.Get_Width() != 320) Width *= 2; + } + if (h == -1) { + Height = FontHeight + FontYSpacing + 2; +// if (SeenBuff.Get_Height() != 200) Height *= 2; + } + } +} + + +/*********************************************************************************************** + * TextButtonClass::TextButtonClass -- Default constructor for a text button. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +TextButtonClass::TextButtonClass(void) : + ToggleClass(0, 0, 0, 0, 0) +{ + X = Y = 0; + Width = Height = 0; + IsBlackBorder = 0; + String = 0; + PrintFlags = TPF_8POINT; +} + + +/*********************************************************************************************** + * TextButtonClass::TextButtonClass -- Normal constructor for a text button. * + * * + * This is the constructor for text buttons if the text is provided as a string pointer. * + * * + * INPUT: id -- The ID number to assign to this button. * + * * + * text -- The text number to use for displaying on top of the button. * + * * + * x,y -- Pixel coordinate of button's upper left corner. * + * * + * w,h -- Dimensions of the button. If these are not filled in (or with -1), then * + * the dimensions are adapted to fit the text assigned to the button. * + * * + * * + * style -- The print style for the text in the button. These are the TPF_ flags * + * used by Fancy_Text_Print(). * + * * + * border-- If the button is to be surrounded by a black border, then this flag * + * should be set to true. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +TextButtonClass::TextButtonClass (unsigned id, int text, TextPrintType style, int x, int y, int w, int h, int blackborder) : + ToggleClass (id, x, y, w, h), + String(0) +{ + PrintFlags = style; + IsBlackBorder = blackborder; + Set_Text(text); + + if (w == -1 || h == -1) { + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, PrintFlags); + if (w == -1) { + Width = String_Pixel_Width(String)+8; +// if (SeenBuff.Get_Width() != 320) Width *= 2; + } + if (h == -1) { + Height = FontHeight + FontYSpacing + 2; +// if (SeenBuff.Get_Height() != 200) Height *= 2; + } + } +} + + +/*********************************************************************************************** + * TextButtonClass::Draw_Me -- Draws the text buttons as indicated. * + * * + * This routine will draw the text button. * + * * + * INPUT: forced -- If the button is to be redrawn regardless of the state of the redraw * + * flag, then this parameter will be true. * + * * + * OUTPUT: bool; Was the button redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + * 01/16/1995 JLB : Modified * + *=============================================================================================*/ +int TextButtonClass::Draw_Me(int forced) +{ + if (ControlClass::Draw_Me(forced)) { + /* + ** Hide the mouse. + */ + if (LogicPage == &SeenBuff) { + //Conditional_Hide_Mouse(X, Y, X+Width-1, Y+Height-1); + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + /* + ** Draw the background and overlaying text. These are virtual function + ** calls so that they may be overridden. + */ + Draw_Background(); + Draw_Text(String); + + /* + ** Display the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TextButtonClass::Set_Text -- Assigns a new text string to this button. * + * * + * Use this routine to assign a new text string to this button. By using this function it * + * is possible to dynmaically change the button's text. An example of this would be an * + * on/off button that every time it is clicked, the text toggles between "on" and "off". * + * * + * INPUT: text -- Pointer to the text string to assign to this button. * + * * + * OUTPUT: none * + * * + * WARNINGS: The text is NOT copied to this button. You must make sure that the text * + * remains valid throughout the lifetime of this text button. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void TextButtonClass::Set_Text(char const * text, bool resize) +{ + String = text; + Flag_To_Redraw(); + if (resize && String) { + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, PrintFlags); + Width = String_Pixel_Width(String)+8; + Height = FontHeight + FontYSpacing + 2; + } +} + + +/*********************************************************************************************** + * TextButtonClass::Set_Text -- Sets the text for this text button. * + * * + * This will set the text for this button. The text is provided as a text number. This * + * number is automatically converted to the appropriate text string before being stored * + * in the button's structure. * + * * + * INPUT: text -- The text number to assign to this button. * + * * + * OUTPUT: none * + * * + * WARNINGS: The text number information is lost when it is assigned to the button. Once * + * the assignment takes place, the text number is NOT remembered by the button. * + * Only the associated text string is. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void TextButtonClass::Set_Text(int text, bool resize) +{ + if (text != TXT_NONE) { + Set_Text(Text_String(text), resize); + } +} + + +/*********************************************************************************************** + * TextButtonClass::Draw_Background -- Draws the background to the text button. * + * * + * This localizes the drawing of the background for the text button. By overriding this * + * function you can give a different background to the button. The text is drawn using * + * a different routine. The mouse is hidden, if necessary, before this routine is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void TextButtonClass::Draw_Background(void) +{ + /* + ** Draw a border if selected style. + */ + if (IsBlackBorder) { + LogicPage->Draw_Rect (X-1, Y-1, X+Width+2, Y+Height+2, BLACK); + } + + /* + ** Draw the body & set text color. + */ + BoxStyleEnum style; + //if (FontPtr == GradFont6Ptr) { + if (PrintFlags & TPF_6PT_GRAD) { + if (IsDisabled) { + style = BOXSTYLE_GREEN_DIS_RAISED; + } else { + if (IsPressed) { + style = BOXSTYLE_GREEN_DOWN; + } else { + style = BOXSTYLE_GREEN_RAISED; + } + } + } else { + if (IsDisabled) { + style = BOXSTYLE_DIS_RAISED; + } else { + if (IsPressed) { + style = BOXSTYLE_DOWN; + } else { + style = BOXSTYLE_RAISED; + } + } + } + Draw_Box(X, Y, Width, Height, style, true); +} + + +/*********************************************************************************************** + * TextButtonClass::Draw_Text -- This draws the text for the text button. * + * * + * This routine draws the text for the text button. You can override this routine if you * + * wish different text rendering styles or colors. The background has already been drawn * + * by the time this function is called. The mouse is hidden, if necessary, as well. * + * * + * INPUT: text -- Pointer to the text string to print over the button. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void TextButtonClass::Draw_Text(char const * text) +{ + /* + ** Display the text. + */ + if (String) { + int color; + //if (FontPtr == GradFont6Ptr) { + if (PrintFlags & TPF_6PT_GRAD) { + TextPrintType flags; + + color = CC_GREEN; + + if (IsDisabled) { + flags = (TextPrintType)0; + } else { + if (IsPressed || IsOn) { + flags = TPF_USE_GRAD_PAL|TPF_BRIGHT_COLOR; + } else { + flags = TPF_USE_GRAD_PAL|TPF_MEDIUM_COLOR; + } + } + + Fancy_Text_Print(text, X+(Width>>1)-1, Y+1, color, TBLACK, PrintFlags|flags|TPF_CENTER); + } else { + if (IsDisabled) { +// color = DKGREY; + color = LTGREY; + } else { + if (IsPressed) { + if (PrintFlags & TPF_NOSHADOW) { + color = DKGREY; + } else { + color = LTGREY; + } + } else { + color = WHITE; + } + } + + Fancy_Text_Print(text, X+(Width>>1)-1, Y+1, IsOn ? RED : color, TBLACK, PrintFlags|TPF_CENTER); + } + + } +} diff --git a/TEXTBTN.H b/TEXTBTN.H new file mode 100644 index 0000000..e18318a --- /dev/null +++ b/TEXTBTN.H @@ -0,0 +1,74 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\textbtn.h_v 2.18 16 Oct 1995 16:46:54 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEXTBTN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TEXTBTN_H +#define TEXTBTN_H + +#include "toggle.h" + + +class TextButtonClass : public ToggleClass +{ + public: + TextButtonClass(void); + TextButtonClass(unsigned id, char const * text, TextPrintType style, int x, int y, int w=-1, int h=-1, int blackborder=false); + TextButtonClass(unsigned id, int text, TextPrintType style, int x, int y, int w=-1, int h=-1, int blackborder=false); + virtual int Draw_Me(int forced=false); + virtual void Set_Text(char const *text, bool resize = false); + virtual void Set_Text(int text, bool resize = false); + virtual void Set_Style (TextPrintType style) {PrintFlags = style;} + + protected: + + virtual void Draw_Background(void); + virtual void Draw_Text(char const * text); + + unsigned IsBlackBorder:1; + + /* + ** This points to a constant string that is used for the button's text. + */ + char const * String; + + /* + ** This is the print flags to use when rendering this button's text. + */ + TextPrintType PrintFlags; +}; + +#endif + diff --git a/THEME.CPP b/THEME.CPP new file mode 100644 index 0000000..16756cf --- /dev/null +++ b/THEME.CPP @@ -0,0 +1,562 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\theme.cpv 2.18 16 Oct 1995 16:51:10 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : THEME.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 14, 1994 * + * * + * Last Update : May 29, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ThemeClass::Scan -- Scans all scores for availability. * + * ThemeClass::AI -- Process the theme engine and restart songs. * + * ThemeClass::Base_Name -- Fetches the base filename for the theme specified. * + * ThemeClass::From_Name -- Determines theme number from specified name. * + * ThemeClass::Full_Name -- Retrieves the full score name. * + * ThemeClass::Is_Allowed -- Checks to see if the specified theme is legal. * + * ThemeClass::Next_Song -- Calculates the next song number to play. * + * ThemeClass::Play_Song -- Starts the specified song play NOW. * + * ThemeClass::Queue_Song -- Queues the song to the play queue. * + * ThemeClass::Still_Playing -- Determines if music is still playing. * + * ThemeClass::Stop -- Stops the current theme from playing. * + * ThemeClass::ThemeClass -- Default constructor for the theme manager class. * + * ThemeClass::Theme_File_Name -- Constructs a filename for the specified theme. * + * ThemeClass::Track_Length -- Caclulates the length of the song (in seconds). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#include "theme.h" + + +/* +** These are the actual filename list for the theme sample files. +*/ +ThemeClass::ThemeControl ThemeClass::_themes[THEME_COUNT] = { + {"AIRSTRIK", TXT_THEME_AIRSTRIKE, 0, 200, false, false,false,true}, + {"80MX226M", TXT_THEME_80MX, 0, 248, false, false,false,true}, + {"CHRG226M", TXT_THEME_CHRG, 0, 256, true, false,false,true}, + {"CREP226M", TXT_THEME_CREP, 0, 222, true, false,false,true}, + {"DRIL226M", TXT_THEME_DRIL, 0, 272, true, false,false,true}, + {"DRON226M", TXT_THEME_DRON, 0, 275, true, false,false,true}, + {"FIST226M", TXT_THEME_FIST, 0, 212, true, false,false,true}, + {"RECN226M", TXT_THEME_RECON, 0, 261, true, false,false,true}, + {"VOIC226M", TXT_THEME_VOICE, 0, 306, true, false,false,true}, + {"HEAVYG", TXT_THEME_HEAVYG, 0, 180, true, false,false,true}, + {"J1", TXT_THEME_J1, 4, 187, true, false,false,true}, +// {"J1", TXT_THEME_J1, 4, 187, false, false,false,true}, + {"JDI_V2", TXT_THEME_JDI_V2, 5, 183, true, false,false,true}, + {"RADIO", TXT_THEME_RADIO, 6, 183, true, false,false,true}, + {"RAIN", TXT_THEME_RAIN, 7, 156, true, false,false,true}, + {"AOI", TXT_THEME_AOI, 0, 168, true, true, false,true}, + {"CCTHANG", TXT_THEME_CCTHANG, 12, 193, true, false,false,true}, + {"DIE", TXT_THEME_DIE, 11, 162, false, false,false,true}, + {"FWP", TXT_THEME_FWP, 10, 53, true, false,false,true}, + {"IND", TXT_THEME_IND, 1, 175, true, false,false,true}, + {"IND2", TXT_THEME_IND2, 1, 38, true, false,false,true}, + {"JUSTDOIT", TXT_THEME_JUSTDOIT, 9, 142, true, false,false,true}, + {"LINEFIRE", TXT_THEME_LINEFIRE, 8, 125, true, false,false,true}, + {"MARCH", TXT_THEME_MARCH, 7, 157, true, false,false,true}, + {"TARGET", TXT_THEME_TARGET, 0, 173, true, false,false,true}, + {"NOMERCY", TXT_THEME_NOMERCY, 2, 204, true, false,false,true}, + {"OTP", TXT_THEME_OTP, 3, 182, true, false,false,true}, + {"PRP", TXT_THEME_PRP, 4, 211, true, false,false,true}, + {"ROUT", TXT_THEME_ROUT, 12, 121, false, true, false,true}, + {"HEART", TXT_THEME_HEART, 5, 206, false, true, false,true}, + {"STOPTHEM", TXT_THEME_STOPTHEM, 0, 190, true, false,false,true}, + {"TROUBLE", TXT_THEME_TROUBLE, 6, 191, true, true, false,true}, + {"WARFARE", TXT_THEME_WARFARE, 0, 182, true, false,false,true}, + {"BEFEARED", TXT_THEME_BEFEARED, 13, 164, false, true, false,true}, + {"I_AM", TXT_THEME_IAM, 6, 161, false, false,false,true}, + {"WIN1", TXT_THEME_WIN1, 0, 41, false, true, true,true}, + {"MAP1", TXT_THEME_WIN1, 0, 61, false, false,true,true}, + {"VALKYRIE", TXT_THEME_VALK, 0, 306, false, false,true,true}, +}; + + +/*********************************************************************************************** + * ThemeClass::Base_Name -- Fetches the base filename for the theme specified. * + * * + * This routine is used to retrieve a pointer to the base filename for the theme * + * specified. * + * * + * INPUT: theme -- The theme number to convert into a base filename. * + * * + * OUTPUT: Returns with a pointer to the base filename for the theme specified. If the * + * theme number is invalid, then a pointer to "No Theme" is returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/29/1995 JLB : Created. * + *=============================================================================================*/ +char const * ThemeClass::Base_Name(ThemeType theme) const +{ + if (theme != THEME_NONE) { + return(_themes[theme].Name); + } + return("No theme"); +} + + +/*********************************************************************************************** + * ThemeClass::ThemeClass -- Default constructor for the theme manager class. * + * * + * This is the default constructor for the theme class object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +ThemeClass::ThemeClass(void) +{ + Current = -1; + Score = THEME_NONE; + Pending = THEME_NONE; +} + + +/*********************************************************************************************** + * ThemeClass::Full_Name -- Retrieves the full score name. * + * * + * This routine will fetch and return with a pointer to the full name of the theme * + * specified. * + * * + * INPUT: theme -- The theme to fetch the full name for. * + * * + * OUTPUT: Returns with a pointer to the full name for this score. This pointer may point to * + * EMS memory. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +char const * ThemeClass::Full_Name(ThemeType theme) const +{ + if (theme != THEME_NONE) { + return(Text_String(_themes[theme].Fullname)); + } + return(NULL); +} + + +/*********************************************************************************************** + * ThemeClass::AI -- Process the theme engine and restart songs. * + * * + * This is a maintenance function that will restart an appropriate theme if the current one * + * has finished. This routine should be called frequently. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + * 01/23/1995 JLB : Picks new song just as it is about to play it. * + *=============================================================================================*/ +void ThemeClass::AI(void) +{ + if (SampleType && !Debug_Quiet) { + if (ScoresPresent && Options.ScoreVolume && !Still_Playing() && Pending != THEME_NONE) { + + /* + ** If the pending song needs to be picked, then pick it now. + */ + if (Pending == THEME_PICK_ANOTHER) { + Pending = Next_Song(Score); + } + + /* + ** Start the song playing and then flag it so that a new song will + ** be picked when this one ends. + */ + Play_Song(Pending); + Pending = THEME_PICK_ANOTHER; + } + Sound_Callback(); + } +} + + +/*********************************************************************************************** + * ThemeClass::Next_Song -- Calculates the next song number to play. * + * * + * use this routine to figure out what song number to play. It examines the option settings * + * for repeat and shuffle so that it can return the correct value. * + * * + * INPUT: theme -- The origin (last) index. The new value is related to this for all but * + * the shuffling method of play. * + * * + * OUTPUT: Returns with the song number for the next song to play. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + * 01/19/1995 JLB : Will not play the same song twice when in shuffle mode. * + *=============================================================================================*/ +ThemeType ThemeClass::Next_Song(ThemeType theme) +{ + if (theme == THEME_NONE) { + theme = Next_Song(THEME_PICK_ANOTHER); + } else { + if (theme == THEME_PICK_ANOTHER || (!_themes[theme].Repeat && !Options.IsScoreRepeat)) { + if (Options.IsScoreShuffle) { + + /* + ** Shuffle the theme, but never pick the same theme that was just + ** playing. + */ + ThemeType newtheme; + do { + newtheme = Sim_Random_Pick(THEME_FIRST, THEME_LAST); + } while(newtheme == theme || !Is_Allowed(newtheme)); + theme = newtheme; + + } else { + + /* + ** Sequential score playing. + */ + do { + theme++; + if (theme > THEME_LAST) { + theme = THEME_FIRST; + } + } while(!Is_Allowed(theme)); + } + } + } + return(theme); +} + + +/*********************************************************************************************** + * ThemeClass::Queue_Song -- Queues the song to the play queue. * + * * + * This routine will cause the current song to fade and the specified song to start. This * + * is the normal and friendly method of changing the current song. * + * * + * INPUT: theme -- The song to start playing. If -1 is pssed in, then just the current song * + * is faded. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void ThemeClass::Queue_Song(ThemeType theme) +{ + if (ScoresPresent && SampleType && !Debug_Quiet && (Pending == THEME_NONE || Pending == THEME_PICK_ANOTHER)) { + if (!Options.ScoreVolume && theme != THEME_NONE) return; + + Pending = theme; + Fade_Sample(Current, THEME_DELAY); + } +} + + +/*********************************************************************************************** + * ThemeClass::Play_Song -- Starts the specified song play NOW. * + * * + * This routine is used to start the specified theme playing right now. If there is already * + * a theme playing, it is cut short so that this one may start. * + * * + * INPUT: theme -- The theme number to start playing. * + * * + * OUTPUT: Returns with the sample play handle. * + * * + * WARNINGS: This cuts off any current song in a abrubt manner. Only use this routine when * + * necessary. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ThemeClass::Play_Song(ThemeType theme) +{ + if (ScoresPresent && SampleType && !Debug_Quiet && Options.ScoreVolume) { + Stop(); + Score = theme; + if (theme >= THEME_FIRST) { + +#ifdef DEMO + if (_themes[theme].Scenario != 99) { + CCFileClass file(Theme_File_Name(theme)); + if (file.Is_Available()) { + Current = File_Stream_Sample_Vol(Theme_File_Name(theme), 0xFF, true); + } else { + Current = -1; + } + } else { + Current = -1; + } +#else + Current = File_Stream_Sample_Vol(Theme_File_Name(theme), 0xFF, true); +#endif + } + } + return(Current); +} + + +/*********************************************************************************************** + * ThemeClass::Theme_File_Name -- Constructs a filename for the specified theme. * + * * + * This routine will construct (into a static buffer) a filename that matches the theme * + * number specified. This constructed filename is returned as a pointer. The filename will * + * remain valid until the next call to this routine. * + * * + * INPUT: theme -- The theme number to convert to a filename. * + * * + * OUTPUT: Returns with a pointer to the constructed filename for the specified theme number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + * 05/09/1995 JLB : Theme variation support. * + *=============================================================================================*/ +char const * ThemeClass::Theme_File_Name(ThemeType theme) +{ + static char name[_MAX_FNAME+_MAX_EXT]; + + if (_themes[theme].Variation && Special.IsVariation) { + _makepath(name, NULL, NULL, _themes[theme].Name, ".VAR"); + CCFileClass file(name); + if (file.Is_Available()) { + return((char const *)(&name[0])); + } + } + _makepath(name, NULL, NULL, _themes[theme].Name, ".AUD"); + return((char const *)(&name[0])); +} + + +/*********************************************************************************************** + * ThemeClass::Track_Length -- Caclulates the length of the song (in seconds). * + * * + * Use this routine to calculate the length of the song. The length is determined by * + * reading the header of the song and dividing the sample rate into the sample length. * + * * + * INPUT: theme -- The song number to examine to find its length. * + * * + * OUTPUT: Returns with the length of the specified theme. This length is in the form of * + * seconds. * + * * + * WARNINGS: This routine goes to disk to fetch this information. Don't call frivolously. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ThemeClass::Track_Length(ThemeType theme) +{ + if ((unsigned)theme < THEME_COUNT) { + return(_themes[theme].Duration); + } + return(0); +} + + +/*********************************************************************************************** + * ThemeClass::Stop -- Stops the current theme from playing. * + * * + * Use this routine to stop the current theme. After this routine is called, no more music * + * will play until the Start() function is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void ThemeClass::Stop(void) +{ + if (ScoresPresent && SampleType && !Debug_Quiet) { + if (Current != -1) { + Stop_Sample(Current); + Current = -1; + Score = THEME_NONE; + Pending = THEME_NONE; + } + } +} + + +/*********************************************************************************************** + * ThemeClass::Still_Playing -- Determines if music is still playing. * + * * + * Use this routine to determine if music is still playing. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the music still audible? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + *=============================================================================================*/ +int ThemeClass::Still_Playing(void) +{ + if (ScoresPresent && SampleType && Current != -1 && !Debug_Quiet) { + return(Sample_Status(Current)); + } + return(false); +} + + +/*********************************************************************************************** + * ThemeClass::Is_Allowed -- Checks to see if the specified theme is legal. * + * * + * Use this routine to determine if a theme is allowed to be played. A theme is not allowed * + * if the scenario is too early for that score, or the score only is allowed in special * + * cases. * + * * + * INPUT: index -- The score the check to see if it is allowed to play. * + * * + * OUTPUT: Is the specified score allowed to play in the normal score playlist? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/09/1995 JLB : Created. * + *=============================================================================================*/ +bool ThemeClass::Is_Allowed(ThemeType index) const +{ +#ifdef DEMO + char buffer[128]; + + sprintf(buffer, "%s.AUD", Base_Name(index)); + CCFileClass file(buffer); + if (_themes[index].Scenario == 99 || !file.Is_Available()) { + _themes[index].Scenario = 99; + return(false); + } +#endif + + return( + _themes[index].Available && + (_themes[index].Normal || +// (index == THEME_MAP1 && ScenarioInit) || + ((Special.IsVariation && _themes[index].Variation && index != THEME_WIN1) && +#ifndef DEMO + (GameToPlay != GAME_NORMAL || _themes[index].Scenario <= Scenario) && +#endif + (index != THEME_J1 || Special.IsJurassic)))); +} + + +/*********************************************************************************************** + * ThemeClass::From_Name -- Determines theme number from specified name. * + * * + * Use this routine to convert a name (either the base filename of the theme, or a partial * + * substring of the full name) into the matching ThemeType value. Typical use of this is * + * when parsing the INI file for theme control values. * + * * + * INPUT: name -- Pointer to base filename of theme or a partial substring of the full * + * theme name. * + * * + * OUTPUT: Returns with the matching theme number. If no match could be found, then * + * THEME_NONE is returned. * + * * + * WARNINGS: If a filename is specified the comparison is case insensitive. When scanning * + * the full theme name, the comparison is case sensitive. * + * * + * HISTORY: * + * 05/29/1995 JLB : Created. * + *=============================================================================================*/ +ThemeType ThemeClass::From_Name(char const * name) +{ + if (name && strlen(name) > 0) { + /* + ** First search for an exact name match with the filename + ** of the theme. This is guaranteed to be unique. + */ + for (ThemeType theme = THEME_FIRST; theme < THEME_COUNT; theme++) { + if (stricmp(_themes[theme].Name, name) == 0) { + return(theme); + } + } + + /* + ** If the filename scan failed to find a match, then scan for + ** a substring within the full name of the score. This might + ** yeild a match, but is not guaranteed to be unique. + */ + for (theme = THEME_FIRST; theme < THEME_COUNT; theme++) { + if (strstr(Text_String(_themes[theme].Fullname), name) != NULL) { + return(theme); + } + } + } + + return(THEME_NONE); +} + + +/*********************************************************************************************** + * ThemeClass::Scan -- Scans all scores for availability. * + * * + * This routine should be called whenever a score mixfile is registered. It will scan * + * to see if any score is unavailable. If this is the case, then the score will be so * + * flagged in order not to appear on the play list. This condition is likely to occur * + * when expansion mission disks contain a different score mix than the release version. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/04/1996 JLB : Created. * + *=============================================================================================*/ +void ThemeClass::Scan(void) +{ + for (ThemeType theme = THEME_FIRST; theme < THEME_COUNT; theme++) { +// if (theme == THEME_J1 && !Special.IsJurassic) { +// _themes[theme].Available = false; +// } else { + _themes[theme].Available = CCFileClass(Theme_File_Name(theme)).Is_Available(); +// } + } +} + diff --git a/THEME.H b/THEME.H new file mode 100644 index 0000000..3591837 --- /dev/null +++ b/THEME.H @@ -0,0 +1,87 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\theme.h_v 2.16 16 Oct 1995 16:45:48 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : THEME.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 14, 1994 * + * * + * Last Update : August 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef THEME_H +#define THEME_H + +class ThemeClass +{ + private: + static char const * Theme_File_Name(ThemeType theme); + + int Current; // Handle to current score. + ThemeType Score; // Score number currently being played. + ThemeType Pending; // Score to play next. + + typedef struct { + char const * Name; // Filename of score. + int Fullname; // Text number for full score name. + int Scenario; // Scenario when it first becomes available. + int Duration; // Duration of theme in seconds. + bool Normal; // Allowed in normal game play? + bool Variation; // Is there a variation to the score? + bool Repeat; // Always repeat this score? + bool Available; // Is the score available? + } ThemeControl; + + static ThemeControl _themes[THEME_COUNT]; + + enum { + THEME_DELAY=TIMER_SECOND + }; + + public: + ThemeClass(void); + + ThemeType From_Name(char const * name); + int Track_Length(ThemeType index); + int Max_Themes(void) {return THEME_COUNT;}; + char const * Full_Name(ThemeType index) const; + char const * Base_Name(ThemeType index) const; + void AI(void); + void Queue_Song(ThemeType index); + int Play_Song(ThemeType index); + ThemeType What_Is_Playing(void) {return Score;}; + void Stop(void); + void Fade_Out(void) {Queue_Song(THEME_NONE);}; + int Still_Playing(void); + ThemeType Next_Song(ThemeType index); + bool Is_Allowed(ThemeType index) const; + static void _pascal Scan(void); +}; + +#endif diff --git a/TOGGLE.CPP b/TOGGLE.CPP new file mode 100644 index 0000000..bbb3ebb --- /dev/null +++ b/TOGGLE.CPP @@ -0,0 +1,199 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\toggle.cpv 2.18 16 Oct 1995 16:50:56 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TOGGLE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : February 2, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ToggleClass::ToggleClass -- Normal constructor for toggle button gadgets. * + * ToggleClass::Turn_Off -- Turns the toggle button to the "OFF" state. * + * ToggleClass::Turn_On -- Turns the toggle button to the "ON" state. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "toggle.h" + + +/*********************************************************************************************** + * ToggleClass::ToggleClass -- Normal constructor for toggle button gadgets. * + * * + * This is the normal constructor for toggle buttons. A toggle button is one that most * + * closesly resembles the Windows style. It has an up and down state as well as an on * + * and off state. * + * * + * INPUT: id -- ID number for this button. * + * * + * x,y -- Pixel coordinate of upper left corner of this button. * + * * + * w,h -- Width and height of the button. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +ToggleClass::ToggleClass(unsigned id, int x, int y, int w, int h) + : ControlClass(id, x, y, w, h, LEFTPRESS|LEFTRELEASE, true) +{ + IsPressed = false; + IsOn = false; + IsToggleType = false; +} + + +/*********************************************************************************************** + * ToggleClass::Turn_On -- Turns the toggle button to the "ON" state. * + * * + * This routine will turn the button on. The button will also be flagged to be redrawn * + * at the next opportunity. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void ToggleClass::Turn_On(void) +{ + IsOn = true; + Flag_To_Redraw(); +} + + +/*********************************************************************************************** + * ToggleClass::Turn_Off -- Turns the toggle button to the "OFF" state. * + * * + * This routine will turn the toggle button "off". It will also be flagged to be redrawn * + * at the next opportunity. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void ToggleClass::Turn_Off(void) +{ + IsOn = false; + Flag_To_Redraw(); +} + + +/*********************************************************************************************** + * ToggleClass::Action -- Handles mouse clicks on a text button. * + * * + * This routine will process any mouse or keyboard event that is associated with this * + * button object. It detects and flags the text button so that it will properly be drawn * + * in a pressed or raised state. It also handles any toggle state for the button. * + * * + * INPUT: flags -- The event flags that triggered this button. * + * * + * key -- The keyboard code associated with this event. Usually this is KN_LMOUSE * + * or similar, but it could be a regular key if this text button is given * + * a hotkey. * + * * + * OUTPUT: Returns whatever the lower level processing for buttons decides. This is usually * + * true. * + * * + * WARNINGS: The button is flagged to be redrawn by this routine. * + * * + * HISTORY: * + * 01/14/1995 JLB : Created. * + * 02/02/1995 JLB : Left press doesn't get passed to other buttons now * + *=============================================================================================*/ +int ToggleClass::Action(unsigned flags, KeyNumType &key) +{ + /* + ** If there are no action flag bits set, then this must be a forced call. A forced call + ** must never actually function like a real call, but rather only performs any necessary + ** graphic updating. + */ + if (!flags) { + if ((unsigned)(Get_Mouse_X() - X) < Width && (unsigned)(Get_Mouse_Y() - Y) < Height ) { + if (!IsPressed) { + IsPressed = true; + Flag_To_Redraw(); + } + } else { + if (IsPressed) { + IsPressed = false; + Flag_To_Redraw(); + } + } + } + + /* + ** Handle the sticky state for this gadget. It must be processed here + ** because the event flags might be cleared before the action function + ** is called. + */ + Sticky_Process(flags); + + /* + ** Flag the button to show the pressed down imagery if this mouse button + ** was pressed over this gadget. + */ + if (flags & LEFTPRESS) { + IsPressed = true; + Flag_To_Redraw(); + flags &= ~LEFTPRESS; + ControlClass::Action(flags, key); + key = KN_NONE; // erase the event + return(true); // stop processing other buttons now + } + + if (flags & LEFTRELEASE) { + if (IsPressed) { + if (IsToggleType) { + IsOn = (IsOn == false); + } + IsPressed = false; + } else { + flags &= ~LEFTRELEASE; + } + } + + /* + ** Do normal button processing. This ends up causing the button's ID number to + ** be returned from the controlling Input() function. + */ + return(ControlClass::Action(flags, key)); +} + + diff --git a/TOGGLE.H b/TOGGLE.H new file mode 100644 index 0000000..ba88826 --- /dev/null +++ b/TOGGLE.H @@ -0,0 +1,82 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\toggle.h_v 2.16 16 Oct 1995 16:47:34 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TOGGLE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TOGGLE_H +#define TOGGLE_H + +#include "control.h" + +/* +** This class handles gadgets that behave like the Windows buttons. That is, once the mouse +** button is clicked over them, they capture the focus until the mouse button is released. +** They have a different imagery for the pressed and released states. They only recognize +** a valid selection when the mouse button is release while over the button. +*/ +class ToggleClass : public ControlClass +{ + public: + ToggleClass(unsigned id, int x, int y, int w, int h); + virtual void Turn_On(void); + virtual void Turn_Off(void); + + /* + ** Is this button in a pressed down state? This occurs when the mouse is clicked on the + ** button and the mouse is still being held down. + */ + unsigned IsPressed:1; + + /* + ** This is the button on/off state. Sometimes a button that is "on" has a different + ** imagery than one that is "off". If the on/off state is not necessary, then just + ** ignore this flag. + */ + unsigned IsOn:1; + + /* + ** If this button can be turned "on" or "off", then this flag should be set to + ** true. Sometimes a button needs to display its on/off state. In the render routine, + ** examine the IsOn flag and display accordingly. If this flag is false, then the + ** IsOn flag will not be changed, regardless of button clicking. + */ + unsigned IsToggleType:1; + + protected: + + virtual int Action(unsigned flags, KeyNumType &key); +}; + +#endif diff --git a/TOOLS/MIXFILE.EXE b/TOOLS/MIXFILE.EXE new file mode 100644 index 0000000..8c0aa86 Binary files /dev/null and b/TOOLS/MIXFILE.EXE differ diff --git a/TRIGGER.CPP b/TRIGGER.CPP new file mode 100644 index 0000000..fa33c3f --- /dev/null +++ b/TRIGGER.CPP @@ -0,0 +1,1486 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\trigger.cpv 2.17 16 Oct 1995 16:51:20 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TRIGGER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/12/94 * + * * + * Last Update : August 27, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Do_All_To_Hunt -- Forces all computer controlled units into hunt mode. * + * TriggerClass::Action_From_Name -- retrieves ActionType for given name * + * TriggerClass::Action_Need_Team -- Determines if this action event requires a team. * + * TriggerClass::As_Pointer -- returns pointer for the given trigger name * + * TriggerClass::As_Target -- Converts trigger to a target value * + * TriggerClass::Event_From_Name -- retrieves EventType for given name * + * TriggerClass::Event_Need_Data -- Determines if this event requires a data value. * + * TriggerClass::Event_Need_House -- Determines if this event requires a house identifier. * + * TriggerClass::Event_Need_Object -- Determines if the specified event requires an object. * + * TriggerClass::Init -- clears triggers for new scenario * + * TriggerClass::Name_From_Action -- retrieves name for ActionType * + * TriggerClass::Name_From_Event -- retrieves name for EventType * + * TriggerClass::Read_INI -- reads triggers from the INI file * + * TriggerClass::Remove -- removes this trigger from the game * + * TriggerClass::Spring -- Trigger processing routine for cell-based triggers * + * TriggerClass::Spring -- Trigger processing routine for house-based triggers * + * TriggerClass::Spring -- Trigger processing routine for object-based triggers * + * TriggerClass::TriggerClass -- constructor * + * TriggerClass::Validate -- validates trigger pointer * + * TriggerClass::Write_INI -- writes triggers to the INI file * + * TriggerClass::operator delete -- 'delete' operator * + * TriggerClass::operator new -- 'new' operator * + * TriggerClass::~TriggerClass -- Destructor for trigger objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +static void Do_All_To_Hunt(void); + +#define FIXUP 0 + +/* +********************************** Globals ********************************** +*/ +static const char * EventText[EVENT_COUNT + 1] = { + "None", + "Player Enters", + "Discovered", + "Attacked", + "Destroyed", + "Any", + "House Discov.", + "Units Destr.", + "Bldgs Destr.", + "All Destr.", + "Credits", + "Time", + "# Bldgs Dstr.", + "# Units Dstr.", + "No Factories", + "Civ. Evac.", + "Built It" +}; + + +static const char * ActionText[TriggerClass::ACTION_COUNT + 1] = { + "None", + "Win", + "Lose", + "Production", + "Create Team", + "Dstry Teams", + "All to Hunt", + "Reinforce.", + "DZ at 'Z'", + "Airstrike", + "Nuclear Missile", + "Ion Cannon", + "Dstry Trig 'XXXX'", + "Dstry Trig 'YYYY'", + "Dstry Trig 'ZZZZ'", + "Autocreate", + "Cap=Win/Des=Lose", + "Allow Win" +}; + + +/*********************************************************************************************** + * TriggerClass::Validate -- validates trigger pointer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int TriggerClass::Validate(void) const +{ + int num; + + num = Triggers.ID(this); + if (num < 0 || num >= TRIGGER_MAX) { + Validate_Error("TRIGGER"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * TriggerClass::Event_Need_Object -- Determines if the specified event requires an object. * + * * + * This routine determines if the specified event must be attached to an object. Such * + * events can only exist in a parasitic fashion attached to object(s) in the game. * + * * + * INPUT: event -- The event type to examine. * + * * + * OUTPUT: Does the specified event require attachement to an object? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/27/1995 JLB : Created. * + *=============================================================================================*/ +bool TriggerClass::Event_Need_Object(EventType event) +{ + switch (event) { + case EVENT_PLAYER_ENTERED: + case EVENT_DISCOVERED: + case EVENT_ATTACKED: + case EVENT_DESTROYED: + case EVENT_ANY: + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TriggerClass::Event_Need_House -- Determines if this event requires a house identifier. * + * * + * This routine is used to determine if the specified event requires a house identifier. * + * All trigger events that affect a house will require a house identifier. * + * * + * INPUT: event -- The event type to examine. * + * * + * OUTPUT: Does the specified event type require a house identifier? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/27/1995 JLB : Created. * + *=============================================================================================*/ +bool TriggerClass::Event_Need_House(EventType event) +{ + switch (event) { + case EVENT_PLAYER_ENTERED: + case EVENT_HOUSE_DISCOVERED: + case EVENT_UNITS_DESTROYED: + case EVENT_BUILDINGS_DESTROYED: + case EVENT_ALL_DESTROYED: + case EVENT_CREDITS: + case EVENT_TIME: + case EVENT_NBUILDINGS_DESTROYED: + case EVENT_NUNITS_DESTROYED: + case EVENT_NOFACTORIES: + case EVENT_EVAC_CIVILIAN: + case EVENT_BUILD: + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TriggerClass::Event_Need_Data -- Determines if this event requires a data value. * + * * + * This routine will determine if the specified event requires a data number parameter. * + * This is commonly needed for trigger events. * + * * + * INPUT: event -- The event to examine. * + * * + * OUTPUT: Does the specified event require a data number parameter? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/27/1995 JLB : Created. * + *=============================================================================================*/ +bool TriggerClass::Event_Need_Data(EventType event) +{ + switch (event) { + case EVENT_CREDITS: + case EVENT_TIME: + case EVENT_NBUILDINGS_DESTROYED: + case EVENT_NUNITS_DESTROYED: + case EVENT_BUILD: + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TriggerClass::Action_Need_Team -- Determines if this action event requires a team. * + * * + * This routine will determine if the specified action requires a team name parameter. * + * Typically, this is needed for reinforcements or other trigger events that affect * + * a particular team type. * + * * + * INPUT: action -- The action that is to be examined. * + * * + * OUTPUT: Does the specified action require a team type name? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/27/1995 JLB : Created. * + *=============================================================================================*/ +bool TriggerClass::Action_Need_Team(TriggerClass::ActionType action) +{ + switch (action) { + case ACTION_CREATE_TEAM: + case ACTION_DESTROY_TEAM: + case ACTION_REINFORCEMENTS: + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TriggerClass::TriggerClass -- constructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +TriggerClass::TriggerClass(void) +{ + IsPersistant = VOLATILE; + AttachCount = 0; + Event = EVENT_NONE; + Action = ACTION_NONE; + House = HOUSE_NONE; + DataCopy = Data = 0L; + Name[0] = '\0'; + Team = NULL; +} + + +/*********************************************************************************************** + * TriggerClass::~TriggerClass -- Destructor for trigger objects. * + * * + * This destructor will update the house blockage value if necessary. No other action need * + * be performed on trigger destruction. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +TriggerClass::~TriggerClass(void) +{ + if (GameActive && House != HOUSE_NONE && Action == ACTION_ALLOWWIN) { + if (Houses.Ptr(House)->Blockage) Houses.Ptr(House)->Blockage--; + Houses.Ptr(House)->BorrowedTime = TICKS_PER_SECOND*4; + } +} + + +/*********************************************************************************************** + * TriggerClass::Init -- clears triggers for new scenario * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=============================================================================================*/ +void TriggerClass::Init(void) +{ + Triggers.Free_All(); +} + + +/*********************************************************************************************** + * TriggerClass::Spring -- Trigger processing routine * + * * + * Checks whether this trigger should "spring" for the given event & object; * + * If it should, then some really cool undocumented stuff magically happens. * + * * + * INPUT: * + * event EventType: What happened? * + * object Ptr to object containing this trigger: What did it happen to? * + * * + * OUTPUT: * + * 0 = nothing happened; 1 = the trigger was sprung * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/06/1994 BR : Created. * + * 06/25/1995 JLB : Added more trigger events. * + *=============================================================================================*/ +bool TriggerClass::Spring(EventType event, ObjectClass *obj) +{ + Validate(); + /* + ** If this is not the event for this trigger, just return. + */ + if (event != Event && Event != EVENT_ANY) { + return(false); + } + + /* + ** If time-based, decrement the minute counter; return if it's not time yet + */ + if (Event == EVENT_TIME) { + Data--; + if (Data > 0) { + return(false); + } + Data = DataCopy; + } + + /* + ** Semi-persistant trigger: first detach it from the calling object, then + ** see if this is the last object we're attached to; if so, the trigger + ** will spring. + */ + if (IsPersistant == SEMIPERSISTANT) { + + /* + ** Detach ourselves from the object + */ + obj->Trigger = NULL; + + /* + ** Decrement our attachment counter + */ + AttachCount--; + + /* + ** If we're attached to more objects, don't spring; otherwise, spring. + ** And, mark ourselves as volatile so we'll completely remove ourselves + ** from the game after we go off. + */ + if (AttachCount > 0) { + return(false); + } else { + IsPersistant = VOLATILE; + } + } + + /* + ** Otherwise, take an appropriate action. + */ + bool success = true; + TriggerClass * trig = NULL; + switch (Action) { + case ACTION_NUKE: + HouseClass::As_Pointer(HOUSE_BAD)->NukeStrike.Enable(true, false); + HouseClass::As_Pointer(HOUSE_BAD)->NukeStrike.Forced_Charge(PlayerPtr->Class->House == HOUSE_BAD); + break; + + case ACTION_ION: + HouseClass::As_Pointer(HOUSE_GOOD)->IonCannon.Enable(true, false); + HouseClass::As_Pointer(HOUSE_GOOD)->IonCannon.Forced_Charge(PlayerPtr->Class->House == HOUSE_GOOD); + break; + + case ACTION_WINLOSE: + switch (event) { + case EVENT_DESTROYED: + if (!PlayerPtr->IsToWin || PlayerPtr->Blockage > 0) PlayerPtr->Flag_To_Lose(); + success = true; + break; + + case EVENT_PLAYER_ENTERED: + if (!PlayerPtr->IsToLose) PlayerPtr->Flag_To_Win(); + success = true; + break; + + default: + success = false; + break; + } + break; + + case ACTION_DESTROY_XXXX: + trig = As_Pointer("XXXX"); + if (trig) { + trig->Remove(); + } + delete trig; + break; + + case ACTION_DESTROY_YYYY: + trig = As_Pointer("YYYY"); + if (trig) { + trig->Remove(); + } + delete trig; + break; + + case ACTION_DESTROY_ZZZZ: + trig = As_Pointer("ZZZZ"); + if (trig) { + trig->Remove(); + } + delete trig; + break; + + case ACTION_AIRSTRIKE: + PlayerPtr->IsAirstrikePending = true; +// PlayerPtr->Make_Air_Strike_Available(true); + break; + + case ACTION_DZ: + new AnimClass(ANIM_LZ_SMOKE, Cell_Coord(Waypoint[25])); + break; + + case ACTION_NONE: + break; + + case ACTION_WIN: + PlayerPtr->Flag_To_Win(); + break; + + case ACTION_LOSE: + PlayerPtr->Flag_To_Lose(); + break; + + case ACTION_BEGIN_PRODUCTION: + HouseClass::As_Pointer(House)->Begin_Production(); + break; + + case ACTION_AUTOCREATE: + if (obj && obj->Is_Techno()) { + ((TechnoClass *)obj)->House->IsAlerted = true; + } + break; + + case ACTION_CREATE_TEAM: + if (Team) { + ScenarioInit++; + Team->Create_One_Of(); + ScenarioInit--; + } + break; + + case ACTION_DESTROY_TEAM: + if (Team) { + Team->Destroy_All_Of(); + } + break; + + case ACTION_REINFORCEMENTS: + if (Team) { + success = Do_Reinforcements(Team); + } + break; + + case ACTION_ALL_HUNT: + Do_All_To_Hunt(); + break; + + default: + break; + } + + if (!success && Event == EVENT_TIME) Data = 1; + + /* + ** Remove trigger from the game. + */ + if (success && IsPersistant == VOLATILE) { + Remove(); + } + + return(true); +} + + +/*********************************************************************************************** + * TriggerClass::Spring -- Trigger processing routine * + * * + * This version of Spring is for cell-based triggers. * + * * + * INPUT: * + * data elapsed time, or credits, depending on what 'Event' is. * + * * + * OUTPUT: * + * 0 = nothing happened; 1 = the trigger was sprung * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/06/1994 BR : Created. * + *=============================================================================================*/ +bool TriggerClass::Spring(EventType event, CELL cell) +{ + Validate(); + /* + ** If this is not the event for this trigger, just return. + */ + if (event != Event) { + return(false); + } + + /* + ** If time-based, decrement the minute counter; return if it's not time yet + */ + if (Event == EVENT_TIME) { + Data--; + if (Data > 0) { + return(false); + } + Data = DataCopy; + } + + /* + ** Semi-persistant trigger: first detach it from the calling cell, then + ** see if this is the last cell we're attached to; if so, the trigger + ** will spring. + */ + if (IsPersistant == SEMIPERSISTANT) { + + /* + ** Detach ourselves from the cell + */ + Map[cell].IsTrigger = 0; + + /* + ** Decrement our attachment counter + */ + AttachCount--; + + /* + ** If we're attached to more cells, don't spring; otherwise, spring. + ** And, mark ourselves as volatile so we'll completely remove ourselves + ** from the game after we go off. + */ + if (AttachCount > 0) { + return(false); + } else { + IsPersistant = VOLATILE; + } + } + + /* + ** Otherwise, take an appropriate action. + */ + bool success = true; + TriggerClass * trig = NULL; + int index; + switch (Action) { + case ACTION_NUKE: + HouseClass::As_Pointer(HOUSE_BAD)->NukeStrike.Enable(true, false); + HouseClass::As_Pointer(HOUSE_BAD)->NukeStrike.Forced_Charge(PlayerPtr->Class->House == HOUSE_BAD); + break; + + case ACTION_ION: + HouseClass::As_Pointer(HOUSE_GOOD)->IonCannon.Enable(true, false); + HouseClass::As_Pointer(HOUSE_GOOD)->IonCannon.Forced_Charge(PlayerPtr->Class->House == HOUSE_GOOD); + break; + + case ACTION_AUTOCREATE: + for (index = 0; index < Houses.Count(); index++) { + Houses.Ptr(index)->IsAlerted = true; + } + break; + + case ACTION_DESTROY_XXXX: + trig = As_Pointer("XXXX"); + if (trig) { + trig->Remove(); + } + delete trig; + break; + + case ACTION_DESTROY_YYYY: + trig = As_Pointer("YYYY"); + if (trig) { + trig->Remove(); + } + delete trig; + break; + + case ACTION_DESTROY_ZZZZ: + trig = As_Pointer("ZZZZ"); + if (trig) { + trig->Remove(); + } + delete trig; + break; + + case ACTION_AIRSTRIKE: + HouseClass::As_Pointer(House)->AirStrike.Enable(false, true); + if (House == PlayerPtr->Class->House) { + PlayerPtr->AirStrike.Forced_Charge(true); + Map.Add(RTTI_SPECIAL, SPC_AIR_STRIKE); + Map.Column[1].Flag_To_Redraw(); + } +// PlayerPtr->Make_Air_Strike_Available(true); + break; + + case ACTION_DZ: + new AnimClass(ANIM_LZ_SMOKE, Cell_Coord(Waypoint[25])); + break; + + case ACTION_NONE: + break; + + case ACTION_WIN: + PlayerPtr->Flag_To_Win(); + break; + + case ACTION_LOSE: + PlayerPtr->Flag_To_Lose(); + break; + + case ACTION_BEGIN_PRODUCTION: + if (PlayerPtr->Class->House == HOUSE_GOOD) { + HouseClass::As_Pointer(HOUSE_BAD)->Begin_Production(); + } else { + HouseClass::As_Pointer(HOUSE_GOOD)->Begin_Production(); + } + break; + + case ACTION_CREATE_TEAM: + if (Team) { + ScenarioInit++; + Team->Create_One_Of(); + ScenarioInit--; + } + break; + + case ACTION_DESTROY_TEAM: + if (Team) { + Team->Destroy_All_Of(); + } + break; + + case ACTION_REINFORCEMENTS: + if (Team) { + success = Do_Reinforcements(Team); + } + break; + + case ACTION_ALL_HUNT: + Do_All_To_Hunt(); + break; + + default: + break; + } + + if (!success && Event == EVENT_TIME) Data = 1; + + /* + ** Remove trigger from the game. + */ + if (success && IsPersistant == VOLATILE) { + Remove(); + } + + return(true); +} + + +/*********************************************************************************************** + * TriggerClass::Spring -- Trigger processing routine * + * * + * This version of Spring is for house-specific triggers. * + * For a time-based trigger, 'data' will the the current TickCount. * + * For a credit-based trigger, 'data' will be the credits for the HouseClass * + * containing this trigger. * + * * + * INPUT: * + * event the event that happened * + * house house that this event relates to * + * data elapsed time, or credits, depending on what 'Event' is. * + * * + * OUTPUT: * + * 0 = nothing happened; 1 = the trigger was sprung * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/06/1994 BR : Created. * + * 06/25/1995 JLB : Added more trigger events. * + *=============================================================================================*/ +bool TriggerClass::Spring(EventType event, HousesType house, long data) +{ + Validate(); + /* + ** If this is not the event for this trigger, just return. + */ + if (event != Event || house != House) { + return(false); + } + + /* + ** If credits-based, check 'data' + */ + if (Event == EVENT_CREDITS && data < Data) { + return(false); + } + + /* + ** Building event check to ensure that the building number matches. + */ + if (Event == EVENT_BUILD && data != Data) { + return(false); + } + + /* + ** Number of objects destroyed checker. If the data supplied indicates that + ** the correct number of objects have been destroyed, then this trigger + ** will succeed. + */ + if (Event == EVENT_NBUILDINGS_DESTROYED || Event == EVENT_NUNITS_DESTROYED) { + if (data < Data) { + return(false); + } + } + + /* + ** If time-based, decrement the minute counter; return if it's not time yet + */ + if (Event == EVENT_TIME) { + Data--; + if (Data > 0) { + return(false); + } + Data = DataCopy; + } + + /* + ** The trigger has gone off; take appropriate action + */ + bool success = true; + TriggerClass * trig = NULL; + switch (Action) { + + case ACTION_NUKE: + HouseClass::As_Pointer(HOUSE_BAD)->NukeStrike.Enable(true, false); + HouseClass::As_Pointer(HOUSE_BAD)->NukeStrike.Forced_Charge(PlayerPtr->Class->House == HOUSE_BAD); + break; + + case ACTION_ION: + HouseClass::As_Pointer(HOUSE_GOOD)->IonCannon.Enable(true, false); + HouseClass::As_Pointer(HOUSE_GOOD)->IonCannon.Forced_Charge(PlayerPtr->Class->House == HOUSE_GOOD); + break; + + /* + ** This will remove a blockage to the win condition. No action need + ** be performed here since the act of deleting the trigger will + ** remove the blockage. + */ + case ACTION_ALLOWWIN: + break; + + case ACTION_AUTOCREATE: + HouseClass::As_Pointer(House)->IsAlerted = true; + break; + + case ACTION_DESTROY_XXXX: + trig = As_Pointer("XXXX"); + if (trig) { + trig->Remove(); + } + delete trig; + break; + + case ACTION_DESTROY_YYYY: + trig = As_Pointer("YYYY"); + if (trig) { + trig->Remove(); + } + delete trig; + break; + + case ACTION_DESTROY_ZZZZ: + trig = As_Pointer("ZZZZ"); + if (trig) { + trig->Remove(); + } + delete trig; + break; + + case ACTION_AIRSTRIKE: + PlayerPtr->AirStrike.Enable(false, true); + if (House == PlayerPtr->Class->House) { + PlayerPtr->AirStrike.Forced_Charge(true); + Map.Add(RTTI_SPECIAL, SPC_AIR_STRIKE); + Map.Column[1].Flag_To_Redraw(); + } + break; + + case ACTION_NONE: + break; + + case ACTION_DZ: + new AnimClass(ANIM_LZ_SMOKE, Cell_Coord(Waypoint[25])); + break; + + case ACTION_WIN: + PlayerPtr->Flag_To_Win(); + break; + + case ACTION_LOSE: + PlayerPtr->Flag_To_Lose(); + break; + + case ACTION_BEGIN_PRODUCTION: + HouseClass::As_Pointer(House)->Begin_Production(); + break; + + case ACTION_CREATE_TEAM: + if (Team) { + ScenarioInit++; + Team->Create_One_Of(); + ScenarioInit--; + } + break; + + case ACTION_DESTROY_TEAM: + if (Team) { + Team->Destroy_All_Of(); + } + break; + + case ACTION_REINFORCEMENTS: + if (Team) { + success = Do_Reinforcements(Team); + } + break; + + case ACTION_ALL_HUNT: + Do_All_To_Hunt(); + break; + + default: + break; + } + + if (!success && Event == EVENT_TIME) Data = 1; + + /* + ** Remove trigger from the game. + */ + if (success && IsPersistant == VOLATILE) { + Remove(); + } + + return(true); +} + + +/*********************************************************************************************** + * TriggerClass::Remove -- removes this trigger from the game * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = trigger was removed, 0 = it wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/06/1994 BR : Created. * + *=============================================================================================*/ +bool TriggerClass::Remove(void) +{ + Validate(); + CELL cell; + HousesType h; + int index; + + /* + ** Loop through all cells; remove any reference to this trigger + */ + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (Map[cell].IsTrigger) { + if (CellTriggers[cell] == this) { + Map[cell].IsTrigger = 0; + CellTriggers[cell] = NULL; + } + } + } + + /* + ** Loop through all objects, removing any reference to this trigger + */ + for (index = 0; index < Infantry.Count(); index++) { + if (Infantry.Ptr(index)->Trigger == this) { + Infantry.Ptr(index)->Trigger = NULL; + } + } + for (index = 0; index < Buildings.Count(); index++) { + if (Buildings.Ptr(index)->Trigger == this) { + Buildings.Ptr(index)->Trigger = NULL; + } + } + for (index = 0; index < Units.Count(); index++) { + if (Units.Ptr(index)->Trigger == this) { + Units.Ptr(index)->Trigger = NULL; + } + } + for (index = 0; index < Terrains.Count(); index++) { + if (Terrains.Ptr(index)->Trigger == this) { + Terrains.Ptr(index)->Trigger = NULL; + } + } + + /* + ** Remove this trigger from any house list it's in. Invoking '-=' with a + ** pointer not in the list has no effect; loop through all houses just to + ** be on the safe side. + */ + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + HouseTriggers[h].Delete(this); + } + + delete this; + + return(true); +} + + +/*********************************************************************************************** + * TriggerClass::Read_INI -- reads triggers from the INI file * + * * + * INI entry format: * + * Triggername = Eventname, Actionname, Data, Housename, TeamName, IsPersistant * + * * + * This routine reads in the triggers & creates them. Then, other classes can * + * get pointers to the triggers they're linked to. * + * * + * The routine relies on the TeamTypeClasses already being loaded so it can resolve * + * references to teams in this function. * + * * + * Cell Trigger pointers & IsTrigger flags are set in DisplayClass::Read_INI(), * + * and cleared in the Map::Init() routine (which clears all cell objects to 0's). * + * * + * Object's pointers are set in: * + * InfantryClass::Read_INI() * + * BuildingClass::Read_INI() * + * UnitClass::Read_INI() * + * TerrainClass::Read_INI() * + * The object trigger pointers are cleared in the ObjectClass constructor. * + * * + * The House's EMSListOf triggers is set in this routine, and cleared in the * + * HouseClass::Init() routine. * + * * + * INPUT: * + * buffer buffer to hold the INI data * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * This function must be called before any other class's Read_INI. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +void TriggerClass::Read_INI(char *buffer) +{ + TriggerClass *trigger; // Working trigger pointer. + char *tbuffer; // Accumulation buffer of trigger IDs. + int len; // Length of data in buffer. + char buf[128]; + + /* + ** Set 'tbuffer' to point just past the INI buffer + */ + len = strlen(buffer) + 2; + tbuffer = buffer + len; + + /* + ** Read all TRIGGER entry names into 'tbuffer' + */ + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); + + /* + ** Loop for all trigger entries. + */ + while (*tbuffer != '\0') { + + /* + ** Create a new trigger. + */ + trigger = new TriggerClass(); + + /* + ** Set its name. + */ + trigger->Set_Name (tbuffer); + + /* + ** Get the trigger entry. + */ + WWGetPrivateProfileString(INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, buffer); + + /* + ** Fill in the trigger. + */ + trigger->Fill_In(tbuffer,buf); + + /* + ** Add 'trigger' to the House's list. + */ +// if (trigger->House != HOUSE_NONE && trigger->Event != EVENT_PLAYER_ENTERED) { +// if (Event_Need_House(trigger->Event) && !Event_Need_Object(trigger->Event)) { + if (trigger->House != HOUSE_NONE) { + if (trigger->Action == ACTION_ALLOWWIN) HouseClass::As_Pointer(trigger->House)->Blockage++; + HouseTriggers[trigger->House].Add(trigger); + trigger->AttachCount++; + } + + /* + ** Go to next entry. + */ + tbuffer += strlen(tbuffer)+1; + } +} + + +/*********************************************************************************************** + * TriggerClass::Fill_In -- fills in trigger from the given INI entry * + * * + * This routine fills in the given trigger with the given name, and values from * + * the given INI entry. * + * * + * (This routine is used by the scenario editor, to import teams from the MASTER.INI file.) * + * * + * INI entry format: * + * Triggername = Eventname, Actionname, Data, Housename, TeamName, IsPersistant * + * * + * INPUT: * + * name mnemonic for the desired trigger * + * entry INI entry to parse * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +void TriggerClass::Fill_In(char * name, char *entry) +{ + Validate(); + char *p; + + /* + ** Set its name. + */ + Set_Name(name); + + /* + ** 1st token: Event. + */ + Event = Event_From_Name(strtok(entry, ",")); + + /* + ** 2nd token: Action. + */ + Action = Action_From_Name(strtok(NULL, ",")); + + /* + ** 3rd token: Data. + */ + DataCopy = Data = atol(strtok(NULL, ",")); + + /* + ** 4th token: House. + */ + House = HouseTypeClass::From_Name(strtok(NULL, ",")); + if (House == HOUSE_NONE && Event == EVENT_PLAYER_ENTERED) { + House = PlayerPtr->Class->House; + } + + /* + ** 5th token: Team. + */ + Team = TeamTypeClass::As_Pointer(strtok(NULL, ",")); + + /* + ** 6th token: IsPersistant. This token was added later, so we must check + ** for its existence. + */ + p = strtok(NULL, ","); + if (p) { + IsPersistant = (PersistantType)atoi(p); + } else { + IsPersistant = VOLATILE; + } +} + + +/*********************************************************************************************** + * TriggerClass::Write_INI -- writes triggers to the INI file * + * * + * INI entry format: * + * Triggername = Eventname, Actionname, Data, Housename, TeamName, IsPersistant * + * * + * INPUT: * + * buffer buffer to hold the INI data * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +void TriggerClass::Write_INI(char *buffer, bool refresh) +{ + int index; + char buf[128]; + TriggerClass *trigger; + char const *hname; + char const *tname; + + /* + ** First, clear out all existing trigger data from the INI file. + */ + if (refresh) { + WWWritePrivateProfileString(INI_Name(), NULL, NULL, buffer); + } + + /* + ** Now write all the trigger data out + */ + for (index = 0; index < Triggers.Count(); index++) { + + /* + ** Get ptr to next active trigger. + */ + trigger = Triggers.Ptr(index); + + /* + ** Generate INI entry. + */ + if (trigger->House==HOUSE_NONE) { + hname = "None"; + } else { + hname = HouseClass::As_Pointer(trigger->House)->Class->IniName; + } + + if (trigger->Team==NULL) { + tname = "None"; + } else { + tname = trigger->Team->IniName; + } + + sprintf(buf,"%s,%s,%ld,%s,%s,%d", + TriggerClass::Name_From_Event(trigger->Event), + TriggerClass::Name_From_Action(trigger->Action), + trigger->Data, + hname, + tname, + trigger->IsPersistant); + WWWritePrivateProfileString(INI_Name(), trigger->Get_Name(), buf, buffer); + } +} + + +/*********************************************************************************************** + * TriggerClass::As_Pointer -- returns pointer for the given trigger name * + * * + * This function is designed for use at initialization time, ie when the * + * trigger mnemonics are read from the INI and the Read_INI functions need * + * to get a pointer to that trigger. * + * * + * INPUT: * + * name mnemonic for the desired trigger * + * * + * OUTPUT: * + * near pointer to that trigger, NULL if not found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +TriggerClass * TriggerClass::As_Pointer(char const * name) +{ + if (name == NULL) { + return(NULL); + } + + for (int i = 0; i < Triggers.Count(); i++) { + TriggerClass * trigger = Triggers.Ptr(i); + + if (!stricmp(name, trigger->Name)) { + return(trigger); + } + } + + return(NULL); +} + + +/*********************************************************************************************** + * TriggerClass::operator new -- 'new' operator * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * pointer to new trigger * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +void * TriggerClass::operator new(size_t ) +{ + void * ptr = Triggers.Allocate(); + if (ptr) { + ((TriggerClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * TriggerClass::operator delete -- 'delete' operator * + * * + * INPUT: * + * ptr pointer to delete * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +void TriggerClass::operator delete(void *ptr) +{ + if (ptr) { + ((TriggerClass *)ptr)->IsActive = false; + } + Triggers.Free((TriggerClass *)ptr); +} + + +/*********************************************************************************************** + * TriggerClass::Event_From_Name -- retrieves EventType for given name * + * * + * INPUT: * + * name name to get event for * + * * + * OUTPUT: * + * EventType for given name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=============================================================================================*/ +EventType TriggerClass::Event_From_Name (char const *name) +{ + int i; + + if (name == NULL) { + return(EVENT_NONE); + } + + for (i = EVENT_NONE; i < EVENT_COUNT; i++) { + if (!stricmp(name,EventText[i + 1])) { + return((EventType)i); + } + } + + return(EVENT_NONE); +} + + +/*********************************************************************************************** + * TriggerClass::Name_From_Event -- retrieves name for EventType * + * * + * INPUT: * + * event EventType to get name for * + * * + * OUTPUT: * + * name for EventType * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=============================================================================================*/ +char const *TriggerClass::Name_From_Event(EventType event) +{ + return(EventText[event + 1]); +} + + +/*********************************************************************************************** + * TriggerClass::Action_From_Name -- retrieves ActionType for given name * + * * + * INPUT: * + * name name to get ActionType for * + * * + * OUTPUT: * + * ActionType for given name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=============================================================================================*/ +TriggerClass::ActionType TriggerClass::Action_From_Name (char const *name) +{ + int i; + + if (name == NULL) { + return(ACTION_NONE); + } + + for (i = ACTION_NONE; i < ACTION_COUNT; i++) { + if (!stricmp(name,ActionText[i + 1])) { + return((ActionType)i); + } + } + + return(ACTION_NONE); +} + + +/*********************************************************************************************** + * TriggerClass::Name_From_Action -- retrieves name for ActionType * + * * + * INPUT: * + * action ActionType to get name for * + * * + * OUTPUT: * + * name of ActionType * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=============================================================================================*/ +char const *TriggerClass::Name_From_Action(ActionType action) +{ + return(ActionText[action + 1]); +} + + +/*********************************************************************************************** + * TriggerClass::As_Target -- Converts trigger to a target value * + * * + * INPUT: none * + * * + * OUTPUT: TARGET value * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +TARGET TriggerClass::As_Target(void) const +{ + Validate(); + return(Build_Target(KIND_TRIGGER, Triggers.ID(this))); +} + + +/*********************************************************************************************** + * Do_All_To_Hunt -- Forces all computer controlled units into hunt mode. * + * * + * This trigger action will cause the computer units and infantry to go into hunt mode. * + * Use it to bring a scenario to a sudden conclusion. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/20/1995 JLB : Created. * + * 08/14/1995 JLB : Removes the member from a team if necessary. * + *=============================================================================================*/ +static void Do_All_To_Hunt(void) +{ + int index; + + for (index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + + if (!unit->House->IsHuman && unit->IsDown && !unit->IsInLimbo) { + if (unit->Team) unit->Team->Remove(unit); + unit->Assign_Mission(MISSION_HUNT); + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * infantry = Infantry.Ptr(index); + + if (!infantry->House->IsHuman && infantry->IsDown && !infantry->IsInLimbo) { + if (infantry->Team) infantry->Team->Remove(infantry); + infantry->Assign_Mission(MISSION_HUNT); + } + } +} diff --git a/TRIGGER.H b/TRIGGER.H new file mode 100644 index 0000000..cb5bc12 --- /dev/null +++ b/TRIGGER.H @@ -0,0 +1,259 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\trigger.h_v 2.15 16 Oct 1995 16:46:32 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TRIGGER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/12/94 * + * * + * Last Update : November 12, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TRIGGER_H +#define TRIGGER_H + +typedef enum EventType { + EVENT_NONE=-1, + + /* + .......................... Cell-specific events .......................... + */ + EVENT_PLAYER_ENTERED, // player enters this square + EVENT_CELLFIRST = EVENT_PLAYER_ENTERED, + + /* + ......................... Object-specific events ......................... + */ + EVENT_DISCOVERED, // player discovers this object + EVENT_OBJECTFIRST = EVENT_DISCOVERED, + EVENT_ATTACKED, // player attacks this object + EVENT_DESTROYED, // player destroys this object + EVENT_ANY, // Any object event will cause the trigger. + + /* + ......................... House-specific events .......................... + */ + EVENT_HOUSE_DISCOVERED, // any object in this house discovered + EVENT_HOUSEFIRST = EVENT_HOUSE_DISCOVERED, + EVENT_UNITS_DESTROYED, // all house's units destroyed + EVENT_BUILDINGS_DESTROYED, // all house's buildings destroyed + EVENT_ALL_DESTROYED, // all house's units & buildings destroyed + EVENT_CREDITS, // house reaches this many credits + EVENT_TIME, // time elapses for this house + EVENT_NBUILDINGS_DESTROYED, // Number of buildings destroyed. + EVENT_NUNITS_DESTROYED, // Number of units destroyed. + EVENT_NOFACTORIES, // No factories left. + EVENT_EVAC_CIVILIAN, // Civilian has been evacuated. + EVENT_BUILD, // If specified building has been built. + + EVENT_COUNT, + EVENT_FIRST=0 +} EventType; + + +class TriggerClass { + public: + typedef enum ActionType { + ACTION_NONE=-1, + + ACTION_WIN, // player wins! + ACTION_LOSE, // player loses. + ACTION_BEGIN_PRODUCTION, // computer begins production + ACTION_CREATE_TEAM, // computer creates a certain type of team + ACTION_DESTROY_TEAM, + ACTION_ALL_HUNT, // all enemy units go into hunt mode (teams destroyed). + ACTION_REINFORCEMENTS, // player gets reinforcements + // (house that gets them is determined by + // the Reinforcement instance) + ACTION_DZ, // Deploy drop zone smoke. + ACTION_AIRSTRIKE, // Enable airstrike. + ACTION_NUKE, // Enable nuke for computer. + ACTION_ION, // Give ion cannon to computer. + ACTION_DESTROY_XXXX, // Destroy trigger XXXX. + ACTION_DESTROY_YYYY, // Destroy trigger YYYY. + ACTION_DESTROY_ZZZZ, // Destroy trigger ZZZZ. + ACTION_AUTOCREATE, // Computer to autocreat teams. + ACTION_WINLOSE, // Win if captured, lose if destroyed. + ACTION_ALLOWWIN, // Allows winning if triggered. + + ACTION_COUNT, + ACTION_FIRST=0 + } ActionType; + + typedef enum PersistantType { + VOLATILE = 0, + SEMIPERSISTANT = 1, + PERSISTANT = 2, + } PersistantType; + + /* + ** Functions: + ** + ** Constructor/Destructor + */ + TriggerClass(void); + ~TriggerClass(void); + + /* + ** Initialization: clears all triggers in preparation for new scenario + */ + static void Init(void); + + /* + ** Processing routines + */ + bool Spring(EventType event, ObjectClass * object); // object-based + bool Spring(EventType event, CELL cell); // cell-based + bool Spring(EventType event, HousesType house, long data=0); // house-based + bool Remove(void); + + /* + ** File I/O routines + */ + static void Read_INI (char *buffer); + void Fill_In(char *name, char *entry); + static void Write_INI (char *buffer, bool refresh); + static char * INI_Name(void) {return "Triggers";}; + bool Load(FileClass & file); + bool Save(FileClass & file); + void Code_Pointers(void); + void Decode_Pointers(void); + + /* + ** As_Pointer gets a pointer to the trigger object give the mnemonic + */ + static TriggerClass * As_Pointer(char const * name); + + /* + ** Data Access routines + */ +// EventType Get_Event(void) const {return (Event);} +// void Set_Event(EventType event) {Event = event;} +// ActionType Get_Action(void) const {return (Action);} +// void Set_Action(ActionType action) {Action = action;} +// HousesType Get_House(void) const {return(House);} +// void Set_House(HousesType house) {House = house;} +// long Get_Data(void) const {return(Data);} +// void Set_Data(long credits) {Data = credits;} + char const * Get_Name(void) const {return (Name);} + void Set_Name(char const *buf) {strncpy(Name, buf, sizeof(Name)); Name[sizeof(Name)-1] = '\0';} + + /* + ** Utility routines + */ + TARGET As_Target(void) const; + static bool Event_Need_Object(EventType event); + static bool Event_Need_House(EventType event); + static bool Event_Need_Data(EventType event); + static bool Action_Need_Team(ActionType action); + static EventType Event_From_Name(char const *name); + static char const *Name_From_Event(EventType event); + static ActionType Action_From_Name(char const *name); + static char const *Name_From_Action(ActionType action); + + /* + ** Overloaded operators + */ + static void * operator new(size_t size); + static void operator delete(void *ptr); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + /* + ** This is the pointer to the team that gets created or destroyed when + ** a team-related trigger goes off, or for reinforcements. The house + ** for reinforcements is determined by the house for that team. + */ + TeamTypeClass *Team; + + /* + ** If this trigger object is active, then this flag will be true. Trigger + ** objects that are not active are either not yet created or have been + ** deleted after fulfilling their action. + */ + unsigned IsActive:1; + + /* + ** This flag controls whether the trigger destroys itself after it goes + ** off. + ** 0 = trigger destroys itself immediately after going off, and removes + ** itself from all objects it's attached to + ** 1 = trigger is "Semi-Persistent"; it maintains a count of all objects + ** it's attached to, and only actually "springs" after its been + ** triggered from all the objects; then, it removes itself. + ** 2 = trigger is Fully Persistent; it just won't go away. + */ + PersistantType IsPersistant; + + /* + ** This value tells how many objects or cells this trigger is attached + ** to. The Read_INI routine for all classes that point to a trigger must + ** increment this value! + */ + int AttachCount; + + /* + ** Each trigger must have an event which activates it. This is the event that is + ** used to activate this trigger. + */ + EventType Event; + + /* + ** This is the action to perform when the trigger event occurs. + */ + ActionType Action; + + /* + ** For house-specific events, this is the house for that event. + */ + HousesType House; + + /* + ** For credit-related triggers, this is the number of credits that + ** generate the trigger. For time-based triggers, this is the number + ** of minutes that must elapse. + */ + long Data; + long DataCopy; + + private: + + /* + ** Triggers can be referred to by their name, which can be up to 4 + ** characters. + */ + char Name[5]; + +}; + + +#endif diff --git a/TURRET.CPP b/TURRET.CPP new file mode 100644 index 0000000..a635d2f --- /dev/null +++ b/TURRET.CPP @@ -0,0 +1,504 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\turret.cpv 2.18 16 Oct 1995 16:50:38 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TURRET.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 25, 1994 * + * * + * Last Update : August 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TurretClass::AI -- Handles the reloading of the turret weapon. * + * TurretClass::Can_Fire -- Determines if turret can fire upon target. * + * TurretClass::Debug_Dump -- Debug printing of turret values. * + * TurretClass::Fire_At -- Try to fire upon the target specified. * + * TurretClass::Fire_Coord -- Determines the coorindate that projectile would appear. * + * TurretClass::Fire_Direction -- Determines the directinon of firing. * + * TurretClass::Ok_To_Move -- Queries whether the vehicle can move. * + * TurretClass::TurretClass -- Normal constructor for the turret class. * + * TurretClass::TurretClass -- The default constructor for turret class objects. * + * TurretClass::Unlimbo -- Unlimboes turret object. * + * TurretClass::~TurretClass -- Default destructor for turret class objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "turret.h" + + +/*********************************************************************************************** + * TurretClass::~TurretClass -- Default destructor for turret class objects. * + * * + * This is the default destructor for turret class objects. It does nothing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +TurretClass::~TurretClass(void) +{ +} + + +/*********************************************************************************************** + * TurretClass::TurretClass -- The default constructor for turret class objects. * + * * + * This is the default constructor for turret class objects. It does nothing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +TurretClass::TurretClass(void) +{ +} + + +/*********************************************************************************************** + * TurretClass::TurretClass -- Normal constructor for the turret class. * + * * + * This is the normal constructor for the turret class. It merely sets the turret up to * + * face north. * + * * + * INPUT: classid -- The type id for this particular unit. * + * * + * house -- The house that this unit will belong to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/02/1995 JLB : Created. * + *=============================================================================================*/ +TurretClass::TurretClass(UnitType classid, HousesType house) : + DriveClass(classid, house) +{ + Reload = 0; +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * TurretClass::Debug_Dump -- Debug printing of turret values. * + * * + * This routine is used to display the current values of this turret * + * class instance. It is primarily used in the debug output screen. * + * * + * INPUT: x,y -- Monochrome screen coordinates to display data. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/12/1994 JLB : Created. * + *=============================================================================================*/ +void TurretClass::Debug_Dump(MonoClass *mono) const +{ + mono->Set_Cursor(36, 3);mono->Printf("%02X:%02X", SecondaryFacing.Current(), SecondaryFacing.Desired()); + mono->Set_Cursor(28, 7);mono->Printf("%2d", Arm); + DriveClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * TurretClass::Ok_To_Move -- Queries whether the vehicle can move. * + * * + * This virtual routine is used to determine if the vehicle is allowed * + * to start moving. It is typically called when the vehicle desires * + * to move but needs confirmation from the turret logic before * + * proceeding. This happens when dealing with a vehicle that must have * + * its turret face the same direction as the body before the vehicle * + * may begin movement. * + * * + * INPUT: dir -- The facing the unit wants to travel in. * + * * + * OUTPUT: bool; Can the unit begin forward movement now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/12/1994 JLB : Created. * + *=============================================================================================*/ +bool TurretClass::Ok_To_Move(DirType dir) +{ + if (Class->IsLockTurret) { + if (IsRotating) { + return(false); + } else { + if (SecondaryFacing.Difference(dir)) { + SecondaryFacing.Set_Desired(dir); + return(false); + } + } + } + return(true); +} + + +/*********************************************************************************************** + * TurretClass::AI -- Handles the reloading of the turret weapon. * + * * + * This processes the reloading of the turret. It does this by decrementing the arming * + * countdown timer and when it reaches zero, the turret may fire. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/21/1994 JLB : Created. * + *=============================================================================================*/ +void TurretClass::AI(void) +{ + DriveClass::AI(); + + /* + ** A unit with a constant rotating radar dish is handled here. + */ + if (Class->IsRadarEquipped) { + SecondaryFacing.Set((DirType)(SecondaryFacing.Current() + 8)); + Mark(MARK_CHANGE); + } else { + + IsRotating = false; + if (Class->IsTurretEquipped) { + if (IsTurretLockedDown) { + SecondaryFacing.Set_Desired(PrimaryFacing.Current()); + } + + if (SecondaryFacing.Is_Rotating()) { + if (SecondaryFacing.Rotation_Adjust(Class->ROT+1)) { + Mark(MARK_CHANGE); + } + + /* + ** If no further rotation is necessary, flag that the rotation + ** has stopped. + */ + IsRotating = SecondaryFacing.Is_Rotating(); + } else { + if (!IsTurretLockedDown && !Target_Legal(TarCom)) { + if (!Target_Legal(NavCom)) { + SecondaryFacing.Set_Desired(PrimaryFacing.Current()); + } else { + SecondaryFacing.Set_Desired(Direction(NavCom)); + } + } + } + } + } +} + + +/*********************************************************************************************** + * TurretClass::Fire_At -- Try to fire upon the target specified. * + * * + * This routine is the auto-fire logic for the turret. It will check * + * to see if firing is technically legal given the specified target. * + * If it is legal to fire, it does so. It is safe to call this routine * + * every game tick. * + * * + * INPUT: target -- The target to fire upon. * + * * + * which -- Which weapon to use when firing. 0=primary, 1=secondary. * + * * + * OUTPUT: bool; Did firing occur? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/26/1994 JLB : Created. * + *=============================================================================================*/ +BulletClass * TurretClass::Fire_At(TARGET target, int which) +{ + BulletClass * bullet = NULL; + WeaponTypeClass const * weapon = (which == 0) ? &Weapons[Class->Primary] : &Weapons[Class->Secondary]; + + if (Can_Fire(target, which) == FIRE_OK) { + bullet = DriveClass::Fire_At(target, which); + + if (bullet) { + + /* + ** Possible reload timer set. + */ + if (*this == UNIT_MSAM && Reload == 0) { + Reload = TICKS_PER_SECOND * 30; + } + } + } + + return(bullet); +} + + +/*********************************************************************************************** + * TurretClass::Can_Fire -- Determines if turret can fire upon target. * + * * + * This routine determines if the turret can fire upon the target * + * specified. * + * * + * INPUT: target -- The target to fire upon. * + * * + * which -- Which weapon to use to determine legality to fire. 0=primary, * + * 1=secondary. * + * * + * OUTPUT: Returns the fire status type that indicates if firing is allowed and if not, why. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/26/1994 JLB : Created. * + * 06/01/1994 JLB : Returns reason why it can't fire. * + *=============================================================================================*/ +FireErrorType TurretClass::Can_Fire(TARGET target, int which) const +{ + DirType dir; // The facing to impart upon the projectile. + int diff; + FireErrorType fire = DriveClass::Can_Fire(target, which); + + if (fire == FIRE_OK) { + WeaponTypeClass const * weapon = (which == 0) ? &Weapons[Class->Primary] : &Weapons[Class->Secondary]; + + /* + ** If this unit cannot fire while moving, then bail. + */ + if ((!Class->IsTurretEquipped || Class->IsLockTurret) && Target_Legal(NavCom)) { + return(FIRE_MOVING); + } + + /* + ** If the turret is rotating and the projectile isn't a homing type, then + ** firing must be delayed until the rotation stops. + */ + if (!IsFiring && IsRotating && !BulletTypeClass::As_Reference(weapon->Fires).IsHoming) { + return(FIRE_ROTATING); + } + + dir = Direction(target); + + /* + ** Determine if the turret facing isn't too far off of facing the target. + */ + if (Class->IsTurretEquipped) { + diff = SecondaryFacing.Difference(dir); + } else { + diff = PrimaryFacing.Difference(dir); + } + diff = ABS(diff); + + /* + ** Special flame tank logic. + */ + if (weapon->Fires == BULLET_FLAME) { + if (Dir_Facing(dir) == Dir_Facing(PrimaryFacing)) { + diff = 0; + } + } + + if (BulletTypeClass::As_Reference(weapon->Fires).IsHoming) { + diff >>= 2; + } + if (diff < 8) { + return(DriveClass::Can_Fire(target, which)); + } + return(FIRE_FACING); + } + return(fire); +} + + +/*********************************************************************************************** + * TurretClass::Fire_Coord -- Determines the coorindate that projectile would appear. * + * * + * Use this routine to determine the exact coordinate that a projectile would appear if it * + * were fired from this unit. For units with turrets, typically, this would be at the end * + * of the barrel. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with coordinate of where a projectile should appear if this unit were * + * to fire one. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/28/1994 JLB : Created. * + *=============================================================================================*/ +COORDINATE TurretClass::Fire_Coord(int which) const +{ + COORDINATE coord = Center_Coord(); + int dist = 0; + int lateral = 0; + DirType dir = PrimaryFacing.Current(); + + if (Class->IsTurretEquipped) { + dir = SecondaryFacing.Current(); + } + + switch (Class->Type) { + case UNIT_GUNBOAT: + coord = Coord_Move(coord, PrimaryFacing.Current(), Pixel2Lepton[Class->TurretOffset]); + dist = 0x0060; + break; + + case UNIT_ARTY: + coord = Coord_Move(coord, DIR_N, 0x0040); + dist = 0x0060; + break; + + case UNIT_FTANK: + dist = 0x30; + if (IsSecondShot) { + coord = Coord_Move(coord, (DirType)(dir+DIR_E), 0x20); + } else { + coord = Coord_Move(coord, (DirType)(dir+DIR_W), 0x20); + } + break; + + case UNIT_HTANK: + coord = Coord_Move(coord, DIR_N, 0x0040); + if (which == 0) { + dist = 0x00C0; + lateral = 0x0028; + } else { + dist = 0x0008; + lateral = 0x0040; + } + if (IsSecondShot) { + coord = Coord_Move(coord, (DirType)(dir+DIR_E), lateral); + } else { + coord = Coord_Move(coord, (DirType)(dir+DIR_W), lateral); + } + break; + + case UNIT_LTANK: + coord = Coord_Move(coord, DIR_N, 0x0020); + dist = 0x00C0; + break; + + case UNIT_MTANK: + coord = Coord_Move(coord, DIR_N, 0x0030); + dist = 0x00C0; + break; + + case UNIT_APC: + case UNIT_JEEP: + case UNIT_BUGGY: + coord = Coord_Move(coord, DIR_N, 0x0030); + dist = 0x0030; + break; + } + + if (dist) { + coord = Coord_Move(coord, dir, dist); + } + + return(coord); +} + + +/*********************************************************************************************** + * TurretClass::Unlimbo -- Unlimboes turret object. * + * * + * This routine is called when a turret equipped unit unlimboes. It sets the turret to * + * face the same direction as the body. * + * * + * INPUT: coord -- The coordinate where the unit is unlimboing. * + * * + * dir -- The desired body and turret facing to use. * + * * + * OUTPUT: Was the unit unlimboed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +bool TurretClass::Unlimbo(COORDINATE coord, DirType dir) +{ + if (DriveClass::Unlimbo(coord, dir)) { + SecondaryFacing = dir; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TurretClass::Fire_Direction -- Determines the directinon of firing. * + * * + * This routine will return with the facing that a projectile will travel if it was * + * fired at this instant. The facing should match the turret facing for those units * + * equipped with a turret. If the unit doesn't have a turret, then it will be the facing * + * of the body. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the default firing direction for a projectile. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +DirType TurretClass::Fire_Direction(void) const +{ + if (Class->IsTurretEquipped) { + if (*this == UNIT_MSAM) { + int diff1 = SecondaryFacing.Difference(DIR_E); + int diff2 = SecondaryFacing.Difference(DIR_W); + diff1 = ABS(diff1); + diff2 = ABS(diff2); + int diff = MIN(diff1, diff2); + int adj = Fixed_To_Cardinal(ABS(SecondaryFacing.Difference(DIR_N)), 64-diff); + if (SecondaryFacing.Difference(DIR_N) < 0) { + return(DirType)(SecondaryFacing - (DirType)adj); + } else { + return(DirType)(SecondaryFacing + (DirType)adj); + } + } + return(SecondaryFacing.Current()); + } + + return(PrimaryFacing.Current()); +} diff --git a/TURRET.H b/TURRET.H new file mode 100644 index 0000000..9af7255 --- /dev/null +++ b/TURRET.H @@ -0,0 +1,85 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\turret.h_v 2.17 16 Oct 1995 16:48:00 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TURRET.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 25, 1994 * + * * + * Last Update : April 25, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TURRET_H +#define TURRET_H + +#include "drive.h" + +class TurretClass : public DriveClass +{ + public: + + /* + ** This is the timer that controls the reload rate. The MSAM rocket + ** launcher is the primary user of this. + */ + TCountDownTimerClass Reload; + + /* + ** This is the facing of the turret. It can be, and usually is, + ** rotated independently of the body it is attached to. + */ + FacingClass SecondaryFacing; + + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + virtual bool Unlimbo(COORDINATE , DirType facing=DIR_N); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + protected: + TurretClass(UnitType classid, HousesType house); + TurretClass(void); + virtual ~TurretClass(void); + + BulletClass * Fire_At(TARGET target, int which); + + virtual DirType Fire_Direction(void) const; + virtual FireErrorType Can_Fire(TARGET target, int which) const; + virtual bool Ok_To_Move(DirType facing); + virtual void AI(void); + virtual COORDINATE Fire_Coord(int which) const; +}; + + +#endif diff --git a/TXTLABEL.CPP b/TXTLABEL.CPP new file mode 100644 index 0000000..71a88aa --- /dev/null +++ b/TXTLABEL.CPP @@ -0,0 +1,98 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\txtlabel.cpv 1.9 16 Oct 1995 16:49:44 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TXTLABEL.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : 02/06/95 * + * * + * Last Update : February 6, 1995 [BR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * TextLabelClass -- Constructor * + * * + * INPUT: * + * txt pointer to text buffer to print from * + * x x-coord for text printing * + * y y-coord for text printing * + * color color to print in * + * style style to print (determines the meaning of x & y) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +TextLabelClass::TextLabelClass(char *txt, int x, int y, int color, + TextPrintType style) : GadgetClass(x,y,1,1,0,0) +{ + Text = txt; + Color = color; + Style = style; + UserData = 0; + PixWidth = -1; + Segments = 0; +} + + +/*********************************************************************************************** + * Draw_Me -- Graphical update routine * + * * + * INPUT: * + * forced true = draw regardless of the current redraw flag state * + * * + * OUTPUT: * + * true = gadget was redrawn, false = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +virtual int TextLabelClass::Draw_Me(int forced) +{ + if (GadgetClass::Draw_Me(forced)) { + if (PixWidth == -1) { + Fancy_Text_Print("%s", X, Y, Color, TBLACK, Style, Text); + } else { + Conquer_Clip_Text_Print(Text, X, Y, Color, TBLACK, Style, PixWidth); + } + return(true); + } + return(false); +} diff --git a/TXTLABEL.H b/TXTLABEL.H new file mode 100644 index 0000000..8b369ac --- /dev/null +++ b/TXTLABEL.H @@ -0,0 +1,72 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\txtlabel.h_v 1.14 16 Oct 1995 16:46:08 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TXTLABEL.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : 02/06/95 * + * * + * Last Update : February 6, 1995 [BR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TXTLABEL_H +#define TXTLABEL_H + +class TextLabelClass : public GadgetClass +{ + public: + /* + ** Constructor/Destructor + */ + TextLabelClass(char *txt, int x, int y, int color, TextPrintType style); + + /* + ** Overloaded draw routine + */ + virtual int Draw_Me(int forced = false); + + /* + ** Sets the displayed text of the label + */ + virtual void Set_Text(char *txt) {Text = txt;}; + + /* + ** General-purpose data field + */ + unsigned long UserData; + TextPrintType Style; + char *Text; + int Color; + int PixWidth; + char Segments; + unsigned short CRC; +}; + +#endif + diff --git a/TXTPRNT.ASM b/TXTPRNT.ASM new file mode 100644 index 0000000..95bf7b1 --- /dev/null +++ b/TXTPRNT.ASM @@ -0,0 +1,514 @@ +; +; Command & Conquer(tm) +; Copyright 2025 Electronic Arts Inc. +; +; 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 3 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 General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . +; + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : TXTPRNT.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 17, 1995 * +;* * +;* Last Update : January 17, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Buffer_Print -- Assembly text print to a buffer * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +;INCLUDE "mcgaprim.inc" +;INCLUDE ".\gbuffer.inc" + +GLOBAL C Buffer_Print : NEAR + +STRUC GraphicViewPort +GVPOffset DD ? ; offset to virtual viewport +GVPWidth DD ? ; width of virtual viewport +GVPHeight DD ? ; height of virtual viewport +GVPXAdd DD ? ; x mod to get to next line +GVPXPos DD ? ; x pos relative to Graphic Buff +GVPYPos DD ? ; y pos relative to Graphic Buff +GVPPitch DD ? ; modulo of graphic view port +GVPBuffPtr DD ? ; ptr to associated Graphic Buff +ENDS + + +;*=========================================================================* +;* Extern the font pointer which is defined by the font class * +;*=========================================================================* +GLOBAL C FontPtr:DWORD +GLOBAL C FontXSpacing:DWORD +GLOBAL C FontYSpacing:DWORD +GLOBAL C ColorXlat:BYTE + +;*=========================================================================* +;* Define the necessary equates for structures and bounds checking * +;*=========================================================================* +; The header of the font file looks like this: +; UWORD FontLength; 0 +; BYTE FontCompress; 2 +; BYTE FontDataBlocks; 3 +; UWORD InfoBlockOffset; 4 +; UWORD OffsetBlockOffset; 6 +; UWORD WidthBlockOffset; 8 +; UWORD DataBlockOffset; 10 +; UWORD HeightOffset; 12 +; For this reason the following equates have these values: +FONTINFOBLOCK EQU 4 +FONTOFFSETBLOCK EQU 6 +FONTWIDTHBLOCK EQU 8 +FONTDATABLOCK EQU 10 +FONTHEIGHTBLOCK EQU 12 + +FONTINFOMAXHEIGHT EQU 4 +FONTINFOMAXWIDTH EQU 5 + + +LOCALS ?? +;*=========================================================================* +;* Define the color xlate table in the data segment * +;*=========================================================================* + DATASEG + +ColorXlat DB 000H,001H,002H,003H,004H,005H,006H,007H + DB 008H,009H,00AH,00BH,00CH,00DH,00EH,00FH + + DB 001H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 002H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 003H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 004H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 005H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 006H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 007H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 008H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 009H,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00AH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00BH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00CH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00DH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00EH,000H,000H,000H,000H,000H,000H,000H + DB 000H,000H,000H,000H,000H,000H,000H,000H + + DB 00FH + + CODESEG + + +;*************************************************************************** +;* Buffer_Print -- Assembly text print to graphic buffer routine * +;* * +;* * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 01/17/1995 PWG : Created. * +;*=========================================================================* + PROC Buffer_Print C near + USES ebx,ecx,edx,esi,edi + + ARG this_object:DWORD + ARG string:DWORD + ARG x_pixel:DWORD + ARG y_pixel:DWORD + ARG fcolor:DWORD + ARG bcolor:DWORD + + LOCAL infoblock:DWORD ; pointer to info block + LOCAL offsetblock:DWORD ; pointer to offset block (UWORD *) + LOCAL widthblock:DWORD ; pointer to width block (BYTE *) + LOCAL heightblock:DWORD ; pointer to height block (UWORD *) + + LOCAL curline:DWORD ; pointer to first column of current row. + LOCAL bufferwidth:DWORD ; width of buffer (vpwidth + Xadd) + LOCAL nextdraw:DWORD ; bufferwidth - width of cur character. + LOCAL startdraw:DWORD ; where next character will start being drawn. + + LOCAL char:DWORD ; current character value. + + LOCAL maxheight:BYTE ; max height of characters in font. + LOCAL bottomblank:BYTE ; amount of empty space below current character. + LOCAL charheight:BYTE ; true height of current character. + LOCAL vpwidth:DWORD + LOCAL vpheight:DWORD + LOCAL original_x:DWORD ; Starting X position. + + +;-------------------------------- Where to draw ----------------------------------------------- + ; Set up memory location to start drawing. + mov ebx,[this_object] ; get a pointer to dest + mov eax,[(GraphicViewPort ebx).GVPHeight] ; get height of viewport + mov [vpheight],eax ; save off height of viewport + mov eax,[(GraphicViewPort ebx).GVPWidth] ; get width of viewport + mov [vpwidth],eax ; save it off for later + add eax,[(GraphicViewPort ebx).GVPXAdd] ; add in xadd for bytes_per_line + add eax,[(GraphicViewPort ebx).GVPPitch] ; add in pitch for direct daraw + mov [bufferwidth],eax ; save it off for later use. + + mul [y_pixel] ; multiply rowsize * y_pixel start. + mov edi,[(GraphicViewPort ebx).GVPOffset] ; get start of the viewport + add edi,eax ; add y position to start of vp + mov [curline],edi ; save 0,y address for line feed stuff. + add edi,[x_pixel] ; add to get starting column in starting row. + mov [startdraw],edi ; save it off. + + mov eax,[x_pixel] + mov [original_x],eax + +;-------------------------------- Create block pointers ---------------------------------------- + ; Get the pointer to the font. + ; We could check for NULL but why waste the time. + ; It is up to programmer to make sure it is set. + mov esi,[FontPtr] ; Get the font pointer + or esi,esi + jz ??overflow + + ; Set up some pointers to the different memory blocks. + ; esi (FontPtr) is added to each to get the true address of each block. + ; Many registers are used for P5 optimizations. + ; ebx is used for InfoBlock which is then used in the next section. + movzx eax,[WORD PTR esi+FONTOFFSETBLOCK] ; get offset to offset block + movzx ebx,[WORD PTR esi+FONTINFOBLOCK] ; get offset to info block (must be ebx for height test) + movzx ecx,[WORD PTR esi+FONTWIDTHBLOCK] ; get offset to width block + movzx edx,[WORD PTR esi+FONTHEIGHTBLOCK] ; get offset to height block + + add eax,esi ; add offset of FontPtr to offset block + add ebx,esi ; add offset of FontPtr to info block + add ecx,esi ; add offset of FontPtr to width block + add edx,esi ; add offset of FontPtr to height block + + mov [offsetblock],eax ; save offset to offset block + mov [infoblock],ebx ; save offset to info block + mov [widthblock],ecx ; save offset to width block + mov [heightblock],edx ; save offset to height block + +;------------------------------------------ Test for fit ---------------------------------------------- + ; Test to make sure the height of the max character will fit on this line + ; and and not fall out of the viewport. + ; remember we set ebx to FONTINFOBLOCK above. + movzx eax,[BYTE PTR ebx + FONTINFOMAXHEIGHT]; get the max height in font. + mov [maxheight],al ; save it for later use. + add eax,[y_pixel] ; add current y_value. + cmp eax,[vpheight] ; are we over the edge? + jg ??overflow ; if so, we're outa here. + + mov [y_pixel],eax ; save for next line feed. y value for next line. + + cld ; Make sure we are always forward copying. + +;------------------------ Set palette foreground and background ---------------------------------- + mov eax,[fcolor] ; foreground color + mov [ColorXlat+1],al + mov [ColorXlat+16],al + + mov eax,[bcolor] ; background color + mov [ColorXlat],al + +;------------------------------------------------------------------------------------------------- +;----------------------------------------- Main loop ---------------------------------------------- + ; Now we go into the main loop of reading each character in the string and doing + ; something with it. +??next_char: + ; while (*string++) + xor eax,eax ; zero out since we will just load al. + mov esi,[string] ; get address on next character. + lodsb ; load the character into al. + test eax,0FFH ; test to see if character is a NULL + jz ??done ; character is NULL, get outa here. + + mov edi,[startdraw] ; Load the starting address. + + mov [string],esi ; save index into string. (incremented by lodsb) + + cmp al,10 ; is the character a line feed? + je ??line_feed ; if so, go to special case. + + cmp al,13 ; is the character a line feed? + je ??line_feed ; if so, go to special case. + + mov [char],eax ; save the character off for later reference. + mov ebx,eax ; save it in ebx for later use also. + + add eax,[widthblock] ; figure address of width of character. + mov ecx,[x_pixel] ; get current x_pixel. + movzx edx,[BYTE PTR eax] ; get the width of the character in dl. + add ecx,edx ; add width of char to current x_pixel. + mov eax,[FontXSpacing] + add ecx,eax + add [startdraw],edx ; save start draw for next character. + add [startdraw],eax ; adjust for the font spacing value + + cmp ecx,[vpwidth] ; is the pixel greater then the vp width? + jg ??force_line_feed ; if so, force a line feed. + + mov [x_pixel],ecx ; save value of start of next character. + mov ecx,[bufferwidth] ; get amount to next y same x (one row down) + sub ecx,edx ; take the current width off. + mov [nextdraw],ecx ; save it to add to edi when done with a row. + + ; At this point we got the character. It is now time to find out specifics + ; about drawing the darn thing. + ; ebx = char so they can be used as an indexes. + ; edx = width of character for loop later. + + ; get offset of data for character into esi. + shl ebx,1 ; mult by 2 to later use as a WORD index. + mov esi,[offsetblock] ; get pointer to begining of offset block. + add esi,ebx ; index into offset block. + movzx esi,[WORD PTR esi] ; get true offset into data block from FontPtr. + add esi,[FontPtr] ; Now add FontPtr address to get true address. + + ; Get top and bottom blank sizes and the true height of the character. + add ebx,[heightblock] ; point ebx to element in height array. + mov al,[ebx+1] ; load the data height into dl. + mov cl,[ebx] ; load the first data row into cl. + mov bl,[maxheight] ; get the max height of characters. + mov [charheight],al ; get number of rows with data. + add al,cl ; add the two heights. + sub bl,al ; subract topblank + char height from maxheight. + mov [bottomblank],bl ; save off the number of blank rows on the bottom. + ; leaving this section: + ; dl is still the width of the character. + ; cl is the height of the top blank area. + + mov ebx,OFFSET ColorXlat ; setup ebx for xlat commands. + mov dh,dl ; save the width of the character to restore each loop. + + cmp cl,0 ; is there any blank rows on top? + jz ??draw_char ; if not go and draw the real character. + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jnz ??loop_top ; if not go and write the color + +;----------------------------------------- skip Top blank area ---------------------------------------- + ; this case, the top is transparrent, but we need to increase our dest pointer to correct row. + movzx eax,cl ; get number of rows into eax; + mov ecx,edx ; save width since edx will be destroyed by mul. + mul [bufferwidth] ; multiply that by the width of the buffer. + mov edx,ecx ; restore the width + add edi,eax ; update the pointer. + jmp short ??draw_char ; now go draw the character. + +;----------------------------------------- fill Top blank area ---------------------------------------- + ; edi was set a long time ago. + ; al is the translated color +??loop_top: + stosb ; store the value + dec dh ; decrement our width. + jnz ??loop_top ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + dec cl ; decrement or row count + mov dh,dl ; restore width in dh for loop. + jz ??draw_char ; we are done here, go draw the character. + jmp short ??loop_top ; go back to top of loop. + + +;----------------------------------------- Draw character ---------------------------------------------- +??draw_char: + movzx ecx,[charheight] ; get the height of character to count down rows. + test ecx,ecx ; is there any data? (blank would not have any) + jz ??next_char ; if no data, go on to next character. + +??while_data: + lodsb ; get byte value from font data + mov ah,al ; save hinibble + and eax,0F00FH ; mask of low nibble in al hi nibble in ah. + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiplo ; skip over write + mov [es:edi],al ; write it out +??skiplo: + inc edi + dec dh ; decrement our width. + jz short ??nextrow ; check if done with width of char + + mov al,ah ; restore to get + ; test the time difference between looking up in a large table when shr al,4 is not done as + ; compared to using only a 16 byte table when using the shr al,4 + ;shr al,4 ; shift the hi nibble down to low nibble + xlat [ebx] ; get new color + + test al,al ; is it a transparent? + jz short ??skiphi ; skip over write + mov [es:edi],al ; write it out +??skiphi: + + inc edi + dec dh ; decrement our width. + jnz short ??while_data ; check if done with width of char + +??nextrow: + add edi,[nextdraw] ; go to next line. + dec ecx ; decrement the number of rows to go + mov dh,dl ; restore our column count for row. + jnz ??while_data ; more data for character. + + ; Now it is time to setup for clearing out the bottom of the character. + movzx ecx,[bottomblank] ; get amount on bottom that is blank + cmp ecx,0 ; if there is no blank bottom... + jz ??next_char ; then skip to go to next character + + xor eax,eax ; get color 0 for background. + xlat [ebx] ; get translated color into al + test al,al ; is it transparent black + jz ??next_char ; skip the top black section to let the background through + + mov dh,dl ; restore width in dh for loop. + +;----------------------------------------- Blank below character ----------------------------------- +??loop_bottom: + stosb ; store the value + dec dh ; decrement our width. + jnz ??loop_bottom ; if more width, continue on. + + add edi,[nextdraw] ; add amount for entire row. + + mov dh,dl ; restore width in dh for loop. + dec cl ; decrement or row count + jz ??next_char ; we are done here, go to the next character. + jmp short ??loop_bottom ; go back to top of loop. + +;----------------------------------- end of next_char (main) loop ------------------------------------ +;------------------------------------------------------------------------------------------------- + + +;----------------------------------- special case line feeds ---------------------------------------- + +??force_line_feed: + ; decrement pointer *string so that it will be back at same character + ; when it goes through the loop. + mov eax,[string] ; get string pointer. + dec eax ; decrement it to point to previos char + mov [string],eax ; save it back off. + xor eax,eax + ; Now go into the line feed code..... + +??line_feed: + mov bl,al + mov edx,[y_pixel] ; get the current y pixel value. + movzx ecx,[maxheight] ; get max height for later use. + add ecx,[FontYSpacing] + add edx,ecx ; add max height to y_pixel + cmp edx,[vpheight] ; are we over the edge? + jg ??overflow ; if so, we are outa here. + + mov eax,[bufferwidth] ; get bytes to next line. + mov edi,[curline] ; get start of current line. + mul ecx ; mult max height * next line. + + add edi,eax ; add adjustment to current line. + add [y_pixel],ecx ; increment to our next y position. +;;; DRD + mov [curline],edi ; save it off for next line_feed. + + ; Move the cursor to either the left edge of the screen + ; or the left margin of the print position depending + ; on whether or was specified. = left margin + ; = left edge of screen + xor eax,eax + cmp bl,10 + je ??lfeed + mov eax,[original_x] +??lfeed: + mov [x_pixel],eax ; zero out x_pixel + + add edi,eax +;;; DRD mov [curline],edi ; save it off for next line_feed. + mov [startdraw],edi ; save it off so we know where to draw next char.w + + jmp ??next_char + +??overflow: + mov [startdraw],0 ; Indicate that there is no valid next pos. +??done: + mov eax,[startdraw] ; return this so calling routine + ret ; can figure out where to draw next. + + ENDP Buffer_Print + + + +;*************************************************************************** +;* GET_FONT_PALETTE_PTR -- Returns a pointer to the 256 byte font palette * +;* * +;* INPUT: none * +;* * +;* OUTPUT: none * +;* * +;* PROTO: void *Get_Font_Palette_Ptr(void); * +;* * +;* HISTORY: * +;* 08/18/1995 PWG : Created. * +;*=========================================================================* + + GLOBAL C Get_Font_Palette_Ptr:NEAR + + PROC Get_Font_Palette_Ptr C near + + mov eax, OFFSET ColorXlat + ret + + ENDP Get_Font_Palette_Ptr + + +END diff --git a/TYPE.H b/TYPE.H new file mode 100644 index 0000000..f6237cd --- /dev/null +++ b/TYPE.H @@ -0,0 +1,1970 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\type.h_v 2.20 16 Oct 1995 16:45:42 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TYPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TYPE_H +#define TYPE_H + +#include "mission.h" +#include "target.h" + +class MapEditClass; +class HouseClass; + + +/********************************************************************** +** This is the constant data associated with a weapon. Some objects +** can have multiple weapons and this class is used to isolate and +** specify this data in a convenient and selfcontained way. +*/ +class WeaponTypeClass +{ + public: + + /* + ** This is the unit class of the projectile fired. A subset of the unit types + ** represent projectiles. It is one of these classes that is specified here. + ** If this object does not fire anything, then this value will be UNIT_NONE. + */ + BulletType Fires; + + /* + ** This is the damage (explosive load) to be assigned to the projectile that + ** this object fires. + */ + unsigned char Attack; + + /* + ** Objects that fire (which can be buildings as well) will fire at a + ** frequency controlled by this value. This value serves as a count + ** down timer between shots. The smaller the value, the faster the + ** rate of fire. + */ + unsigned char ROF; + + /* + ** When this object fires, the range at which it's projectiles travel is + ** controlled by this value. The value represents the number of cells the + ** projectile will travel. Objects outside of this range will not be fired + ** upon (in normal circumstances). + */ + int Range; + + /* + ** This is the typical sound generated when firing. + */ + VocType Sound; + + /* + ** This is the animation to display at the firing coordinate. + */ + AnimType Anim; +}; + + +/********************************************************************** +** Each of the warhead types has specific characteristics. This structure +** holds these characteristics. +*/ +class WarheadTypeClass +{ + public: + + /* + ** This value control how damage from this warhead type will reduce + ** over distance. The larger the number, the less the damage is reduced + ** the farther the distance from the source of the damage. + */ + int SpreadFactor; + + /* + ** If this warhead type can destroy walls, then this flag will be true. + */ + bool IsWallDestroyer; + + /* + ** If this warhead can destroy wooden walls, then this flag will be true. + */ + bool IsWoodDestroyer; + + /* + ** Does this warhead damage tiberium? + */ + bool IsTiberiumDestroyer; + + /* + ** The warhead damage is reduced depending on the the type of armor the + ** defender has. This table is what gives weapons their "character". + */ + unsigned char Modifier[ARMOR_COUNT]; +}; + + +/********************************************************************** +** Each house has certain unalienable characteristics. This structure +** elaborates these. +*/ +class HouseTypeClass { + public: + + /* + ** This is the house number (enum). This is a unique identification + ** number for the house. + */ + HousesType House; + + /* + ** The INI name of the house is pointed to by this element. This is the + ** identification name used in the scenario INI file. + */ + char const *IniName; + + /* + ** The full name (translated) of the house is identified by this number. + ** The actual text of the name is located in a text file loaded at run + ** time. + */ + int FullName; + + /* + ** This is the filename suffix to use when creating a house specific + ** file name. It is three characters long. + */ + char Suffix[4]; + + /* + ** This is the "lemon percentage" to use when determining if a particular + ** object owned by this house is to be flagged as a "lemon". Objects so + ** flagged have a greater break-down chance. The percentage is expressed + ** as a fixed point number with 0x000 meaning 0% and 0x100 meaning 100%. + */ + unsigned Lemon; + + /* + ** Each house is assigned a unique identification color to be used on the + ** radar map and other color significant areas. + */ + unsigned char Color; + + unsigned char BrightColor; + + /* + ** This points to the default remap table for this house. + */ + unsigned char const * RemapTable; + PlayerColorType RemapColor; + + /* + ** This is a unique ASCII character used when constructing filenames. It + ** serves a similar purpose as the "Suffix" element, but is only one + ** character long. + */ + char Prefix; + + //------------------------------------------------------------------------ + HouseTypeClass(HousesType house, + char const *ini, + int fullname, + char const *ext, + int lemon, + int color, + int bright_color, + PlayerColorType remapcolor, + unsigned char const * remap, + char prefix); + + static HousesType From_Name(char const *name); + static HouseTypeClass const & As_Reference(HousesType house); + static void One_Time(void); + + private: + static HouseTypeClass const * const Pointers[HOUSE_COUNT]; +}; + + +/*************************************************************************** +** This is the abstract type class. It holds information common to all +** objects that might exist. This contains the name of +** the object type. +*/ +class AbstractTypeClass +{ + public: + + /* + ** This is the internal control name of the object. This name does + ** not change regardless of language specified. This is the name + ** used in scenario control files and for other text based unique + ** identification purposes. + */ + char IniName[9]; + + /* + ** The translated (language specific) text name number of this object. + ** This number is used to fetch the object's name from the language + ** text file. Whenever the name of the object needs to be displayed, + ** this is used to determine the text string. + */ + int Name; + + AbstractTypeClass(void) {}; + AbstractTypeClass(int name, char const * ini); + virtual RTTIType What_Am_I(void) const; + + virtual COORDINATE Coord_Fixup(COORDINATE coord) const; + virtual int Full_Name(void) const; + void Set_Name(char const *buf) const + { + strncpy((char *)IniName, buf, sizeof(IniName)); + ((char &)IniName[sizeof(IniName)-1]) = '\0'; + }; + virtual unsigned short Get_Ownable(void) const; +}; + + +/*************************************************************************** +** This the the common base class of game objects. Since these values +** represent the unchanging object TYPES, this data is initialized at game +** start and not changed during play. It is "const" data. +*/ +class ObjectTypeClass : public AbstractTypeClass +{ + public: + + /* + ** Is this object squashable by heavy vehicles? If it is, then the vehicle + ** can travel over this object and destroy it in the process. + */ + unsigned IsCrushable:1; + + /* + ** Does this object type NOT show up on radar scans? If true, then in any + ** radar display, only the underlying ground will be show, not this object. + ** Most terrain falls into this category, but only a few special real units/buildings + ** do. + */ + unsigned IsStealthy:1; + + /* + ** It is legal to "select" some objects in the game. If it is legal to select this + ** object type then this flag will be true. Selected game objects typically display + ** a floating health bar and allows special user I/O control. + */ + unsigned IsSelectable:1; + + /* + ** Can this object be the target of an attack or move command? Typically, only objects + ** that take damage or can be destroyed are allowed to be a target. + */ + unsigned IsLegalTarget:1; + + /* + ** "Insignificant" objects will not be announced when they are destroyed or when they + ** appear. Terrain elements and some lesser vehicles have this characteristic. + */ + unsigned IsInsignificant:1; + + /* + ** Is this object immune to normal combat damage? Rocks and other inert type terrain + ** object are typically of this type. + */ + unsigned IsImmune:1; + + /* + ** If this terrain object is flammable (such as trees are) then this + ** flag will be true. Flammable objects can catch fire if damaged by + ** flame type weapons. + */ + unsigned IsFlammable:1; + + /* + ** "Sentient" objects are ones that have logic AI processing performed on them. All + ** vehicles, buildings, infantry, and aircraft are so flagged. Terrain elements also + ** fall under this category, but only because certain animation effects require this. + */ + unsigned IsSentient:1; + + /* + ** The defense of this object is greatly affected by the type of armor + ** it possesses. This value specifies the type of armor. + */ + ArmorType Armor; + + /* + ** This is the maximum strength of this object type. + */ + unsigned short MaxStrength; + + /* + ** These point to the shape imagery for this object type. Since the shape imagery + ** exists in a separate file, the data is filled in after this object is constructed. + ** The "mutable" keyword allows easy modification to this otherwise const object. + */ + void const * ImageData; + + /* + ** This points to the radar imagery for this object. + */ + void const * RadarIcon; + + //-------------------------------------------------------------------- + ObjectTypeClass( bool is_sentient, + bool is_flammable, + bool is_crushable, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + int fullname, + char const *name, + ArmorType armor, + unsigned short strength); + + static void One_Time(void); + + virtual int Max_Pips(void) const; + virtual void Dimensions(int &width, int &height) const; + virtual bool Create_And_Place(CELL , HousesType =HOUSE_NONE) const = 0; + virtual int Cost_Of(void) const; + virtual int Time_To_Build(HousesType house) const; + virtual ObjectClass * Create_One_Of(HouseClass *) const = 0; + virtual short const * Occupy_List(bool placement=false) const; + virtual short const * Overlap_List(void) const; + virtual BuildingClass * Who_Can_Build_Me(bool, bool, HousesType) const; + virtual void const * Get_Cameo_Data(void) const; + void const * Get_Image_Data(void) const {return ImageData;}; + void const * Get_Radar_Data(void) const {return RadarIcon;}; + + #ifdef SCENARIO_EDITOR + virtual void Display(int, int, WindowNumberType, HousesType) const {}; + #endif + + static void const * SelectShapes; + static void const * PipShapes; +}; + + +/*************************************************************************** +** This class is the common data for all objects that can be owned, produced, +** or delivered as reinforcements. These are objects that typically contain +** crews and weapons -- the fighting objects of the game. +*/ +class TechnoTypeClass : public ObjectTypeClass +{ + public: + + /* + ** If this object can serve as a good leader for a group selected + ** series of objects, then this flag will be true. Unarmed or + ** ability challenged units do not make good leaders. + */ + unsigned IsLeader:1; + + /* + ** Does this object have the ability to detect the presence of a nearby + ** cloaked object? + */ + unsigned IsScanner:1; + + /* + ** If this object is always given its proper name rather than a generic + ** name, then this flag will be true. Typically, civilians and Dr. Mobius + ** fall under this catagory. + */ + unsigned IsNominal:1; + + /* + ** If the artwork for this object (only for generics) is theater specific, then + ** this flag will be true. Civilian buildings are a good example of this. + */ + unsigned IsTheater:1; + + /* + ** Does this object type contain a rotating turret? Gun emplacements, SAM launchers, + ** and many vehicles contain a turret. If a turret is present, special rendering and + ** combat logic must be performed. + */ + unsigned IsTurretEquipped:1; + + /* + ** Certain units and buildings fire two shots in quick succession. If this is + ** the case, then this flag is true. + */ + unsigned IsTwoShooter:1; + + /* + ** Certain objects can be repaired. For buildings, they repair "in place". For units, + ** they must travel to a repair center to be repaired. If this flag is true, then + ** allow the player or computer AI to repair the object. + */ + unsigned IsRepairable:1; + + /* + ** Is this object possible to be constructed? Certain buildings and units cannot + ** be constructed using normal means. They are either initially placed in the scenario + ** or can only arrive by pre arranged reinforcement scheduling. Civilian buildings and + ** vehicles are typical examples of this type of object. They would set this flag to + ** false. + */ + unsigned IsBuildable:1; + + /* + ** Does this object contain a crew? If it does, then when the object is destroyed, there + ** is a distinct possibility that infantry will "pop out". Only units with crews can + ** become "heros". + */ + unsigned IsCrew:1; + + /* + ** Is this object typically used to transport reinforcements or other cargo? + ** Transport aircraft, helicopters, and hovercraft are typicall examples of + ** this. + */ + unsigned IsTransporter:1; + + /* + ** Most objects have the ability to reveal the terrain around themselves. + ** This sight range (expressed in cell distance) is specified here. If + ** this value is 0, then this unit never reveals terrain. Bullets are + ** typically of this nature. + */ + int SightRange; + + /* + ** These values control the cost to produce, the time to produce, and + ** the scenario when production can first start. + */ + int Cost; + unsigned char Scenario; + + /* + ** Special build prerequisite control values. These are primarily used for + ** multi-player or special events. + */ + unsigned char Level; + long Pre; + + /* + ** The risk and reward values are used to determine targets and paths + ** toward targets. When heading toward a target, a path of least + ** risk will be followed. When picking a target, the object of + ** greatest reward will be selected. The values assigned are + ** arbitrary. + */ + int Risk,Reward; + + /* + ** This value indicates the maximum speed that this object can achieve. + */ + MPHType MaxSpeed; + + /* + ** This is the maximum number of ammo shots this object can hold. If + ** this number is -1, then this indicates unlimited ammo. + */ + int MaxAmmo; + + /* + ** This is a bit field representing the houses that are allowed to + ** own (by normal means) this particular object type. This value is + ** typically used in production contexts. It is possible for a side + ** to take possession of an object type otherwise not normally allowed. + ** This event usually occurs as a result of capture. + */ + unsigned short Ownable; + + /* + ** This is the small icon image that is used to display the object in + ** the sidebar for construction selection purposes. + */ + void const * CameoData; + + /* + ** These are the weapons that this techno object is armed with. + */ + WeaponType Primary; + WeaponType Secondary; + + //-------------------------------------------------------------------- + TechnoTypeClass( + int name, + char const *ininame, + unsigned char level, + long pre, + bool is_leader, + bool is_scanner, + bool is_nominal, + bool is_transporter, + bool is_flammable, + bool is_crushable, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_theater, + bool is_twoshooter, + bool is_turret_equipped, + bool is_repairable, + bool is_buildable, + bool is_crew, + int ammo, + unsigned short strength, + MPHType maxspeed, + int sightrange, + int cost, + int scenario, + int risk, + int reward, + int ownable, + WeaponType primary, + WeaponType secondary, + ArmorType armor); + + virtual int Raw_Cost(void) const; + virtual int Max_Passengers(void) const; + virtual int Repair_Cost(void) const; + virtual int Repair_Step(void) const; + virtual void const * Get_Cameo_Data(void) const; + virtual int Cost_Of(void) const; + virtual int Time_To_Build(HousesType house) const; + virtual unsigned short Get_Ownable(void) const; +}; + + +/*************************************************************************** +** Building types need some special information custom to buildings. This +** is a derived class that elaborates these additional data elements. +*/ +class BuildingTypeClass : public TechnoTypeClass { + enum BuildingTypeClassRepairEnums { + //REPAIR_COST=1, // Cost to repair a single "step". + REPAIR_PERCENT=102, // 40% fixed point number. + REPAIR_STEP=5 // Number of damage points recovered per "step". + }; + + public: + + /* + ** This flag controls whether the building is equiped with a dirt + ** bib or not. A building with a bib has a dirt patch automatically + ** attached to the structure when it is placed. + */ + unsigned IsBibbed:1; + + /* + ** If this building is a special wall type, such that it exists as a building + ** for purposes of construction but transforms into an overlay wall object when + ** it is placed on the map, then this flag will be true. + */ + unsigned IsWall:1; + + /* + ** Some buildings are producers. This flag will be true in that case. Producer, + ** or factory, type buildings have special logic performed. + */ + unsigned IsFactory:1; + + /* + ** Buildings can have either simple or complex damage stages. If simple, + ** then the second to the last frame is the half damage stage, and the last + ** frame is the complete damage stage. For non-simple damage, buildings + ** have a complete animation set for damaged as well as undamaged condition. + ** Turrets, oil pumps, and repair facilities are a few examples. + */ + unsigned IsSimpleDamage:1; + + /* + ** Some buildings can be placed directly on raw ground. Such buildings don't require + ** and are not affected by concrete or lack thereof. Typically, concrete itself is + ** considered sturdy. The same goes for walls and similar generic type structures. + ** The more sophisticated buildings are greatly affected by lack of concrete and thus + ** would have this flag set to false. + */ + unsigned IsSturdy:1; + + /* + ** Certain building types can be captures by enemy infantry. For those + ** building types, this flag will be true. Typically, military or hardened + ** structures such as turrets cannot be captured. + */ + unsigned IsCaptureable:1; + + /* + ** If this building really only has cosmetic idle animation, then this flag will be + ** true if this animation should run at a relatively constant rate regardless of game + ** speed setting. + */ + unsigned IsRegulated:1; + + /* + ** This flag specifies the type of object this factory building can "produce". For non + ** factory buildings, this value will be RTTI_NONE. + */ + RTTIType ToBuild; + + /* + ** For building that produce ground units (infantry and vehicles), there is a default + ** exit poit defined. This point is where the object is first placed on the map. + ** Typically, this is located next to a door. The unit will then travel on to a clear + ** terrain area and enter normal game processing. + */ + COORDINATE ExitPoint; + + /* + ** When determine which cell to head toward when exiting a building, use the + ** list elaborated by this variable. There are directions of exit that are + ** more suitable than others. This list is here to inform the system which + ** directions those are. + */ + short const *ExitList; + + /* + ** This is the structure type identifier. It can serve as a unique + ** identification number for building types. + */ + StructType Type; + + /* + ** This is a bitflag that represents which unit types can enter this + ** building. Determine if a unit can enter by taking 1 and shifting it + ** left by the unit type ID. If the corresponding bit is set, then that + ** unit type can enter this building. + */ + unsigned long CanEnter; + + /* + ** This is the starting facing to give this building when it first + ** gets constructed. The facing matches the final stage of the + ** construction animation. + */ + DirType StartFace; + + /* + ** This is the Tiberium storage capacity of the building. The sum of all + ** building's storage capacity is used to determine how much Tiberium can + ** be accumulated. + */ + unsigned Capacity; + + /* + ** Each building type produces and consumes power. These values tell how + ** much. + */ + int Power; + int Drain; + + /* + ** This is the size of the building. This size value is a rough indication + ** of the building's "footprint". + */ + BSizeType Size; + + /********************************************************************** + ** For each stage that a building may be in, its animation is controlled + ** by this structure. It dictates the starting and length of the animation + ** frames needed for the specified state. In addition it specifies how long + ** to delay between changes in animation. With this data it is possible to + ** control the appearance of all normal buildings. Turrets and SAM sites are + ** an exception since their animation is not merely cosmetic. + */ + typedef struct { + int Start; // Starting frame of animation. + int Count; // Number of frames in this animation. + int Rate; // Number of ticks to delay between each frame. + } AnimControlType; + AnimControlType Anims[BSTATE_COUNT]; + + /* + ** This is a mask flag used to determine if all the necessary prerequisite + ** buildings have been built. + */ + // long Prerequisite; + + /*--------------------------------------------------------------------------- + ** This is the building type explicit constructor. + */ + BuildingTypeClass ( + StructType type, + int name, + char const *ininame, + COORDINATE exitpoint, + unsigned char level, + long pre, + bool is_scanner, + bool is_regulated, + bool is_bibbed, + bool is_nominal, + bool is_wall, + bool is_factory, + bool is_capturable, + bool is_flammable, + bool is_simpledamage, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_theater, + bool is_turret_equipped, + bool is_twoshooter, + bool is_repairable, + bool is_buildable, + bool is_crew, + bool is_sturdy, + RTTIType tobuild, + DirType sframe, + unsigned short strength, + int sightrange, + int cost, + int scenario, + int risk, + int reward, + int ownable, + WeaponType primary, + WeaponType secondary, + ArmorType armor, + unsigned long canenter, + unsigned capacity, + int power, + int drain, + BSizeType size, + short const *exitlist, + short const *sizelist, + short const *overlap); + virtual RTTIType What_Am_I(void) const {return RTTI_BUILDINGTYPE;}; + operator StructType(void) const {return(Type);}; + + static BuildingTypeClass const & As_Reference(StructType type); + static StructType From_Name(char const *name); + static void Init(TheaterType theater); + static void One_Time(void); + static void Prep_For_Add(void); + + int Width(void) const; + int Height(void) const; + + virtual int Cost_Of(void) const; + virtual int Full_Name(void) const; + virtual COORDINATE Coord_Fixup(COORDINATE coord) const {return coord & 0xFF00FF00L;} + virtual int Max_Pips(void) const; + virtual void Dimensions(int &width, int &height) const; + virtual int Legal_Placement(CELL pos) const; + virtual bool Create_And_Place(CELL cell, HousesType house) const; + virtual ObjectClass * Create_One_Of(HouseClass * house) const; + virtual short const * Occupy_List(bool placement=false) const; + virtual short const * Overlap_List(void) const; + virtual BuildingClass * Who_Can_Build_Me(bool intheory, bool legal, HousesType house) const; + virtual void const * Get_Buildup_Data(void) const {return(BuildupData);}; + + virtual int Raw_Cost(void) const; + virtual int Repair_Cost(void) const; + virtual int Repair_Step(void) const; + bool Bib_And_Offset(SmudgeType & bib, CELL & cell) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house) const; + #endif + + + private: + + /* + ** This is a pointer to a list of offsets (from the upper left corner) that + ** are used to indicate the building's "footprint". This footprint is used + ** to determine building placement legality and terrain passibility. + */ + short const *OccupyList; + + /* + ** Buildings can often times overlap a cell but not actually "occupy" it for + ** purposes of movement. This points to a list of offsets that indicate which + ** cells the building has visual overlap but does not occupy. + */ + short const *OverlapList; + + static BuildingTypeClass const * const Pointers[STRUCT_COUNT]; + + /* + ** The construction animation graphic data pointer is + ** pointed to by this element. + */ + void const * BuildupData; + + void Init_Anim(BStateType state, int start, int count, int rate) const; +}; + + +/*************************************************************************** +** The various unit types need specific data that is unique to units as +** opposed to buildings. This derived class elaborates these additional +** data types. +*/ +class UnitTypeClass : public TechnoTypeClass +{ + public: + enum UnitTypeClassRepairEnums { + TIBERIUM_STEP=25, // Credits per step of Tiberium. + STEP_COUNT=28, // Number of steps a harvester can carry. + FULL_LOAD_CREDITS=(TIBERIUM_STEP*STEP_COUNT), + REPAIR_PERCENT=102, // 40% fixed point number. + REPAIR_STEP=4 // Number of damage points recovered per "step". + }; + + /* + ** If this unit can appear out of a crate, then this flag will be true. + */ + unsigned IsCrateGoodie:1; + + /* + ** Does this unit have only 8 facings? Special test units have limited + ** facings. + */ + unsigned IsPieceOfEight:1; + + /* + ** Can this unit squash infantry? If it can then if the player selects + ** an (enemy) infantry unit as the movement target, it will ride over and + ** squish the infantry unit. + */ + unsigned IsCrusher:1; + + /* + ** Does this unit go into harvesting mode when it stops on a tiberium + ** field? Typically, only one unit does this and that is the harvester. + */ + unsigned IsToHarvest:1; + + /* + ** Does this unit's shape data consist of "chunky" facings? This kind of unit + ** art has the unit in only 4 facings (N, W, S, and E) and in each of those + ** directions, the unit's turrets rotates 32 facings (counter clockwise from north). + ** This will result in 32 x 4 = 128 unit shapes in the shape data file. + */ + unsigned IsChunkyShape:1; + + /* + ** Some units are equipped with a rotating radar dish. These units have special + ** animation processing. The rotating radar dish is similar to a turret, but + ** always rotates and does not affect combat. + */ + unsigned IsRadarEquipped:1; + + /* + ** If this unit has a firing animation, this flag is true. Infantry and some special + ** vehicles are the ones with firing animations. + */ + unsigned IsFireAnim:1; + + /* + ** Many vehicles have a turret with restricted motion. These vehicles must move the + ** turret into a locked down position while travelling. Rocket launchers and artillery + ** are good examples of this kind of unit. + */ + unsigned IsLockTurret:1; + + /* + ** Does this unit lay tracks when it travels? Most tracked vehicles and some wheeled + ** vehicles have this ability. + */ + unsigned IsTracked:1; + + /* + ** Is this unit of the humongous size? Harvesters and mobile construction vehicles are + ** of this size. If the vehicle is greater than 24 x 24 but less than 48 x 48, it is + ** considered "Gigundo". + */ + unsigned IsGigundo:1; + + /* + ** Is the unit capable of cloaking? Only Stealth Tank can do so now. + */ + unsigned IsCloakable:1; + + /* + ** Does this unit have a constant animation (like Visceroid?) + */ + unsigned IsAnimating:1; + + /* + ** This value represents the unit class. It can serve as a unique + ** identification number for this unit class. + */ + UnitType Type; + + /* + ** This indicates the speed (locomotion) type for this unit. Through this + ** value the movement capabilities are deduced. + */ + SpeedType Speed; + + /* + ** This is the rotational speed of the unit. This value represents the + ** turret rotation speed. + */ + unsigned char ROT; + + /* + ** This is the distance along the centerline heading in the direction the body + ** is facing used to reach the center point of the turret. This distance is + ** in leptons. + */ + signed char TurretOffset; + + /* + ** This value is used to provide the unit with a default mission order when + ** first created. Usually, this is a resting or idle type of order. + */ + MissionType Mission; + + /* + ** This is the default explosion to use when this vehicle is destroyed. + */ + AnimType Explosion; + + /* + ** The width or height of the largest dimension for this unit. + */ + int MaxSize; + + /* + ** This is the explicit unit class constructor. + */ + UnitTypeClass ( + UnitType type, + int name, + char const *ininame, + AnimType exp, + unsigned char level, + long pre, + bool is_goodie, + bool is_leader, + bool is_eight, + bool is_nominal, + bool is_transporter, + bool is_crushable, + bool is_crusher, + bool is_harvest, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_turret_equipped, + bool is_twoshooter, + bool is_repairable, + bool is_buildable, + bool is_crew, + bool is_radar_equipped, + bool is_fire_anim, + bool is_lock_turret, + bool is_tracked, + bool is_gigundo, + bool is_chunky, + bool is_cloakable, + bool is_animating, + int ammo, + unsigned short strength, + int sightrange, + int cost, + int scenario, + int risk, + int reward, + int ownable, + WeaponType primary, + WeaponType secondary, + ArmorType armor, + SpeedType speed, + MPHType maxSpeed, + unsigned rot, + int toffset, + MissionType order); + virtual RTTIType What_Am_I(void) const {return RTTI_UNITTYPE;}; + + static UnitType From_Name(char const *name); + static UnitTypeClass const & As_Reference(UnitType type); + static void Init(TheaterType ); + static void One_Time(void); + static void Prep_For_Add(void); + + virtual void Dimensions(int &width, int &height) const; + virtual bool Create_And_Place(CELL cell, HousesType house) const; + virtual ObjectClass * Create_One_Of(HouseClass * house) const; + virtual short const * Occupy_List(bool placement=false) const; + virtual BuildingClass * Who_Can_Build_Me(bool intheory, bool legal, HousesType house) const; + virtual int Max_Pips(void) const; + + virtual int Repair_Cost(void) const; + virtual int Repair_Step(void) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house) const; + #endif + + /* + ** This is a pointer to the wake shape (as needed by the gunboat). + */ + static void const * WakeShapes; + + private: + static UnitTypeClass const * const Pointers[UNIT_COUNT]; +}; + + +/*************************************************************************** +** The various unit types need specific data that is unique to units as +** opposed to buildings. This derived class elaborates these additional +** data types. +*/ +class InfantryTypeClass : public TechnoTypeClass +{ + private: + static InfantryTypeClass const * const Pointers[INFANTRY_COUNT]; + + public: + + /* + ** If this civilian infantry type is female, then this flag + ** will be true. This information is used to get the correct + ** voice response. + */ + unsigned IsFemale:1; + + /* + ** Does this infantry unit have crawling animation? If not, then this + ** means that the "crawling" frames are actually running animation frames. + */ + unsigned IsCrawling:1; + + /* + ** For those infantry types that can capture buildings, this flag + ** will be set to true. Typically, this is minigun soldiers. + */ + unsigned IsCapture:1; + + /* + ** For infantry types that will run away from any damage causing + ** events, this flag will be true. Typically, this is so for all + ** civilians as well as the flame thrower guys. + */ + unsigned IsFraidyCat:1; + + /* + ** This flags whether this infantry is actually a civilian. A + ** civilian uses different voice responses, has less ammunition, + ** and runs from danger more often. + */ + unsigned IsCivilian:1; + + /* + ** This value represents the unit class. It can serve as a unique + ** identification number for this unit class. + */ + InfantryType Type; + + /* + ** This is an array of the various animation frame data for the actions that + ** the infantry may perform. + */ + DoInfoStruct DoControls[DO_COUNT]; + + /* + ** There are certain units with special animation sequences built into the + ** shape file. These values tell how many frames are used for the firing animation. + */ + char FireLaunch; + char ProneLaunch; + + /* + ** This is the explicit unit class constructor. + */ + InfantryTypeClass ( + InfantryType type, + int name, + char const *ininame, + unsigned char level, + long pre, + bool is_female, + bool is_leader, + bool is_crawling, + bool is_civilian, + bool is_nominal, + bool is_fraidycat, + bool is_capture, + bool is_theater, + int ammo, + int *do_table, + int firelaunch, + int pronelaunch, + unsigned short strength, + int sightrange, + int cost, + int scenario, + int risk, + int reward, + int ownable, + WeaponType primary, + WeaponType secondary, + MPHType maxSpeed); + virtual RTTIType What_Am_I(void) const {return RTTI_INFANTRYTYPE;}; + + static InfantryType From_Name(char const *name); + static InfantryTypeClass const & As_Reference(InfantryType type) {return *Pointers[type];}; + static void Init(TheaterType ); + static void One_Time(void); + static void Prep_For_Add(void); + + virtual void Dimensions(int &width, int &height) const {width = 12;height = 16;}; + virtual bool Create_And_Place(CELL cell, HousesType house) const; + virtual ObjectClass * Create_One_Of(HouseClass * house) const; + virtual short const * Occupy_List(bool placement=false) const; + virtual BuildingClass * Who_Can_Build_Me(bool intheory, bool legal, HousesType house) const; + virtual int Full_Name(void) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house) const; + #endif +}; + + +/*************************************************************************** +** Bullets and other projectiles need some specific information according +** to their type. +*/ +class BulletTypeClass : public ObjectTypeClass +{ + public: + + /* + ** Does this bullet type fly over walls? + */ + unsigned IsHigh:1; + + /* + ** If this projecile is one that balistically arcs from ground level, up into the air and + ** then back to the ground, where it explodes. Typical uses of this are for grenades and + ** artillery shells. + */ + unsigned IsArcing:1; + + /* + ** If the projectile changes course as it flies in order to home in on the + ** projectile's target, then this flag is true. Missiles are typically ones + ** that do this. + */ + unsigned IsHoming:1; + + /* + ** Certain projectiles do not travel horizontally, but rather, vertically -- they drop + ** from a height. Bombs fall into this category and will have this value set to + ** true. Dropping projectiles do not calculate collision with terrain (such as walls). + */ + unsigned IsDropping:1; + + /* + ** Is this projectile invisible? Some bullets and weapon effects are not directly + ** rendered. Small caliber bullets and flame thrower flames are treated like + ** normal projectiles for damage purposes, but are displayed using custom + ** rules. + */ + unsigned IsInvisible:1; + + /* + ** Does this bullet explode when near the target? Some bullets only explode if + ** it actually hits the target. Some explode even if nearby. + */ + unsigned IsProximityArmed:1; + + /* + ** Does this projectile spew puffs of smoke out its tail while it + ** travels? Missiles are prime examples of this projectile type. + */ + unsigned IsFlameEquipped:1; + + /* + ** Should fuel consumption be tracked for this projectile? Rockets are the primary + ** projectile with this characteristic, but even for bullets it should be checked so that + ** bullets don't travel too far. + */ + unsigned IsFueled:1; + + /* + ** Is this projectile without different facing visuals? Most plain bullets do not change + ** visual imagery if their facing changes. Rockets, on the other hand, are equipped with + ** the full 32 facing imagery. + */ + unsigned IsFaceless:1; + + /* + ** If this is a typically inaccurate projectile, then this flag will be true. Artillery + ** is a prime example of this type. + */ + unsigned IsInaccurate:1; + + /* + ** If the bullet contains translucent pixels, then this flag will be true. These + ** translucent pixels really are "shadow" pixels in the same style as the shadow + ** cast by regular ground units. + */ + unsigned IsTranslucent:1; + + /* + ** If this bullet can be fired on aircraft, then this flag will be true. + */ + unsigned IsAntiAircraft:1; + + /* + ** This element is a unique identification number for the bullet + ** type. + */ + BulletType Type; + + /* + ** Maximum speed for this bullet type. + */ + MPHType MaxSpeed; + + /* + ** This projectile has a certain style of warhead. This value specifies + ** what that warhead type is. + */ + WarheadType Warhead; + + /* + ** This is the "explosion" effect to use when this projectile impacts + */ + AnimType Explosion; + + /* + ** This is the rotation speed of the bullet. It only has practical value + ** for those projectiles that performing homing action during flight -- such + ** as with rockets. + */ + unsigned char ROT; + + /* + ** Some projectiles have a built in arming distance that must elapse before the + ** projectile may explode. If this value is non-zero, then this override is + ** applied. + */ + int Arming; + + /* + ** Some projectiles have a built in override for distance travelled before it + ** automatically explodes. This value, if non-zero, specifies this distance. + */ + int Range; + + //--------------------------------------------------------------------- + BulletTypeClass( + BulletType type, + char const *ininame, + bool is_high, + bool is_homing, + bool is_arcing, + bool is_dropping, + bool is_invisible, + bool is_proximity_armed, + bool is_flame_equipped, + bool is_fueled, + bool is_faceless, + bool is_inaccurate, + bool is_translucent, + bool is_antiair, + int arming, + int range, + MPHType maxspeed, + unsigned rot, + WarheadType warhead, + AnimType explosion); + + virtual RTTIType What_Am_I(void) const {return RTTI_BULLETTYPE;}; + + static BulletTypeClass const & As_Reference(BulletType type) {return *Pointers[type];}; + static void Init(TheaterType ) {}; + static void One_Time(void); + + virtual bool Create_And_Place(CELL , HousesType =HOUSE_NONE) const {return false;}; + virtual ObjectClass * Create_One_Of(HouseClass *) const {return 0;}; + + private: + static BulletTypeClass const * const Pointers[BULLET_COUNT]; +}; + + +/**************************************************************************** +** These are the different TYPES of terrain objects. Every terrain object must +** be one of these types. +*/ +class TerrainTypeClass : public ObjectTypeClass +{ + public: + /* + ** Which terrain object does this class type represent. + */ + TerrainType Type; + + /* + ** Does this terrain element consist of a normal frame followed by a + ** series of crumble frames? Trees fall under this case. + */ + unsigned IsDestroyable:1; + + /* + ** Does this object have the capability to transform after a period + ** of time (such as a blossom tree? + */ + unsigned IsTransformable:1; + + /* + ** Does this terrain object spawn the growth of Tiberium? Blossom trees are + ** a good example of this. + */ + unsigned IsTiberiumSpawn:1; + + /* + ** This is the fully translated name for the terrain element. + */ + short FullName; + + /* + ** This is the coordinate offset (from upper left) of where the center base + ** position of the terrain object lies. For trees, this would be the base of + ** the trunk. This is used for sorting purposes. + */ + COORDINATE CenterBase; + + /* + ** This is the bitfield control that tells which theater this terrain object is + ** valid for. If the bit (1 << TheaterType) is true, then this terrain object + ** is allowed. + */ + unsigned char Theater; + + //---------------------------------------------------------------- + TerrainTypeClass( + TerrainType terrain, + int theater, + COORDINATE centerbase, + bool is_spawn, + bool is_destroyable, + bool is_transformable, + bool is_flammable, + bool is_crushable, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + char const *ininame, + int fullname, + unsigned short strength, + ArmorType armor, + short const *occupy, + short const *overlap); + virtual RTTIType What_Am_I(void) const {return RTTI_TERRAINTYPE;}; + + static TerrainType From_Name(char const*name); + static TerrainTypeClass const & As_Reference(TerrainType type) {return *Pointers[type];}; + static void Init(TheaterType theater = THEATER_TEMPERATE); + static void One_Time(void) {}; + static void Prep_For_Add(void); + + virtual COORDINATE Coord_Fixup(COORDINATE coord) const {return coord & 0xFF00FF00L;} + virtual bool Create_And_Place(CELL cell, HousesType house) const; + virtual ObjectClass * Create_One_Of(HouseClass *) const; + virtual short const * Occupy_List(bool placement=false) const; + virtual short const * Overlap_List(void) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house=HOUSE_NONE) const; + #endif + + private: + short const *Occupy; + short const *Overlap; + + static TerrainTypeClass const * const Pointers[TERRAIN_COUNT]; +}; + + +/**************************************************************************** +** The tile type objects are controlled by this class. It specifies the form +** of the tile set for the specified object as well as other related datum. +** It is derived from the ObjectTypeClass solely for the purpose of scenario +** editing and creation. +*/ +class TemplateTypeClass: public ObjectTypeClass +{ + public: + /* + ** What template is this. + */ + TemplateType Type; + + /* + ** A bitfield container that indicates which theaters this template is allowed + ** in. A bit set in the (1<. +*/ + +/* $Header: F:\projects\c&c\vcs\code\udata.cpv 2.17 16 Oct 1995 16:50:42 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : UDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : June 26, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * UnitTypeClass::As_Reference -- Fetches a reference to the unit type class specified. * + * UnitTypeClass::Create_And_Place -- Creates and places a unit object onto the map. * + * UnitTypeClass::Create_One_Of -- Creates a unit in limbo. * + * UnitTypeClass::Dimensions -- Determines the unit's pixel dimensions. * + * UnitTypeClass::Display -- Displays a generic unit shape. * + * UnitTypeClass::From_Name -- Fetch class pointer from specified name. * + * UnitTypeClass::Max_Pips -- Fetches the maximum pips allowed for this unit. * + * UnitTypeClass::Occupy_List -- Returns with unit occupation list. * + * UnitTypeClass::One_Time -- Performs one time processing for unit type class objects. * + * UnitTypeClass::Prep_For_Add -- Prepares scenario editor to add unit. * + * UnitTypeClass::UnitTypeClass -- Constructor for unit types. * + * UnitTypeClass::Who_Can_Build_Me -- Determines which factory can build this unit type. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +void const * UnitTypeClass::WakeShapes = 0; + +// Visceroid +static UnitTypeClass const UnitVisceroid( + UNIT_VICE, + TXT_VISCEROID, // NAME: Text name of this unit type. + "VICE", // NAME: Text name of this unit type. + ANIM_NAPALM2, // EXPLOSION: Type of explosion when destroyed. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + true, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + true, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired in a repair facility? + false, // Can the player construct or order this unit? + false, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Does it lay tracks while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + true, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 150, // STRENGTH: Strength (in damage points). + 4, // SIGHTRANGE: Range of sighting. + 800, // COST: Cost to build (Credits). + 1, // SCENARIO: Starting availability scenario. + 80,20, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_GOOD| + HOUSEF_BAD| + HOUSEF_NEUTRAL| + HOUSEF_JP, // OWNABLE: Ownable by house (bit field). + WEAPON_CHEMSPRAY,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_MEDIUM, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Flame tank +static UnitTypeClass const UnitFTank( + UNIT_FTANK, + TXT_FTANK, // NAME: Text name of this unit type. + "FTNK", // NAME: Text name of this unit type. + ANIM_NAPALM3, // EXPLOSION: Type of explosion when destroyed. + 4, // Build level. + STRUCTF_RADAR, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + true, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 300, // STRENGTH: Strength (in damage points). + 4, // SIGHTRANGE: Range of sighting. + 800, // COST: Cost to build (Credits). + 9, // SCENARIO: Starting availability scenario. + 80,66, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_FLAME_TONGUE,WEAPON_NONE, + ARMOR_STEEL, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_MEDIUM, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Stealth tank +static UnitTypeClass const UnitSTank( + UNIT_STANK, + TXT_STANK, // NAME: Text name of this unit type. + "STNK", // NAME: Text name of this unit type. + ANIM_FRAG2, // EXPLOSION: Type of explosion when destroyed. + 5, // Build level. + STRUCTF_RADAR, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + true, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + true, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + true, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 110, // STRENGTH: Strength (in damage points). + 4, // SIGHTRANGE: Range of sighting. + 900, // COST: Cost to build (Credits). + 12, // SCENARIO: Starting availability scenario. + 80,81, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_DRAGON,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_MEDIUM_FAST, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Light tank +static UnitTypeClass const UnitLTank( + UNIT_LTANK, + TXT_LTANK, // NAME: Text name of this unit type. + "LTNK", // NAME: Text name of this unit type. + ANIM_FRAG1, // EXPLOSION: Type of explosion when destroyed. + 3, // Build level. + STRUCTF_NONE, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + true, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 300, // STRENGTH: Strength (in damage points). + 3, // SIGHTRANGE: Range of sighting. + 600, // COST: Cost to build (Credits). + 5, // SCENARIO: Starting availability scenario. + 80,56, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_75MM,WEAPON_NONE, + ARMOR_STEEL, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_MEDIUM, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Medium tank +static UnitTypeClass const UnitMTank( + UNIT_MTANK, + TXT_MTANK, // NAME: Text name of this unit type. + "MTNK", // NAME: Text name of this unit type. + ANIM_FRAG2, // EXPLOSION: Type of explosion when destroyed. + 3, // Build level. + STRUCTF_NONE, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + true, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 400, // STRENGTH: Strength (in damage points). + 3, // SIGHTRANGE: Range of sighting. + 800, // COST: Cost to build (Credits). + 7, // SCENARIO: Starting availability scenario. + 80,62, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_105MM,WEAPON_NONE, + ARMOR_STEEL, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_MEDIUM, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Mastadon tank +static UnitTypeClass const UnitHTank( + UNIT_HTANK, + TXT_HTANK, // NAME: Text name of this unit type. + "HTNK", // NAME: Text name of this unit type. + ANIM_ART_EXP1, // EXPLOSION: Type of explosion when destroyed. + 5, // Build level. + STRUCTF_REPAIR, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + true, // Is it equipped with a combat turret? + true, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 600, // STRENGTH: Strength (in damage points). + 4, // SIGHTRANGE: Range of sighting. + 1500, // COST: Cost to build (Credits). + 13, // SCENARIO: Starting availability scenario. + 80,80, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_120MM,WEAPON_MAMMOTH_TUSK, + ARMOR_STEEL, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_MEDIUM_SLOW, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Mobile HQ +static UnitTypeClass const UnitMHQ( + UNIT_MHQ, + TXT_MHQ, // NAME: Text name of this unit type. + "MHQ", // NAME: Text name of this unit type. + ANIM_FRAG2, // EXPLOSION: Type of explosion when destroyed. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Can this be a goodie surprise from a crate? + false, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + true, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 110, // STRENGTH: Strength (in damage points). + 5, // SIGHTRANGE: Range of sighting. + 600, // COST: Cost to build (Credits). + 99, // SCENARIO: Starting availability scenario. + 80,100, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_MEDIUM, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Landing craft +static UnitTypeClass const UnitHover( + UNIT_HOVER, + TXT_HOVER, // NAME: Text name of this unit type. + "LST", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Can this be a goodie surprise from a crate? + false, // Is a leader type? + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + true, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + false, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? +// true, // Is selectable by player? + false, // Is selectable by player? + false, // Can it be a target for attack or move? + true, // Is it insignificant (won't be announced)? + true, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired in a repair facility? + false, // Can the player construct or order this unit? + false, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Does it lay tracks while moving? + true, // Is this a gigundo-rotund-enormous unit? + true, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 400, // STRENGTH: Strength (in damage points). + 3, // SIGHTRANGE: Range of sighting. + 300, // COST: Cost to build (Credits). + 99, // SCENARIO: Starting availability scenario. + 80,40, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + SPEED_HOVER, // MOVE: Locomotion type. + MPH_MEDIUM_FAST, // SPEED: Miles per hour. + 127, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Mobile sam launcher +static UnitTypeClass const UnitSAM( + UNIT_MSAM, + TXT_MSAM, // NAME: Text name of this unit type. + "MLRS", // NAME: Text name of this unit type. + ANIM_FRAG2, // EXPLOSION: Type of explosion when destroyed. + 7, // Build level. + STRUCTF_ATOWER, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + false, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + true, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + true, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + 2, // AMMO: Number of shots it has (default). + 120, // STRENGTH: Strength (in damage points). + 4, // SIGHTRANGE: Range of sighting. + 750, // COST: Cost to build (Credits). + 98, // SCENARIO: Starting availability scenario. + 80,30, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| +// HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_HONEST_JOHN,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_MEDIUM, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Artillery +static UnitTypeClass const UnitArty( + UNIT_ARTY, + TXT_ARTY, // NAME: Text name of this unit type. + "ARTY", // NAME: Text name of this unit type. + ANIM_ART_EXP1, // EXPLOSION: Type of explosion when destroyed. + 6, // Build level. + STRUCTF_NONE, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + false, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 75, // STRENGTH: Strength (in damage points). + 4, // SIGHTRANGE: Range of sighting. + 450, // COST: Cost to build (Credits). + 9, // SCENARIO: Starting availability scenario. + 80,73, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_155MM,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_MEDIUM_SLOW, // SPEED: Miles per hour. + 2, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Harvester +static UnitTypeClass const UnitHarvester( + UNIT_HARVESTER, + TXT_HARVESTER, // NAME: Text name of this unit type. + "HARV", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 2, // Build level. + STRUCTF_REFINERY, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + false, // Is a leader type? + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + true, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 600, // STRENGTH: Strength (in damage points). +// 300, // STRENGTH: Strength (in damage points). + 2, // SIGHTRANGE: Range of sighting. + 1400, // COST: Cost to build (Credits). + 7, // SCENARIO: Starting availability scenario. + 80,85, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + SPEED_WHEEL, // MOVE: Locomotion type. + MPH_MEDIUM_SLOW, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HARVEST // ORDERS: Default order to give new unit. +); + +// Mobile construction vehicle +static UnitTypeClass const UnitMCV( + UNIT_MCV, + TXT_MCV, // NAME: Text name of this unit type. + "MCV", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 7, // Build level. + STRUCTF_EYE, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + false, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 600, // STRENGTH: Strength (in damage points). + 2, // SIGHTRANGE: Range of sighting. + 5000, // COST: Cost to build (Credits). + 15, // SCENARIO: Starting availability scenario. + 80,86, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_NONE,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + SPEED_WHEEL, // MOVE: Locomotion type. + MPH_MEDIUM_SLOW, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Jeep (hummer) +static UnitTypeClass const UnitJeep( + UNIT_JEEP, + TXT_JEEP, // NAME: Text name of this unit type. + "JEEP", // NAME: Text name of this unit type. + ANIM_FRAG1, // EXPLOSION: Type of explosion when destroyed. + 2, // Build level. + STRUCTF_NONE, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + false, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + true, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 150, // STRENGTH: Strength (in damage points). + 2, // SIGHTRANGE: Range of sighting. + 400, // COST: Cost to build (Credits). + 5, // SCENARIO: Starting availability scenario. + 80,41, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_M60MG,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + SPEED_WHEEL, // MOVE: Locomotion type. + MPH_MEDIUM_FAST, // SPEED: Miles per hour. + 10, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Buggy +static UnitTypeClass const UnitBuggy( + UNIT_BUGGY, + TXT_DUNE_BUGGY, // NAME: Text name of this unit type. + "BGGY", // NAME: Text name of this unit type. + ANIM_FRAG1, // EXPLOSION: Type of explosion when destroyed. + 4, // Build level. + STRUCTF_NONE, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + false, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + true, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Does it lay tracks while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 140, // STRENGTH: Strength (in damage points). + 2, // SIGHTRANGE: Range of sighting. + 300, // COST: Cost to build (Credits). + 5, // SCENARIO: Starting availability scenario. + 80,42, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_M60MG,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + SPEED_WHEEL, // MOVE: Locomotion type. + MPH_MEDIUM_FAST, // SPEED: Miles per hour. + 10, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Attack cycle +static UnitTypeClass const UnitBike( + UNIT_BIKE, + TXT_BIKE, // NAME: Text name of this unit type. + "BIKE", // NAME: Text name of this unit type. + ANIM_FRAG1, // EXPLOSION: Type of explosion when destroyed. + 2, // Build level. + STRUCTF_NONE, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + true, // Can it be crushed by a heavy vehicle? + false, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Does it lay tracks while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). +#ifdef ADVANCED + 90, // STRENGTH: Strength (in damage points). +#else + 160, // STRENGTH: Strength (in damage points). +#endif + 2, // SIGHTRANGE: Range of sighting. + 500, // COST: Cost to build (Credits). + 5, // SCENARIO: Starting availability scenario. + 80,45, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_DRAGON,WEAPON_NONE, + ARMOR_WOOD, // ARMOR: Armor type + SPEED_WHEEL, // MOVE: Locomotion type. + MPH_FAST, // SPEED: Miles per hour. + 10, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Rocket launcher +static UnitTypeClass const UnitMLRS( + UNIT_MLRS, + TXT_MLRS, // NAME: Text name of this unit type. + "MSAM", // NAME: Text name of this unit type. + ANIM_ART_EXP1, // EXPLOSION: Type of explosion when destroyed. + 7, // Build level. + STRUCTF_EYE, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + false, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + true, // Is it equipped with a combat turret? + true, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + true, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 100, // STRENGTH: Strength (in damage points). + 4, // SIGHTRANGE: Range of sighting. + 800, // COST: Cost to build (Credits). + 11, // SCENARIO: Starting availability scenario. + 80,72, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_BAD| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_MLRS,WEAPON_NONE, + ARMOR_ALUMINUM, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_MEDIUM, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_GUARD // ORDERS: Default order to give new unit. +); + +// Armored personnel carrier +static UnitTypeClass const UnitAPC( + UNIT_APC, + TXT_APC, // NAME: Text name of this unit type. + "APC", // NAME: Text name of this unit type. + ANIM_FRAG2, // EXPLOSION: Type of explosion when destroyed. + 4, // Build level. + STRUCTF_BARRACKS, // Building prerequisite. + true, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + false, // Always use the given name for the vehicle? + true, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + true, // Can it be repaired in a repair facility? + true, // Can the player construct or order this unit? + false, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + true, // Does it lay tracks while moving? + false, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 200, // STRENGTH: Strength (in damage points). + 4, // SIGHTRANGE: Range of sighting. + 700, // COST: Cost to build (Credits). + 5, // SCENARIO: Starting availability scenario. + 80,15, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_BAD| + HOUSEF_JP| + HOUSEF_GOOD, // OWNABLE: Ownable by house (bit field). + WEAPON_M60MG,WEAPON_NONE, + ARMOR_STEEL, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_MEDIUM_FASTER, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_HUNT // ORDERS: Default order to give new unit. +); + +// Gunboat +static UnitTypeClass const UnitGunBoat( + UNIT_GUNBOAT, + TXT_GUNBOAT, // NAME: Text name of this unit type. + "BOAT", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + false, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + false, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + true, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + true, // Is it equipped with a combat turret? + true, // Fires multiple shots in quick succession? + false, // Can it be repaired in a repair facility? + false, // Can the player construct or order this unit? + true, // Is there a crew inside? + false, // Does it have a rotating radar dish? + false, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Does it lay tracks while moving? + true, // Is this a gigundo-rotund-enormous unit? + true, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 700, // STRENGTH: Strength (in damage points). + 5, // SIGHTRANGE: Range of sighting. + 300, // COST: Cost to build (Credits). + 99, // SCENARIO: Starting availability scenario. + 80,40, // RISK/RWRD: Risk/reward rating values. + HOUSEF_MULTI1| + HOUSEF_MULTI2| + HOUSEF_MULTI3| + HOUSEF_MULTI4| + HOUSEF_MULTI5| + HOUSEF_MULTI6| + HOUSEF_JP| + HOUSEF_GOOD| + HOUSEF_BAD, // OWNABLE: Ownable by house (bit field). + WEAPON_TOMAHAWK,WEAPON_NONE, + ARMOR_STEEL, // ARMOR: Armor type + SPEED_FLOAT, // MOVE: Locomotion type. + MPH_SLOW, // SPEED: Miles per hour. + 1, // ROT: Rate of turn (degrees per tick). + 14, // Turret center offset along body centerline. + MISSION_GUARD // ORDERS: Default order to give new unit. +); + +// Triceratops +static UnitTypeClass const UnitTric( + UNIT_TRIC, + TXT_TRIC, // NAME: Text name of this unit type. + "TRIC", // NAME: Text name of this unit type. + ANIM_TRIC_DIE, // EXPLOSION: Type of explosion when destroyed. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + true, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + true, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired in a repair facility? + false, // Can the player construct or order this unit? + false, // Is there a crew inside? + false, // Does it have a rotating radar dish? + true, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Does it lay tracks while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 700, // STRENGTH: Strength (in damage points). + 5, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to build (Credits). + 99, // SCENARIO: Starting availability scenario. + 50,50, // RISK/RWRD: Risk/reward rating values. + HOUSEF_JP, // OWNABLE: Ownable by house (bit field). + WEAPON_STEG,WEAPON_NONE, + ARMOR_STEEL, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_SLOW, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_GUARD // ORDERS: Default order to give new unit. +); + +// Tyrannosaurus Rex +static UnitTypeClass const UnitTrex( + UNIT_TREX, + TXT_TREX, // NAME: Text name of this unit type. + "TREX", // NAME: Text name of this unit type. + ANIM_TREX_DIE, // EXPLOSION: Type of explosion when destroyed. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + true, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + true, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired in a repair facility? + false, // Can the player construct or order this unit? + false, // Is there a crew inside? + false, // Does it have a rotating radar dish? + true, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Does it lay tracks while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 750, // STRENGTH: Strength (in damage points). + 5, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to build (Credits). + 99, // SCENARIO: Starting availability scenario. + 50,50, // RISK/RWRD: Risk/reward rating values. + HOUSEF_JP, // OWNABLE: Ownable by house (bit field). + WEAPON_TREX,WEAPON_NONE, + ARMOR_STEEL, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_MEDIUM, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_GUARD // ORDERS: Default order to give new unit. +); + +// Velociraptor +static UnitTypeClass const UnitRapt( + UNIT_RAPT, + TXT_RAPT, // NAME: Text name of this unit type. + "RAPT", // NAME: Text name of this unit type. + ANIM_RAPT_DIE, // EXPLOSION: Type of explosion when destroyed. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + true, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + true, // Can it be crushed by a heavy vehicle? + false, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + true, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired in a repair facility? + false, // Can the player construct or order this unit? + false, // Is there a crew inside? + false, // Does it have a rotating radar dish? + true, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Does it lay tracks while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 180, // STRENGTH: Strength (in damage points). + 5, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to build (Credits). + 99, // SCENARIO: Starting availability scenario. + 50,50, // RISK/RWRD: Risk/reward rating values. + HOUSEF_JP, // OWNABLE: Ownable by house (bit field). + WEAPON_TREX,WEAPON_NONE, + ARMOR_STEEL, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_FAST, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_GUARD // ORDERS: Default order to give new unit. +); + +// Stegosaurus +static UnitTypeClass const UnitSteg( + UNIT_STEG, + TXT_STEG, // NAME: Text name of this unit type. + "STEG", // NAME: Text name of this unit type. + ANIM_STEG_DIE, // EXPLOSION: Type of explosion when destroyed. + 99, // Build level. + STRUCTF_NONE, // Building prerequisite. + false, // Can this be a goodie surprise from a crate? + true, // Is a leader type? + true, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is this a typical transport vehicle? + false, // Can it be crushed by a heavy vehicle? + true, // Can this unit squash infantry? + false, // Does this unit harvest Tiberium? + true, // Is invisible to radar? + true, // Is selectable by player? + true, // Can it be a target for attack or move? + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + false, // Is it equipped with a combat turret? + false, // Fires multiple shots in quick succession? + false, // Can it be repaired in a repair facility? + false, // Can the player construct or order this unit? + false, // Is there a crew inside? + false, // Does it have a rotating radar dish? + true, // Is there an associated firing animation? + false, // Must the turret be in a locked down position while moving? + false, // Does it lay tracks while moving? + true, // Is this a gigundo-rotund-enormous unit? + false, // Is the unit's art as "chunky" cardinal facing only? + false, // Is the unit capable of cloaking? + false, // Does the unit have a constant animation? + -1, // AMMO: Number of shots it has (default). + 600, // STRENGTH: Strength (in damage points). + 5, // SIGHTRANGE: Range of sighting. + 0, // COST: Cost to build (Credits). + 99, // SCENARIO: Starting availability scenario. + 50,50, // RISK/RWRD: Risk/reward rating values. + HOUSEF_JP, // OWNABLE: Ownable by house (bit field). + WEAPON_STEG,WEAPON_NONE, + ARMOR_STEEL, // ARMOR: Armor type + SPEED_TRACK, // MOVE: Locomotion type. + MPH_SLOW, // SPEED: Miles per hour. + 5, // ROT: Rate of turn (degrees per tick). + 0, // Turret center offset along body centerline. + MISSION_GUARD // ORDERS: Default order to give new unit. +); + + +/* +** This is the array of pointers to the static data associated with each +** vehicle type. +*/ +UnitTypeClass const * const UnitTypeClass::Pointers[UNIT_COUNT] = { + &UnitHTank, // UNIT_HTANK + &UnitMTank, // UNIT_MTANK + &UnitLTank, // UNIT_LTANK + &UnitSTank, // UNIT_STANK + &UnitFTank, // UNIT_FTANK + &UnitVisceroid, // UNIT_VICE + &UnitAPC, // UNIT_APC + &UnitMLRS, // UNIT_MLRS + &UnitJeep, // UNIT_JEEP + &UnitBuggy, // UNIT_BUGGY + &UnitHarvester, // UNIT_HARVESTER + &UnitArty, // UNIT_ARTY + &UnitSAM, // UNIT_MSAM + &UnitHover, // UNIT_HOVER + &UnitMHQ, // UNIT_MHQ + &UnitGunBoat, // UNIT_GUNBOAT + &UnitMCV, // UNIT_MCV + &UnitBike, // UNIT_BIKE + &UnitTric, // UNIT_TRIC + &UnitTrex, // UNIT_TREX + &UnitRapt, // UNIT_RAPT + &UnitSteg, // UNIT_STEG +}; + + +/*********************************************************************************************** + * UnitTypeClass::UnitTypeClass -- Constructor for unit types. * + * * + * This is the constructor for the unit types. It is used to initialize the unit type class * + * structure. The unit type class is used to control the behavior of the various types * + * of units in the game. This constructor is called for every unique unit type as it * + * exists in the array of unit types. * + * * + * INPUT: bla bla bla... see below * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + *=============================================================================================*/ +UnitTypeClass::UnitTypeClass(UnitType type, + int name, + char const *ininame, + AnimType exp, + unsigned char level, + long pre, + bool is_goodie, + bool is_leader, + bool is_eight, + bool is_nominal, + bool is_transporter, + bool is_crushable, + bool is_crusher, + bool is_harvest, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_turret_equipped, + bool is_twoshooter, + bool is_repairable, + bool is_buildable, + bool is_crew, + bool is_radar_equipped, + bool is_fire_anim, + bool is_lock_turret, + bool is_tracked, + bool is_gigundo, + bool is_chunky, + bool is_cloakable, + bool is_animating, + int ammo, + unsigned short strength, + int sightrange, + int cost, + int scenario, + int risk, + int reward, + int ownable, + WeaponType primary, WeaponType secondary, + ArmorType armor, + SpeedType speed, + MPHType maxSpeed, + unsigned rot, + int toffset, + MissionType order) : + TechnoTypeClass(name, + ininame, + level, + pre, + is_leader, + false, + is_nominal, + is_transporter, + false, + is_crushable, + is_stealthy, + is_selectable, + is_legal_target, + is_insignificant, + is_immune, + false, + is_twoshooter, + is_turret_equipped, + is_repairable, + is_buildable, + is_crew, + ammo, + strength, + maxSpeed, + sightrange, + cost, + scenario, + risk, + reward, + ownable, + primary,secondary, + armor) +{ + Explosion = exp; + IsCrateGoodie = is_goodie; + IsPieceOfEight = is_eight; + IsCloakable = is_cloakable; + IsChunkyShape = is_chunky; + IsCrusher = is_crusher; + IsFireAnim = is_fire_anim; + IsGigundo = is_gigundo; + IsLockTurret = is_lock_turret; + IsRadarEquipped = is_radar_equipped; + IsToHarvest = is_harvest; + IsTracked = is_tracked; + IsAnimating = is_animating; + Mission = order; + ROT = rot; + Speed = speed; + TurretOffset = toffset; + Type = type; +} + + +/*********************************************************************************************** + * UnitTypeClass::Occupy_List -- Returns with unit occupation list. * + * * + * This routine returns with an occupation list for the unit type. * + * The unit occupation list is used for placing units. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a pointer to the unit occupation list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + *=============================================================================================*/ +short const * UnitTypeClass::Occupy_List(bool ) const +{ + static short const _simple[] = {0, REFRESH_EOL}; + static short const _gun[] = {0, -1, 1, REFRESH_EOL}; + + if (Type == UNIT_GUNBOAT) { + return(&_gun[0]); + } + return(&_simple[0]); +} + + +/*********************************************************************************************** + * UnitTypeClass::From_Name -- Fetch class pointer from specified name. * + * * + * This routine converts an ASCII representation of a unit class and * + * converts it into a real unit class number. * + * * + * INPUT: name -- ASCII name representing a unit class. * + * * + * OUTPUT: Returns with the actual unit class number that the string * + * represents. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 05/02/1994 JLB : Converted to member function. * + *=============================================================================================*/ +UnitType UnitTypeClass::From_Name(char const *name) +{ + if (name) { + for (UnitType classid = UNIT_FIRST; classid < UNIT_COUNT; classid++) { + if (stricmp(Pointers[classid]->IniName, name) == 0) { + return(classid); + } + } + } + return(UNIT_NONE); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * UnitTypeClass::Display -- Displays a generic unit shape. * + * * + * This routine displays a generic representation of a unit of this * + * type. Typically, it is used when adding objects to the game map. * + * * + * INPUT: x,y -- Coordinate to render the unit shape. * + * * + * window-- Window to render within. * + * * + * house -- House to render the unit colors. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + * 11/08/1994 JLB : Handles chunky type vehicles now. * + *=============================================================================================*/ +void UnitTypeClass::Display(int x, int y, WindowNumberType window, HousesType house) const +{ + int shape = 0; + void const * ptr = Get_Cameo_Data(); + if (!ptr) { + ptr = Get_Image_Data(); + shape = IsChunkyShape ? 0 : 5; + } + CC_Draw_Shape(ptr, shape, x, y, window, SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, HouseTypeClass::As_Reference(house).RemapTable); +} + + +/*********************************************************************************************** + * UnitTypeClass::Prep_For_Add -- Prepares scenario editor to add unit. * + * * + * This routine is used to prepare the generic object adder dialog * + * box so that it will be able to add a unit object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + * 06/04/1994 JLB : Uses map editing interface functions. * + *=============================================================================================*/ +void UnitTypeClass::Prep_For_Add(void) +{ + for (UnitType index = UNIT_FIRST; index < UNIT_COUNT; index++) { + if (As_Reference(index).Get_Image_Data() != NULL) { + Map.Add_To_List(&As_Reference(index)); + } + } +} +#endif + + +/*********************************************************************************************** + * UnitTypeClass::One_Time -- Performs one time processing for unit type class objects. * + * * + * This routine is used to perform the action necessary only once for the unit type class. * + * It loads unit shapes and brain files. This routine should only be called once. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once. * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void UnitTypeClass::One_Time(void) +{ + for (UnitType index = UNIT_FIRST; index < UNIT_COUNT; index++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + char buffer[_MAX_FNAME]; + UnitTypeClass const & uclass = As_Reference(index); + CCFileClass file; + int largest; // Largest dimension of shape (so far). + + void const *ptr; // Shape pointer and set pointer. + + largest = 0; + if (uclass.IsBuildable) { + + /* + ** Fetch the supporting data files for the unit. + */ + if ( Get_Resolution_Factor() ) { + sprintf(buffer, "%sICNH", uclass.IniName); + } else { + sprintf(buffer, "%sICON", uclass.IniName); + } + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + ((void const *&)uclass.CameoData) = MixFileClass::Retrieve(fullname); + } + + /* + ** Fetch a pointer to the unit's shape data. + */ + if (!uclass.IsPieceOfEight || (Special.IsJurassic && AreThingiesEnabled) ) { + _makepath(fullname, NULL, NULL, uclass.IniName, ".SHP"); + ptr = MixFileClass::Retrieve(fullname); + } else { + ptr = NULL; + } + + ((void const *&)uclass.ImageData) = ptr; + if (ptr) { + + if (index == UNIT_MLRS || index == UNIT_MSAM) { + largest = 26; + } else { + largest = MAX(largest, (int)Get_Build_Frame_Width(ptr)); + largest = MAX(largest, (int)Get_Build_Frame_Height(ptr)); + } + } + + ((int &)uclass.MaxSize) = MAX(largest, 8); + } + + /* + ** Load the wake shapes in at this time. + */ + if (!WakeShapes) { + WakeShapes = MixFileClass::Retrieve("WAKE.SHP"); + } +} + + + +/*********************************************************************************************** + * UTC::Init -- fetches the sidebar icons for the unittypeclass objects * + * * + * * + * * + * INPUT: theater type * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 4/26/96 4:07PM ST : Created * + *=============================================================================================*/ + +void UnitTypeClass::Init(TheaterType theater) +{ + + if (Get_Resolution_Factor()){ + + if (theater != LastTheater){ + + void const * cameo_ptr; + char fullname[_MAX_FNAME+_MAX_EXT]; + char buffer[_MAX_FNAME]; + + for (UnitType index = UNIT_FIRST; index < UNIT_COUNT; index++) { + + UnitTypeClass const & uclass = As_Reference(index); + + ((void const *&)uclass.CameoData) = NULL; + + if (uclass.IsBuildable) { + sprintf(buffer, "%sICNH", uclass.IniName); + _makepath(fullname, NULL, NULL, buffer, Theaters[theater].Suffix); + cameo_ptr = MixFileClass::Retrieve(fullname); + if (cameo_ptr){ + ((void const *&)uclass.CameoData) = cameo_ptr; + } + } + } + } + } +} + + +/*********************************************************************************************** + * UnitTypeClass::Create_And_Place -- Creates and places a unit object onto the map. * + * * + * This routine is used by the scenario editor to create and place a unit object of this * + * type onto the map. * + * * + * INPUT: cell -- The cell that the unit is to be placed into. * + * * + * house -- The house that the unit belongs to. * + * * + * OUTPUT: bool; Was the unit created and placed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitTypeClass::Create_And_Place(CELL cell, HousesType house) const +{ + UnitClass * unit = new UnitClass(Type, house); + if (unit) { + return(unit->Unlimbo(Cell_Coord(cell), Random_Pick(DIR_N, DIR_MAX))); + } + return(false); +} + + +/*********************************************************************************************** + * UnitTypeClass::Create_One_Of -- Creates a unit in limbo. * + * * + * This function creates a unit of this type and keeps it in limbo. A pointer to the * + * created unit object is returned. It is presumed that this object will later be * + * unlimboed at the correct time and place. * + * * + * INPUT: house -- Pointer to the house that is to own the unit. * + * * + * OUTPUT: Returns with a pointer to the created unit object. If the unit object * + * could not be created, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/07/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * UnitTypeClass::Create_One_Of(HouseClass * house) const +{ + return(new UnitClass(Type, house->Class->House)); +} + + +/*********************************************************************************************** + * UnitTypeClass::Who_Can_Build_Me -- Determines which factory can build this unit type. * + * * + * Use this routine to examine the buildings on the map in order to determine which one * + * can build the unit type. * + * * + * INPUT: intheory -- If this parameter is true, then no examination of whether the factory * + * is currently busy is performed. It just checks to see if the unit * + * could be produced "in theory". * + * * + * legal -- Should building prerequisite legality checks be performed as well? * + * For building placements, this is usually false. For sidebar button * + * adding, this is usually true. * + * * + * house -- The owner of the unit to be produced. * + * * + * OUTPUT: Returns with pointer to the factory that can produce the unit. If no suitable * + * factory could be found then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/12/1994 JLB : Created. * + *=============================================================================================*/ +BuildingClass * UnitTypeClass::Who_Can_Build_Me(bool intheory, bool legal, HousesType house) const +{ + BuildingClass * anybuilding = NULL; + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + if (building && + !building->IsInLimbo && + building->House->Class->House == house && + building->Class->ToBuild == RTTI_UNITTYPE && + building->Mission != MISSION_DECONSTRUCTION && + ((1L << building->ActLike) & Ownable) && + (!legal || building->House->Can_Build(Type, building->ActLike)) && + (intheory || !building->In_Radio_Contact())) { + + if (building->IsLeader) return(building); + anybuilding = building; + } + } + return(anybuilding); +} + + +/*********************************************************************************************** + * UnitTypeClass::As_Reference -- Fetches a reference to the unit type class specified. * + * * + * Use this routine to return a reference to the UnitTypeClass object as indicated by * + * the unit type number speicified. * + * * + * INPUT: type -- The unit type number to convert into a UnitTypeClass object reference. * + * * + * OUTPUT: Returns with a reference to the unit type class object specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +UnitTypeClass const & UnitTypeClass::As_Reference(UnitType type) +{ + return(*Pointers[type]); +} + + +/*********************************************************************************************** + * UnitTypeClass::Dimensions -- Determines the unit's pixel dimensions. * + * * + * This routine will fill in the width and height for this unit type. This width and * + * height are used to render the selection rectangle and the positioning of the health * + * bargraph. * + * * + * INPUT: width -- Reference to the width of the unit (to be filled in). * + * * + * height -- Reference to the height of the unit (to be filled in). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +void UnitTypeClass::Dimensions(int &width, int &height) const +{ + if (Type == UNIT_GUNBOAT) { + width = 46; + height = 18; + } else { + width = MaxSize-(MaxSize/4); + height = MaxSize-(MaxSize/4); + } +} + +/*********************************************************************************************** + * UnitTypeClass::Repair_Cost -- Determines cost per "step" of repair. * + * * + * Use this routine to determine how much it will cost to repair the unit one * + * step. A step is defined as the number of hit points returned from the Repair_Step() * + * function. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the credit cost to repair this unit one step. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/03/1995 BWG : Created. * + *=============================================================================================*/ +int UnitTypeClass::Repair_Cost(void) const +{ + return(Fixed_To_Cardinal(Cost/(MaxStrength/REPAIR_STEP), REPAIR_PERCENT)); +} + + +/*********************************************************************************************** + * UnitTypeClass::Repair_Step -- Determines the repair step rate. * + * * + * This routine will determine how many strength points get healed for each "step". The * + * cost to repair one step is determine from the Repair_Cost() function. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of health points repaired for each "step". * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/03/1995 BWG : Created. * + *=============================================================================================*/ +int UnitTypeClass::Repair_Step(void) const +{ + return(REPAIR_STEP); +} + + +/*********************************************************************************************** + * UnitTypeClass::Max_Pips -- Fetches the maximum pips allowed for this unit. * + * * + * This routine will determine the number of pips (maximum) allowed for this unit type. * + * Typically, this is the number of passengers allowed, but for harvesters, it is the * + * number of credits it holds divided by 100. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the maximum number of pips allowed for this unit type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/26/1995 JLB : Created. * + *=============================================================================================*/ +int UnitTypeClass::Max_Pips(void) const +{ + if (Type == UNIT_HARVESTER) { + return(FULL_LOAD_CREDITS/100); + } + + if (IsTransporter) { + return(Max_Passengers()); + } + return(0); +} diff --git a/UNIT.CPP b/UNIT.CPP new file mode 100644 index 0000000..989740c --- /dev/null +++ b/UNIT.CPP @@ -0,0 +1,4051 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\unit.cpv 2.17 16 Oct 1995 16:48:28 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : UNIT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : August 16, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Recoil_Adjust -- Adjust pixel values in direction specified. * + * Turret_Adjust -- Turret adjustment routine for MLRS and MSAM units. * + * UnitClass::AI -- AI processing for the unit. * + * UnitClass::APC_Close_Door -- Closes an APC door. * + * UnitClass::APC_Open_Door -- Opens an APC door. * + * UnitClass::Active_Click_With -- Intercepts the active click to see if deployment is possib* + * UnitClass::As_Target -- Returns the unit as a target value. * + * UnitClass::Blocking_Object -- Determines how a object blocks a unit * + * UnitClass::Can_Enter_Building -- Determines building entry legality. * + * UnitClass::Can_Fire -- Determines if this unit can fire. * + * UnitClass::Can_Player_Move -- Determines if the player is legally allowed to move it. * + * UnitClass::Click_With -- Handles player map clicking while this unit is selected. * + * UnitClass::Crew_Type -- Fetches the kind of crew that this object produces. * + * UnitClass::Debug_Dump -- Displays the status of the unit to the mono monitor. * + * UnitClass::Desired_Load_Dir -- Determines the best cell and facing for loading. * + * UnitClass::Draw_It -- Draws a unit object. * + * UnitClass::Enter_Idle_Mode -- Unit enters idle mode state. * + * UnitClass::Find_LZ -- Maintenance function for transport units. * + * UnitClass::Flag_Attach -- Attaches a house flag to this unit. * + * UnitClass::Flag_Remove -- Removes the house flag from this unit. * + * UnitClass::Goto_Clear_Spot -- Finds a clear spot to deploy. * + * UnitClass::Goto_Tiberium -- Search for and head toward nearest available Tiberium patch. * + * UnitClass::Harvesting -- Harvests tiberium at the current location. * + * UnitClass::Init -- Clears all units for scenario preparation. * + * UnitClass::Limbo -- Prepares vehicle and then limbos it. * + * UnitClass::Look -- Perform map revelation from a unit's position. * + * UnitClass::Mission_Attack -- Handles the mission attack logic. * + * UnitClass::Mission_Guard -- Special guard mission override processor. * + * UnitClass::Mission_Harvest -- Handles the harvesting process used by harvesters. * + * UnitClass::Mission_Hunt -- This is the AI process for aggressive enemy units. * + * UnitClass::Mission_Move -- Handles special move mission overrides. * + * UnitClass::Mission_Unload -- Handles unloading cargo. * + * UnitClass::Overlap_List -- Determines overlap list for units. * + * UnitClass::Per_Cell_Process -- Performs operations necessary on a per cell basis. * + * UnitClass::Pip_Count -- Fetchs the number of pips to display on unit. * + * UnitClass::Random_Animate -- Handles random idle animation for the unit. * + * UnitClass::Read_INI -- Reads units from scenario INI file. * + * UnitClass::Receive_Message -- Handles receiving a radio message. * + * UnitClass::Remap_Table -- Fetches the remap table to use for this object. * + * UnitClass::Response_Attack -- Voice feedback when ordering the unit to attack a target. * + * UnitClass::Response_Move -- Voice feedback when ordering the unit to move. * + * UnitClass::Response_Select -- Voice feedback when selecting the unit. * + * UnitClass::Scatter -- Causes the unit to travel to a nearby safe cell. * + * UnitClass::Set_Speed -- Initiate unit movement physics. * + * UnitClass::Sort_Y -- Give Y coordinate sort value for unit. * + * UnitClass::Start_Driver -- Starts driving and reserves destination cell. * + * UnitClass::Stop_Driver -- Handles removing occupation bits when driving stops. * + * UnitClass::Stun -- Stuns the unit in preparation for unit removal. * + * UnitClass::Take_Damage -- Inflicts damage points on a unit. * + * UnitClass::Target_Coord -- The coordinate to use when targeting this unit. * + * UnitClass::Try_To_Deploy -- The unit attempts to "deploy" at current location. * + * UnitClass::UnitClass -- Constructor for units. * + * UnitClass::Unlimbo -- Removes unit from stasis. * + * UnitClass::Unload_Hovercraft_Process -- Handles unloading hovercraft transport. * + * UnitClass::Validate -- validates unit pointer. * + * UnitClass::What_Action -- Determines what action would occur if clicked on object. * + * UnitClass::What_Am_I -- Returns with the RTTI type this object is. * + * UnitClass::Write_INI -- Writes all the units out to an INI file. * + * UnitClass::delete -- Deletion operator for units. * + * UnitClass::new -- Allocate a unit slot and adjust access arrays. * + * UnitClass::~UnitClass -- Destructor for unit objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** This contains the value of the Virtual Function Table Pointer +*/ +void * UnitClass::VTable; + + +/*********************************************************************************************** + * UnitClass::Validate -- validates unit pointer. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = ok, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/09/1995 BRR : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +int UnitClass::Validate(void) const +{ + int num; + + num = Units.ID(this); + if (num < 0 || num >= UNIT_MAX) { + Validate_Error("UNIT"); + return (0); + } + else + return (1); +} +#else +#define Validate() +#endif + + +/*********************************************************************************************** + * Recoil_Adjust -- Adjust pixel values in direction specified. * + * * + * This is a helper routine that modifies the pixel coordinates provided according to the * + * direction specified. The effect is the simulate recoil effects by moving an object 'back'* + * one pixel. Since the pixels moved depend on facing, this routine handles the pixel * + * adjustment quickly. * + * * + * INPUT: dir -- The direction to base the recoil on. * + * * + * x,y -- References to the pixel coordinates that will be adjusted. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void Recoil_Adjust(DirType dir, int &x, int &y) +{ + static struct { + signed char X,Y; + } _adjust[32] = { + {0,1}, // N + {0,1}, + {0,1}, + {-1,1}, + {-1,1}, // NE + {-1,1}, + {-1,0}, + {-1,0}, + {-1,0}, // E + {-1,0}, + {-1,-1}, + {-1,-1}, + {-1,-1}, // SE + {-1,-1}, + {-1,-1}, + {0,-1}, + {0,-1}, // S + {0,-1}, + {0,-1}, + {1,-1}, + {1,-1}, // SW + {1,-1}, + {1,0}, + {1,0}, + {1,0}, // W + {1,0}, + {1,1}, + {1,1}, + {1,1}, // NW + {1,1}, + {0,1}, + {0,1} + }; + + int index = Facing_To_32(dir); + x += _adjust[index].X; + y += _adjust[index].Y; +} + + +/*********************************************************************************************** + * Turret_Adjust -- Turret adjustment routine for MLRS and MSAM units. * + * * + * This routine adjusts the pixel coordinates specified to account for the displacement of * + * the turret on the MLRS and MSAM vehicles. * + * * + * INPUT: dir -- The direction of the body of the vehicle. * + * * + * x,y -- References to the turret center pixel position. These will be modified as * + * necessary. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void Turret_Adjust(DirType dir, int &x, int &y) +{ + static struct { + signed char X,Y; + } _adjust[32] = { + {1,2}, // N + {-1,1}, + {-2,0}, + {-3,0}, + {-3,1}, // NW + {-4,-1}, + {-4,-1}, + {-5,-2}, + {-5,-3}, // W + {-5,-3}, + {-3,-3}, + {-3,-4}, + {-3,-4}, // SW + {-3,-5}, + {-2,-5}, + {-1,-5}, + {0,-5}, // S + {1,-6}, + {2,-5}, + {3,-5}, + {4,-5}, // SE + {6,-4}, + {6,-3}, + {6,-3}, + {6,-3}, // E + {5,-1}, + {5,-1}, + {4,0}, + {3,0}, // NE + {2,0}, + {2,1}, + {1,2} + }; + + int index = Facing_To_32(dir); + x += _adjust[index].X; + y += _adjust[index].Y; +} + + +/*********************************************************************************************** + * UnitClass::As_Target -- Returns the unit as a target value. * + * * + * This routine will convert the unit into a target value that is then returned. Target * + * values are typically used for navigation and other computer uses. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with target value of unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +TARGET UnitClass::As_Target(void) const +{ + Validate(); + return(Build_Target(KIND_UNIT, Units.ID(this))); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * UnitClass::Debug_Dump -- Displays the status of the unit to the mono monitor. * + * * + * This displays the current status of the unit class to the mono monitor. By this display * + * bugs may be tracked down or prevented. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Debug_Dump(MonoClass *mono) const +{ + Validate(); + mono->Set_Cursor(0, 0); + mono->Print( + "ÚName:ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂMission:ÄÄÄÂTarCom:ÂNavCom:ÂRadio:ÂCoord:ÄÄÂHeadTo:ÄÂSt:Ä¿\n" + "³ ³ ³ ³ ³ ³ ³ ³ ³\n" + "ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂNÂYÂHealth:ÄÂBody:ÂTurret:ÂSpeed:ÂPath:ÁÄÄÄÄÄÄÂCargo:ÄÄÄÄÁÄÄÄÄ´\n" + "³Active........³ ³ ³ ³ ³ ³ ³ ³ ³\n" + "³Limbo.........³ ³ ÃÄÄÄÄÄÄÄÄÁÄÄÄÄÄÁÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n" + "³Owned.........³ ³ ³Last Message: ³\n" + "³Discovered....³ ³ ÃTimer:ÂArm:ÂTrack:ÂTiberium:ÂFlash:ÂStage:ÂTeam:ÄÄÄÄÂArch:´\n" + "³Selected......³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³\n" + "³Teathered.....³ ³ ÃÄÄÄÄÄÄÁÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÙ\n" + "³Locked on Map.³ ³ ³ \n" + "³Turret Locked.³ ³ ³ \n" + "³Is A Loaner...³ ³ ³ \n" + "³Deploying.....³ ³ ³ \n" + "³Rotating......³ ³ ³ \n" + "³Firing........³ ³ ³ \n" + "³Driving.......³ ³ ³ \n" + "³To Look.......³ ³ ³ \n" + "³Recoiling.....³ ³ ³ \n" + "³To Display....³ ³ ³ \n" + "ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÁÄÙ \n"); + mono->Set_Cursor(1, 1);mono->Printf("%s:%s", House->Class->IniName, Class->IniName); + CargoClass::Debug_Dump(mono); + MissionClass::Debug_Dump(mono); + TarComClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * UnitClass::Sort_Y -- Give Y coordinate sort value for unit. * + * * + * This routine is used by the rendering system in order to sort the * + * game objects in a back to front order. This is now the correct * + * overlap effect is achieved. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a coordinate value that can be used for sorting. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +COORDINATE UnitClass::Sort_Y(void) const +{ + Validate(); + if (IsTethered && *this == UNIT_HOVER) { + return(Coord_Add(Coord, 0xFF800000L)); + } + return(Coord_Add(Coord, 0x00800000L)); +} + + +/*********************************************************************************************** + * UnitClass::AI -- AI processing for the unit. * + * * + * This routine will perform the AI processing necessary for the unit. These are non- * + * graphic related operations. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::AI(void) +{ + Validate(); + + /* + ** Act on new orders if the unit is at a good position to do so. + */ + if (!IsDriving && Is_Door_Closed() /*Mission != MISSION_UNLOAD*/) { + Commence(); + } + + TarComClass::AI(); + + /* + ** Delete this unit if it finds itself off the edge of the map and it is in + ** guard or other static mission mode. + */ + if (!Team && Mission == MISSION_GUARD && !Map.In_Radar(Coord_Cell(Coord))) { + Stun(); + delete this; + return; + } + + /* + ** Rocket launchers will reload every so often. + */ + if (*this == UNIT_MSAM && Ammo < Class->MaxAmmo) { + if (IsDriving) { + Reload = Reload + 1; + } else { + if (Reload.Expired()) { + Ammo++; + if (Ammo < Class->MaxAmmo) { + Reload = TICKS_PER_SECOND*30; + } + Mark(MARK_CHANGE); + } + } + } + + /* + ** Hover landers always are flagged to redraw since they don't record themselves + ** on the map in the normal fashion. + */ + if (*this == UNIT_HOVER) { +// Mark_For_Redraw(); +//if (IsDown) Mono_Printf("*"); + Mark(MARK_CHANGE); + } + + /* + ** If this is a vehicle that heals itself (e.g., Mammoth Tank), then it will perform + ** the heal logic here. + */ + if (*this == UNIT_HTANK && (Frame % 16) == 0 && Health_Ratio() < 0x0080) { + Strength++; + Mark(MARK_CHANGE); + } + if (*this == UNIT_VICE && Map[Coord_Cell(Coord)].Land_Type() == LAND_TIBERIUM && Health_Ratio() < 0x0100 && (Frame % 16) == 0) { + Strength++; + Mark(MARK_CHANGE); + } + + /* + ** Transporters require special logic handled here since there isn't a MISSION_WAIT_FOR_PASSENGERS + ** mission that they can follow. Passenger loading is merely a part of their normal operation. + */ + if (Class->IsTransporter) { + + /* + ** Double check that there is a passenger that is trying to load or unload. + ** If not, then close the door. + */ + if (!Is_Door_Closed() && Mission != MISSION_UNLOAD && Transmit_Message(RADIO_TRYING_TO_LOAD) != RADIO_ROGER) { + APC_Close_Door(); + } + } + + /* + ** Don't start a new mission unless the vehicle is in the center of + ** a cell (not driving) and the door (if any) is closed. + */ + if (!IsDriving && Is_Door_Closed()/*&& Mission != MISSION_UNLOAD*/) { + Commence(); + } + + /* + ** Handle recoil recovery here. + */ + if (IsInRecoilState) { + IsInRecoilState = false; + Mark(MARK_CHANGE); + } + + /* + ** For animating objects such as Visceroids, max-out their animation + ** stages here + */ + if (Class->IsAnimating) { + if (!Fetch_Rate()) Set_Rate(2); + StageClass::Graphic_Logic(); + if (Fetch_Stage() >= Get_Build_Frame_Count(Class->Get_Image_Data())-1) { + Set_Stage(0); + } + } + + /* + ** for Jurassic objects, animate them if they're walking + */ + if (Class->IsPieceOfEight && Special.IsJurassic && AreThingiesEnabled) { + // Only animate if they're walking + if (IsDriving || IsFiring) { + if (!Fetch_Rate()) { + Set_Rate(Options.Normalize_Delay(2)); + Set_Stage(0); + } + StageClass::Graphic_Logic(); + if (Fetch_Stage() >= ( (IsDriving || *this == UNIT_TREX || *this == UNIT_RAPT) ? 8 : 12) ) { + Set_Stage(0); + if (IsFiring) { + Set_Rate(0); + IsFiring = false; + } + } + } + } + + /* + ** A cloaked object that is carrying the flag will always shimmer. + */ + if (Cloak == CLOAKED && Flagged != HOUSE_NONE) { + Do_Shimmer(); + } + +} + + +/*********************************************************************************************** + * UnitClass::Can_Fire -- Determines if this unit can fire. * + * * + * INPUT: target -- The target that is desired to fire upon. * + * * + * which -- The weapon (primary=0, secondary=1) to fire. * + * * + * OUTPUT: Returns with the fire error number if it cannot fire or else FIRE_OK. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +FireErrorType UnitClass::Can_Fire(TARGET target, int which) const +{ + Validate(); + FireErrorType cf; + + cf = TarComClass::Can_Fire(target, which); + if (cf == FIRE_OK) { + + /* + ** If it's a dinosaur, when it's OK to fire we should start the firing + ** animation, but wait for the proper attack stage before starting the + ** bullet, so things won't die prematurely. + */ + if (Class->IsFireAnim) { + if (!IsFiring) { + UnitClass *nonconst; + + nonconst = (UnitClass *)this; + nonconst->Set_Rate(Options.Normalize_Delay(2)); + nonconst->Set_Stage(0); + IsFiring = true; + cf = FIRE_BUSY; + } else { + if (Fetch_Stage() < 4) cf = FIRE_BUSY; + } + } + } + return(cf); +} + + +/*********************************************************************************************** + * UnitClass::Receive_Message -- Handles receiving a radio message. * + * * + * This is the handler function for when a unit receives a radio * + * message. Typical use of this is when a unit unloads from a hover * + * class so that clearing of the transport is successful. * + * * + * INPUT: from -- Pointer to the originator of the message. * + * * + * message -- The radio message received. * + * * + * param -- Reference to an optional parameter the might be needed to return * + * information back to the originator of the message. * + * * + * OUTPUT: Returns with the radio message response. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1994 JLB : Created. * + *=============================================================================================*/ +RadioMessageType UnitClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + Validate(); + switch (message) { + + /* + ** Asks if the passenger can load on this transport. + */ + case RADIO_CAN_LOAD: + if (Class->IsTransporter && How_Many() < Class->Max_Passengers() && from && House->Class->House == from->Owner()) { + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + + /* + ** The refinery has told this harvester that it should begin the backup procedure + ** so that proper unloading may take place. + */ + case RADIO_BACKUP_NOW: + TarComClass::Receive_Message(from, message, param); + if (!IsRotating && PrimaryFacing != DIR_SW) { + Do_Turn(DIR_SW); + } else { + if (!IsDriving) { + Force_Track(BACKUP_INTO_REFINERY, Adjacent_Cell(Center_Coord(), FACING_N)); + Set_Speed(128); + } + } + return(RADIO_ROGER); + + /* + ** This message is sent by the passenger when it determines that it has + ** entered the transport. + */ + case RADIO_IM_IN: + if (How_Many() == Class->Max_Passengers()) { + APC_Close_Door(); + } + return(RADIO_ATTACH); + + /* + ** Docking maintenance message received. Check to see if new orders should be given + ** to the impatient unit. + */ + case RADIO_DOCKING: + if (Class->IsTransporter && *this == UNIT_APC && How_Many() < Class->Max_Passengers()) { + TarComClass::Receive_Message(from, message, param); + + if (!IsDriving && !IsRotating && !IsTethered) { + + /* + ** If the potential passenger needs someplace to go, then figure out a good + ** spot and tell it to go. + */ + if (Transmit_Message(RADIO_NEED_TO_MOVE, from) == RADIO_ROGER) { + + CELL cell; + DirType dir = Desired_Load_Dir(from, cell); + + /* + ** If no adjacent free cells are detected, then passenger loading + ** cannot occur. Break radio contact. + */ + if (cell == 0) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + param = (long)::As_Target(cell); + Do_Turn(dir); + + /* + ** If it is now facing the correct direction, then open the + ** transport doors. Close the doors if the transport is or needs + ** to rotate. + */ + if (IsRotating) { + if (!Is_Door_Closed()) { + APC_Close_Door(); + } + } else { + if (!Is_Door_Open()) { + APC_Open_Door(); + } + } + + /* + ** Tell the potential passenger where it should go. If the passenger is + ** already at the staging location, then tell it to move onto the transport + ** directly. + */ + if (Transmit_Message(RADIO_MOVE_HERE, param, from) == RADIO_YEA_NOW_WHAT) { + if (Is_Door_Open()) { + param = (long)As_Target(); + Transmit_Message(RADIO_TETHER); + if (Transmit_Message(RADIO_MOVE_HERE, param, from) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + Contact_With_Whom()->Unselect(); + } + } + } + } + } + } + return(RADIO_ROGER); + } + break; + + /* + ** When this message is received, it means that the other object + ** has already turned its radio off. Turn this radio off as well. + */ + case RADIO_OVER_OUT: + if (Mission == MISSION_RETURN) { + Assign_Mission(MISSION_GUARD); + } + TarComClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + } + return(TarComClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * UnitClass::Unlimbo -- Removes unit from stasis. * + * * + * This routine will place a unit into the game and out of its limbo * + * state. This occurs whenever a unit is unloaded from a transport. * + * * + * INPUT: coord -- The coordinate to make the unit appear. * + * * + * dir -- The initial facing to impart upon the unit. * + * * + * OUTPUT: bool; Was the unit unlimboed successfully? If the desired * + * coordinate is illegal, then this might very well return * + * false. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Unlimbo(COORDINATE coord, DirType dir) +{ + Validate(); + /* + ** All units must start out facing one of the 8 major directions. + */ + dir = Facing_Dir(Dir_Facing(dir)); + + if (TarComClass::Unlimbo(coord, dir)) { + + /* + ** Ensure that the owning house knows about the + ** new object. + */ + House->UScan |= (1L << Class->Type); + House->ActiveUScan |= (1L << Class->Type); + + /* + ** If it starts off the edge of the map, then it already starts cloaked. + */ + if (IsCloakable && !IsLocked) Cloak = CLOAKED; + + /* + ** Units default to no special animation. + */ + Set_Rate(0); + Set_Stage(0); + + /* + ** A gun boat and a hover craft are allowed to exit the map thus we + ** flag them so they can. This undoes the work of Techno::Unlimbo which + ** stole their IsALoaner flag. + */ + if (*this == UNIT_GUNBOAT || *this == UNIT_HOVER) { + IsALoaner = true; + } + + /* + ** Start the gunboat animating when it is unlimboed. + */ + if (*this == UNIT_GUNBOAT) { + Set_Rate(2); + Set_Stage(0); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Take_Damage -- Inflicts damage points on a unit. * + * * + * This routine will inflict the specified number of damage points on * + * the given unit. If the unit is destroyed, then this routine will * + * remove the unit cleanly from the game. The return value indicates * + * whether the unit was destroyed. This will allow appropriate death * + * animation or whatever. * + * * + * INPUT: damage-- The number of damage points to inflict. * + * * + * distance -- The distance from the damage center point to the object's center point.* + * * + * warhead--The type of damage to inflict. * + * * + * source -- Who is responsible for this damage? * + * * + * OUTPUT: Returns the result of the damage process. This can range from RESULT_NONE up to * + * RESULT_DESTROYED. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/30/1991 JLB : Created. * + * 07/12/1991 JLB : Script initiated by unit destruction. * + * 04/15/1994 JLB : Converted to member function. * + * 04/16/1994 JLB : Warhead modifier. * + * 06/03/1994 JLB : Added the source of the damage target value. * + * 06/20/1994 JLB : Source is a base class pointer. * + * 11/22/1994 JLB : Shares base damage handler for techno objects. * + * 06/30/1995 JLB : Lasers do maximum damage against gunboat. * + * 08/16/1995 JLB : Harvester crushing doesn't occur on early missions. * + *=============================================================================================*/ +ResultType UnitClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source) +{ + Validate(); + ResultType res = RESULT_NONE; + + /* + ** Special case: If this is a laser attacking a gunboat, then the gunboat is ALWAYS toasted. + */ + if (*this == UNIT_GUNBOAT && warhead == WARHEAD_LASER) { + damage = Strength*3; + } + + /* + ** Remember if this object was selected. If it was and it gets destroyed and it has + ** passengers that pop out, then the passengers will inherit the select state. + */ + bool select = (IsSelected && IsOwnedByPlayer); + + /* + ** In order for a this to be damaged, it must either be a unit + ** with a crew or a sandworm. + */ + res = TarComClass::Take_Damage(damage, distance, warhead, source); + + if (res == RESULT_DESTROYED) { + Death_Announcement(source); + if (Class->Explosion != ANIM_NONE) { + AnimType anim = Class->Explosion; + + /* + ** SSM launchers will really explode big if they are carrying + ** missiles at the time of the explosion. + */ + if (*this == UNIT_MSAM && Ammo) { + anim = ANIM_NAPALM3; + } + + if (*this == UNIT_TRIC || *this == UNIT_TREX || *this == UNIT_RAPT || *this == UNIT_STEG) { + Sound_Effect(VOC_DINODIE1, Coord); + } + + new AnimClass(anim, Coord); + + /* + ** When the flame tank blows up, it really blows up. It is + ** equivalent to a napalm strike. + */ + if (Class->Explosion == ANIM_NAPALM3) { + Explosion_Damage(Coord, 200, source, WARHEAD_FIRE); + } + + /* + ** Very strong units that have an explosion will also rock the + ** screen when they are destroyed. + */ + if (Class->MaxStrength > 400) { + Shake_Screen(3); + } + } + + /* + ** Possibly have the crew member run away. + */ + Mark(MARK_UP); + if (Class->IsCrew && !Class->IsTransporter) { + if (Random_Pick(0, 1) == 0) { + InfantryClass * i = 0; + if (Class->Primary == WEAPON_NONE) { + i = new InfantryClass(INFANTRY_C1, House->Class->House); + i->IsTechnician = true; + } else { + i = new InfantryClass(INFANTRY_E1, House->Class->House); + } + if (i) { + if (i->Unlimbo(Coord, DIR_N)) { + i->Strength = Random_Pick(5, (int)i->Class->MaxStrength/2); + i->Scatter(0, true); + if (!House->IsHuman) { + i->Assign_Mission(MISSION_HUNT); + } else { + i->Assign_Mission(MISSION_GUARD); + } + if (select) i->Select(); + } else { + delete i; + } + } + } + } else { + if (*this != UNIT_HOVER) { + while (Is_Something_Attached()) { + FootClass * object = Detach_Object(); + + if (!object) break; // How can this happen? + + /* + ** Only infantry can run from a destroyed vehicle. Even then, it is not a sure + ** thing. + */ + if (object->Is_Infantry() && object->Unlimbo(Coord, DIR_N)) { + // object->Strength = Random_Pick(5, (int)((InfantryClass*)object)->Class->MaxStrength/2); + object->Scatter(0, true); + if (select) object->Select(); + } else { + object->Record_The_Kill(source); + delete object; + } + } + } else { + Kill_Cargo(source); + } + } + + /* + ** When the mobile head quarters blows up, the entire side blows up. + */ + if (*this == UNIT_MHQ) { + House->Flag_To_Die(); + } + + /* + ** Finally, delete the vehicle. + */ + delete this; + + } else { + + /* + ** When damaged and below half strength, start smoking if + ** it isn't already smoking. + */ + if (Health_Ratio() < 0x0080 && !IsAnimAttached && *this != UNIT_VICE && *this != UNIT_STEG && *this != UNIT_TREX && *this != UNIT_TRIC && *this != UNIT_RAPT) { + AnimClass * anim = new AnimClass(ANIM_SMOKE_M, Coord_Add(Coord, XYP_Coord(0, -8))); + if (anim) anim->Attach_To(this); + } + + /* + ** If at half damage, then start smoking or burning as appropriate. + */ + if (res == RESULT_HALF) { + if (*this == UNIT_GUNBOAT) { + AnimClass * anim = new AnimClass(ANIM_ON_FIRE_MED, Coord_Add(Coord, XYP_Coord(Random_Pick(0, 16)-8, -2))); + if (anim) anim->Attach_To(this); + } + } + + /* + ** Try to crush anyone that fires on this unit if possible. The harvester + ** typically is the only one that will qualify here. + */ + if (!Team && source && !IsTethered && !House->Is_Ally(source) && (!House->IsHuman || Special.IsSmartDefense)) { + + /* + ** Try to crush the attacker if it can be crushed by this unit and this unit is + ** not equipped with a flame type weapon. If this unit has a weapon and the target + ** is not very close, then fire on it instead. In easy mode, they never run over the + ** player. In hard mode, they always do. In normal mode, they only overrun past + ** mission #8. + */ + if ((Class->Primary == WEAPON_NONE || (Distance(source) < 0x0180 && BulletTypeClass::As_Reference(Weapons[Class->Primary].Fires).Warhead != WARHEAD_FIRE)) && + (GameToPlay != GAME_NORMAL || + *this != UNIT_HARVESTER || + BuildLevel > 8 || + Special.IsDifficult) && + !Special.IsEasy && + Class->IsCrusher && source->Is_Techno() && ((TechnoTypeClass const &)source->Class_Of()).IsCrushable) { + + Assign_Destination(source->As_Target()); + Assign_Mission(MISSION_MOVE); + } else { + + /* + ** Try to return to base if possible. + */ + if (*this == UNIT_HARVESTER && Pip_Count() && Health_Ratio() < 0x0080) { + /* + ** Find nearby refinery and head to it? + */ + BuildingClass * building = Find_Docking_Bay(STRUCT_REFINERY, false); + + /* + ** Since the refinery said it was ok to load, establish radio + ** contact with the refinery and then await docking orders. + */ + if (building && Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) { + Assign_Mission(MISSION_ENTER); + } + } + } + } + + /* + ** Computer controlled harvester will radio for help if they are attacked. + */ + if (*this == UNIT_HARVESTER && !House->IsHuman && source) { + Base_Is_Attacked(source); + } + } + return(res); +} + + +/*********************************************************************************************** + * UnitClass::new -- Allocate a unit slot and adjust access arrays. * + * * + * This routine will allocate a unit from the available unit pool and * + * fixup all the access lists to match. It will allocate a unit slot * + * from within the range allowed for the specified unit type. If no * + * slot was found, then it will fail. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocated unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/11/1994 JLB : Created. * + * 04/21/1994 JLB : Converted to operator new. * + *=============================================================================================*/ +void * UnitClass::operator new(size_t) +{ + void * ptr = (UnitClass *)Units.Allocate(); + if (ptr) { + ((UnitClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * UnitClass::delete -- Deletion operator for units. * + * * + * This removes the unit from the local allocation system. Since this * + * is a fixed block of memory, not much has to be done to delete the * + * unit. Merely marking it as inactive is enough. * + * * + * INPUT: ptr -- Pointer to the unit to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/21/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::operator delete(void *ptr) +{ + if (ptr) { + ((UnitClass *)ptr)->IsActive = false; + } + Units.Free((UnitClass *)ptr); +} + + +/*********************************************************************************************** + * UnitClass::~UnitClass -- Destructor for unit objects. * + * * + * This destructor will lower the unit count for the owning house as well as inform any * + * other units in communication, that this unit is about to leave reality. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/15/1994 JLB : Created. * + *=============================================================================================*/ +UnitClass::~UnitClass(void) +{ + if (GameActive && Class) { + + /* + ** If there are any cargo members, delete them. + */ + while (Is_Something_Attached()) { + delete Detach_Object(); + } + + Limbo(); + } + if (GameActive && Team) Team->Remove(this); +} + + +/*********************************************************************************************** + * UnitClass::UnitClass -- Constructor for units. * + * * + * This constructor for units will initialize the unit into the game * + * system. It will be placed in all necessary tracking lists. The initial condition will * + * be in a state of limbo. * + * * + * INPUT: classid -- The type of unit to create. * + * * + * house -- The house owner of this unit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/21/1994 JLB : Created. * + *=============================================================================================*/ +UnitClass::UnitClass(UnitType classid, HousesType house) : + TarComClass(classid, house) +{ + Flagged = HOUSE_NONE; + Reload = 0; + Ammo = Class->MaxAmmo; + IsCloakable = Class->IsCloakable; + if (Class->IsAnimating) Set_Rate(Options.Normalize_Delay(3)); + + /* + ** Keep count of the number of units created. + */ + if (GameToPlay == GAME_INTERNET){ + House->UnitTotals->Increment_Unit_Total((int)classid); + } +} + + +/*********************************************************************************************** + * UnitClass::Active_Click_With -- Intercepts the active click to see if deployment is possible* + * * + * This routine intercepts the active click operation. It check to see if this is a self * + * deployment request (MCV's have this ability). If it is, then the object is initiated * + * to self deploy. In the other cases, it passes the operation down to the lower * + * classes for processing. * + * * + * INPUT: action -- The action requested of the unit. * + * * + * object -- The object that the mouse pointer is over. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + Validate(); + if (action != What_Action(object)) { + switch (action) { + case ACTION_SABOTAGE: + case ACTION_CAPTURE: + action = ACTION_ATTACK; + break; + + case ACTION_ENTER: + action = ACTION_MOVE; + break; + + default: + action = ACTION_NONE; + break; + } + } + TarComClass::Active_Click_With(action, object); +} + + +void UnitClass::Active_Click_With(ActionType action, CELL cell) {TarComClass::Active_Click_With(action, cell);}; + + +/*********************************************************************************************** + * UnitClass::Enter_Idle_Mode -- Unit enters idle mode state. * + * * + * This routine is called when the unit completes one mission but does not have a clear * + * follow up mission to perform. In such a case, the unit should enter a default idle * + * state. This idle state varies depending on what the current internal computer * + * settings of the unit is as well as what kind of unit it is. * + * * + * INPUT: initial -- Is this called when the unit just leaves a factory or is initially * + * or is initially placed on the map? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 06/03/1994 JLB : Fixed to handle non-combat vehicles. * + * 06/18/1995 JLB : Allows a harvester to stop harvesting. * + *=============================================================================================*/ +void UnitClass::Enter_Idle_Mode(bool initial) +{ + Validate(); + MissionType order; + + /* + ** A movement mission without a NavCom would be pointless to have a radio contact since + ** no radio coordination occurs on a just a simple movement mission. + */ + if (Mission == MISSION_MOVE && !Target_Legal(NavCom)) { + Transmit_Message(RADIO_OVER_OUT); + } + + if (Class->Primary == WEAPON_NONE) { + if (Class->IsToHarvest) { + if (!In_Radio_Contact() && Mission != MISSION_HARVEST) { + if (initial || !House->IsHuman || Map[Coord_Cell(Coord)].Land_Type() == LAND_TIBERIUM) { + order = MISSION_HARVEST; + } else { + order = MISSION_GUARD; + } + Assign_Target(TARGET_NONE); + Assign_Destination(TARGET_NONE); + } else { + return; + } + } else { + if (IsALoaner && Class->IsTransporter && Is_Something_Attached() && !Team) { + order = MISSION_UNLOAD; + } else { + order = MISSION_GUARD; + Assign_Target(TARGET_NONE); + Assign_Destination(TARGET_NONE); + } + } + } else { + if (Target_Legal(TarCom)) { + order = MISSION_ATTACK; + } else { + if (Target_Legal(NavCom)) { + order = MISSION_MOVE; + } else { + if (GameToPlay == GAME_NORMAL || House->IsHuman) { + order = MISSION_GUARD; + } else { + if (GameToPlay != GAME_NORMAL) { + order = MISSION_TIMED_HUNT; + } else { + order = MISSION_HUNT; + } + } + } + } + } + Assign_Mission(order); +} + + +/*********************************************************************************************** + * UnitClass::Find_LZ -- Maintenance function for transport units. * + * * + * This is a maintenance function for transport units. It checks to see if it is loaded * + * with cargo, but it doesn't know where to put it. In such a case, the unit must look * + * for a landing zone (LZ) to unload the cargo. This routine should be called every so * + * often. Typically, calling this routine is controlled by the scripts. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Find_LZ(void) +{ + Validate(); + CELL cell; // Map exit cell number. + + if (*this == UNIT_HOVER) { + + if (!IsRotating && Is_Something_Attached() && !Target_Legal(NavCom)) { + cell = Map.Calculated_Cell(SOURCE_BEACH, House->Class->House); + Assign_Destination(::As_Target(cell)); + } + } +} + + +/*********************************************************************************************** + * UnitClass::Unload_Hovercraft_Process -- Handles unloading hovercraft transport. * + * * + * This is a maintenance routine to handle the special operations necessary for a * + * hovercraft transport. This routine checks to see if special unloading operations are * + * necessary and performs them. These operations can include unloading the transported * + * object, finding a new beach cell, and rotating to a convenient unloading facing. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Has the transport finished its unloading mission? This is true after the * + * hovercraft has completely dispatched its cargo. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 07/31/1995 JLB : Second infantry unloaded MUST be the one tethered. * + *=============================================================================================*/ +bool UnitClass::Unload_Hovercraft_Process(void) +{ + Validate(); + bool unloaded = false; + FootClass *unit; // The unit to be unloaded. + CELL cell; // Cell to unload to. + + /* + ** If the hovercraft is currently waiting for the last unit + ** to completely unload, then don't do anything. + */ + if (IsTethered || IsRotating) { + return(false); + } + + if (Is_Something_Attached()) { + + /* + ** Only unload if the hovercraft has reached the beach. + */ + if (!Target_Legal(NavCom)) { + + cell = Coord_Cell(Adjacent_Cell(Coord, Dir_Facing(PrimaryFacing.Current()))); + + unit = (FootClass *)Attached_Object(); + + Mark(MARK_UP); + if (Map.In_Radar(cell) && !Map[cell].Cell_Unit()) { + + if (unit->Can_Enter_Cell(cell, FACING_NONE) == MOVE_OK) { + + /* + ** Place all the transported units onto the map. + */ + int count = 0; + bool first = true; + FootClass * secondary = 0; + while (Attached_Object()) { + FootClass * u = (FootClass *)Detach_Object(); + + if (!first && !secondary) secondary = u; + first = false; + + /* + ** Place the unit on the map and start it heading in the right direction. + */ + ScenarioInit++; + if (u->Unlimbo(Coord_Add(Coord & 0xFF00FF00L, StoppingCoordAbs[count]), DIR_N)) { + u->Assign_Mission(MISSION_MOVE); + u->NavCom = ::As_Target(cell); + u->Path[0] = Dir_Facing(u->PrimaryFacing.Current()); + u->Path[1] = FACING_NONE; + u->Set_Speed(0x80); + u->IsUnloading = true; + + } else { + + /* + ** Couldn't unlimbo for some strange reason. Kill the unit off. + */ + delete u; + } + ScenarioInit--; + count++; + } + if (secondary) unit = secondary; + + /* + ** Establish radio contact bond with the transport + ** hovercraft. This bond tells the hovercraft to + ** not move until the unit has completely unloaded. + */ + if (Transmit_Message(RADIO_HELLO, unit) == RADIO_ROGER) { + Transmit_Message(RADIO_UNLOAD); + } + Mark(MARK_DOWN); + Map.Layer[LAYER_GROUND].Sort(); + Map.Layer[LAYER_GROUND].Sort(); + Map.Layer[LAYER_GROUND].Sort(); + return(false); + } else { + + /* + ** If the attached object cannot unload because the desired destination + ** cell is impassable, then abort the landing. This is faked by pretending + ** that the unload was successful. The controlling routine will cause + ** the transport to leave. + */ +Mark(MARK_DOWN); +return(false); +// return(true); + } + } + + Mark(MARK_DOWN); + } + } else { + return(true); + } + + return(unloaded); +} + + +/*********************************************************************************************** + * UnitClass::Goto_Clear_Spot -- Finds a clear spot to deploy. * + * * + * This routine is used by the MCV to find a clear spot to deploy. If a clear spot * + * is found, then the MCV will assign that location to its navigation computer. This only * + * occurs if the MCV isn't already heading toward a spot. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the located at a spot where it can deploy? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Goto_Clear_Spot(void) +{ + Validate(); + Mark(MARK_UP); + if (!Target_Legal(NavCom) && BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(Coord_Cell(Coord))) { + Mark(MARK_DOWN); + return(true); + } + + if (!Target_Legal(NavCom)) { + /* + ** This scan table is skewed to north scanning only. This should + ** probably be converted to a more flexible method. + */ + static int _offsets[] = { + -MAP_CELL_W*1, + -MAP_CELL_W*2, + -(MAP_CELL_W*2)+1, + -(MAP_CELL_W*2)-1, + -MAP_CELL_W*3, + -(MAP_CELL_W*3)+1, + -(MAP_CELL_W*3)-1, + -(MAP_CELL_W*3)+2, + -(MAP_CELL_W*3)-2, + -MAP_CELL_W*4, + -(MAP_CELL_W*4)+1, + -(MAP_CELL_W*4)-1, + -(MAP_CELL_W*4)+2, + -(MAP_CELL_W*4)-2, + 0 + }; + int *ptr; + + ptr = &_offsets[0]; + while (*ptr) { + CELL cell = Coord_Cell(Coord)+*ptr++; + + if (BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(cell)) { + Assign_Destination(::As_Target(cell)); + break; + } + } + } + Mark(MARK_DOWN); + + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Try_To_Deploy -- The unit attempts to "deploy" at current location. * + * * + * Certain units have the ability to deploy into a building. When this routine is called * + * for one of those units, it will attempt to deploy at its current location. If the unit * + * is in motion to a destination or it isn't one of the special units that can deploy or * + * it isn't allowed to deploy at this location for some reason it won't deploy. In all * + * other cases, it will begin to deploy and once it begins only a player abort action will * + * stop it. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was deployment begun? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Try_To_Deploy(void) +{ + Validate(); + if (!Target_Legal(NavCom) && !IsRotating) { + if (*this == UNIT_MCV) { + + /* + ** Determine if it is legal to deploy at this location. If not, tell the + ** player. + */ + Mark(MARK_UP); + if (!BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(Coord_Cell(Adjacent_Cell(Center_Coord(), FACING_NW)))) { + if (PlayerPtr == House) { + Speak(VOX_DEPLOY); + } + Mark(MARK_DOWN); + IsDeploying = false; + return(false); + } + Mark(MARK_DOWN); + + /* + ** If the unit is not facing the correct direction, then start it rotating + ** toward the right facing, but still flag it as if it had deployed. This is + ** because it will deploy as soon as it reaches the correct facing. + */ + if (PrimaryFacing.Current() != DIR_SW) { + Do_Turn(DIR_SW); +// PrimaryFacing.Set_Desired(DIR_SW); + IsDeploying = true; + return(true); + } + + /* + ** Since the unit is already facing the correct direction, actually do the + ** deploy logic. If for some reason this cannot occur, then don't delete the + ** unit, just mark it as not deploying. + */ + Mark(MARK_UP); + BuildingClass * building = new BuildingClass(STRUCT_CONST, House->Class->House); + if (building) { + if (building->Unlimbo(Adjacent_Cell(Coord, FACING_NW))) { + + /* + ** Always reveal the construction yard to the player that owned the + ** mobile construction vehicle. + */ + building->Revealed(House); + + /* + ** Force the newly placed construction yard to be in the same strength + ** ratio as the MCV that deployed into it. + */ + int ratio = Health_Ratio(); + building->Strength = Fixed_To_Cardinal(building->Class->MaxStrength, ratio); + /* + ** Force the MCV to drop any flag it was carrying. This will also set + ** the owner house's flag home cell (since the house's FlagHome is + ** presumably 0 at this point). + */ + Stun(); + delete this; + return(true); + } else { + + /* + ** Could not deploy the construction yard at this location! Just revert + ** back to normal "just sitting there" mode and await further instructions. + */ + delete building; + } + } + Mark(MARK_DOWN); + IsDeploying = false; + } + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Per_Cell_Process -- Performs operations necessary on a per cell basis. * + * * + * This routine will perform the operations necessary that occur when a unit is at the * + * center of a cell. These operations could entail deploying into a construction yard, * + * radioing a transport unit, and looking around for the enemy. * + * * + * INPUT: center -- Is the unit safely at the center of a cell? If it is merely "close" * + * to the center, then this parameter will be false. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1994 JLB : Created. * + * 06/17/1995 JLB : Handles case when building says "NO!" * + * 06/30/1995 JLB : Gunboats head back and forth now. * + *=============================================================================================*/ +void UnitClass::Per_Cell_Process(bool center) +{ + Validate(); + CELL cell = Coord_Cell(Coord); + TechnoClass * whom; + HousesType house; + + /* + ** If this is a unit that is driving onto a building then the unit must enter + ** the building as the final step. + */ + whom = Contact_With_Whom(); + if (IsTethered && whom && center) { + if (whom->What_Am_I() == RTTI_BUILDING && Mission == MISSION_ENTER) { + if (whom == Map[cell].Cell_Building()) { + switch (Transmit_Message(RADIO_IM_IN, whom)) { + case RADIO_ROGER: + break; + + case RADIO_ATTACH: + Mark(MARK_UP); + SpecialFlag = true; + Limbo(); + SpecialFlag = false; + whom->Attach(this); + return; + + default: + Scatter(true); + break; + } + } + } + } + + /* + ** When breaking away from a transport object or building, possibly + ** scatter or otherwise begin normal unit operations. + */ + if (IsTethered && center && Mission != MISSION_ENTER) { + if (Transmit_Message(RADIO_UNLOADED) == RADIO_RUN_AWAY) { + if (!Target_Legal(NavCom)) { + Scatter(0, true); + } + } else { + if (*this == UNIT_HARVESTER) { + if (Target_Legal(ArchiveTarget)) { + Assign_Mission(MISSION_HARVEST); + Assign_Destination(ArchiveTarget); + ArchiveTarget = TARGET_NONE; + } else { + + /* + ** Since there is no place to go, move away to clear + ** the pad for another harvester. + */ + if (!Target_Legal(NavCom)) { + Scatter(0, true); + } + } + } + } + } + +#ifdef OBSOLETE + /* + ** If this unit is on a teather, then cut it at this time so that + ** the "parent" unit is free to proceed. Note that the parent + ** unit might actually be a building. + */ + if (IsTethered && center && !Map[cell].Cell_Building()) { + if (!Tiberium || *this != UNIT_HARVESTER) { + Transmit_Message(RADIO_UNLOADED); + if (*this == UNIT_HARVESTER) { + if (Target_Legal(ArchiveTarget)) { + Assign_Mission(MISSION_HARVEST); + Assign_Destination(ArchiveTarget); + ArchiveTarget = TARGET_NONE; + } else { + + /* + ** Since there is no place to go, move away to clear + ** the pad for another harvester. + */ + Scatter(0, true); + } + } + } + } +#endif + +#ifdef OBSOLETE + /* + ** If the unit is at the center of the repair facility, and that was his + ** destination, then start him repairing. + */ + if (center && !IsRepairing) { + BuildingClass * b = As_Building(NavCom); + if (b && *b==STRUCT_REPAIR && Coord == b->Center_Coord()) { + NavCom = 0; + IsRepairing = true; + Transmit_Message(RADIO_REPAIR_BEGIN_ANIM); + } + } +#endif + + /* + ** Check to see if this is merely the end of a rotation for the MCV as it is + ** preparing to deploy. In this case, it should begin its deploy process. + */ + if (center && IsDeploying) { + Try_To_Deploy(); + if (!IsActive) return; // Unit no longer exists -- bail. + } + + /* + ** If this is a loaner unit and is is off the edge of the + ** map, then it gets eliminated. That is, unless it is carrying cargo. This means that + ** it is probably carrying an incoming reinforcement and it should not be eliminated. + */ + if (center && IsALoaner && !Map.In_Radar(cell)) { + if (IsReturning || !Is_Something_Attached()) { + if (*this == UNIT_GUNBOAT) { + CELL cell = Coord_Cell(Coord); + if (Cell_X(cell) <= Map.MapCellX) { + Assign_Mission(MISSION_HUNT); + Assign_Destination(::As_Target(XY_Cell(Map.MapCellX+Map.MapCellWidth, Cell_Y(cell)))); + Set_Speed(255); + PrimaryFacing = DIR_E; + } else { + Assign_Mission(MISSION_HUNT); + Assign_Destination(::As_Target(XY_Cell(Map.MapCellX-1, Cell_Y(cell)))); + Set_Speed(255); + PrimaryFacing = DIR_W; + } + Mark(MARK_DOWN); + } else { + Mark(MARK_DOWN); + Stun(); + delete this; + return; + } + } + } + +#ifdef OBSOLETE + /* + ** Destroy any crushable wall that is driven over by a tracked vehicle. + */ + CellClass * cellptr = &Map[cell]; + if (center && Class->Speed == SPEED_TRACK && cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + + if (optr->IsCrushable) { + cellptr->Reduce_Wall(100); + cellptr->Reduce_Wall(100); + cellptr->Reduce_Wall(100); + cellptr->Reduce_Wall(100); + cellptr->Reduce_Wall(100); + cellptr->Reduce_Wall(100); + } + } +#endif + + /* + ** Check to see if crushing of any unfortunate infantry is warranted. + */ + Overrun_Square(Coord_Cell(Coord), false); + + /* + ** The unit performs looking around at this time. If the + ** unit moved further than one square during the last track + ** move, don't do an incremental look. Do a full look around + ** instead. + */ + if (IsPlanningToLook) { + IsPlanningToLook = false; + Look(false); + } else { + Look(true); + } + + /* + ** Act on new orders if the unit is at a good position to do so. + */ + if (center) { + Commence(); + } + + /* + ** Certain units require some setup time after they come to a halt. + */ + if (Special.IsDefenderAdvantage && /*center &&*/ !Target_Legal(NavCom) && Path[0] == FACING_NONE) { + if (*this == UNIT_MLRS || *this == UNIT_ARTY || *this == UNIT_MSAM) { + Arm = Rearm_Delay(false)*2; + } + } + + /* + ** If there is a house flag here, then this unit just might pick it up. + */ + if (center && Flagged == HOUSE_NONE) { + + if (Map[cell].IsFlagged && Map[cell].Owner != House->Class->House) { + HouseClass::As_Pointer(Map[cell].Owner)->Flag_Attach(this); + } + } + + /* + ** If this is the unit's own flag-home-cell and the unit is carrying + ** a flag, destroy the house of the flag the unit is carrying. + */ + if (Flagged != HOUSE_NONE) { + + /* + ** If this vehicle is carrying your flag, then it will reveal the + ** map for you as well as itself. This gives you and opportunity to + ** attack the unit. + */ + if (!IsOwnedByPlayer && Flagged == PlayerPtr->Class->House) { + Map.Sight_From(Coord_Cell(Coord), Class->SightRange, true); + } + + /* + ** If the flag reaches the home cell for the player, then the flag's + ** owner will be destroyed. + */ + if (cell == HouseClass::As_Pointer(Owner())->FlagHome && center) { + house = Flagged; // Flag_Remove will clear 'Flagged', so save it + HouseClass::As_Pointer(house)->Flag_Remove(As_Target(),true); + HouseClass::As_Pointer(house)->Flag_To_Die(); + } + } + + TarComClass::Per_Cell_Process(center); +} + + +/*********************************************************************************************** + * UnitClass::Draw_It -- Draws a unit object. * + * * + * This routine is the one that actually draws a unit object. It displays the unit * + * according to its current state flags and centered at the location specified. * + * * + * INPUT: x,y -- The X and Y coordinate of where to draw the unit. * + * * + * window -- The clipping window to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + * 06/27/1994 JLB : Takes a window parameter. * + * 08/15/1994 JLB : Removed infantry support. * + * 01/07/1995 JLB : Harvester animation support. * + * 07/08/1995 JLB : Uses general purpose draw routine. * + *=============================================================================================*/ +void UnitClass::Draw_It(int x, int y, WindowNumberType window) +{ + Validate(); + int shapenum; // Working shape number. + void const *shapefile; // Working shape file pointer. + int facing = Facing_To_32(PrimaryFacing.Current()); + int tfacing = Facing_To_32(SecondaryFacing.Current()); + + /* + ** Verify the legality of the unit class. + */ + shapefile = Class->Get_Image_Data(); + if (!shapefile) return; + + /* + ** If drawing of this unit is not explicitly prohibited, then proceed + ** with the render process. + */ + if (Visual_Character() != VISUAL_HIDDEN) { + + /* + ** For eight facing units, adjust the facing number accordingly. + */ + if (Class->IsPieceOfEight) { + facing = Dir_Facing(PrimaryFacing.Current()); + } + + /* + ** Some units have only four facings, but are equipped with a turret + ** that has 32 facings. + */ + if (Class->IsChunkyShape) { + + /* + ** Special wake drawing occurs here. + */ + if (*this == UNIT_GUNBOAT) { + int shapestart; + int xx,yy; + + xx = x; + yy = y; + switch (Dir_Facing(PrimaryFacing.Current())) { + case FACING_NE: + case FACING_E: + case FACING_SE: + shapenum = UnitClass::BodyShape[tfacing]+96; + shapestart = 0; + //xx -= 4; + break; + + case FACING_W: + default: + shapenum = UnitClass::BodyShape[tfacing]; + shapestart = 6; + xx += 4; + break; + } + + CC_Draw_Shape(UnitTypeClass::WakeShapes, shapestart + (Fetch_Stage() % 6), xx-1, yy+3, window, SHAPE_CENTER|SHAPE_WIN_REL); + + if (Health_Ratio() < 0x0080) shapenum += 32; + if (Health_Ratio() < 0x0040) shapenum += 32; + + } else { + + /* + ** Special hovercraft shape is ALWAYS N/S. + */ + shapenum = 0; + + //shapenum = ((UnitClass::BodyShape[facing] + 4) / 8) & 0x03; + } + + } else { + + /* + ** Fetch the harvesting animation stage as appropriate. + */ + if (IsHarvesting && !PrimaryFacing.Is_Rotating() && !NavCom && !IsDriving) { + static char _hstage[6] = { + 0, 1, 2, 3, 2, 1 + }; + shapenum = 32 + (((UnitClass::BodyShape[facing]+2)/4)*4)+_hstage[Fetch_Stage()%sizeof(_hstage)]; + } else { + shapenum = UnitClass::BodyShape[facing]; + if (Class->IsAnimating) { + shapenum = Fetch_Stage(); + } + if (Class->IsPieceOfEight) { + shapenum = 0; + if (facing) shapenum = UnitClass::BodyShape[24+facing]; + if (IsDriving) shapenum = Fetch_Stage() + 16 + shapenum*8; + if (IsFiring) shapenum = Fetch_Stage() + 80 + shapenum*( ((*this == UNIT_TREX) || (*this == UNIT_RAPT)) ? 8 : 12); + } else { + + /* + ** Door opening and closing animation must be handled carefully. There are only + ** certain directions where this door animation will work. + */ + if (!Is_Door_Closed() && (PrimaryFacing == DIR_NW || PrimaryFacing == DIR_NE)) { + if (PrimaryFacing == DIR_NE) { + shapenum = 32; + } else { + if (PrimaryFacing == DIR_NW) { + shapenum = 35; + } + } + shapenum += Door_Stage(); + } + } + } + } + + /* + ** The artillery unit should have its entire body recoil when it fires. + */ + if (*this == UNIT_ARTY && IsInRecoilState) { + Recoil_Adjust(PrimaryFacing.Current(), x, y); + } + + /* + ** Actually perform the draw. Overlay an optional shimmer effect as necessary. + */ +//if (*this == UNIT_HOVER) { +// Mono_Printf("Display hover %p %d.\n", shapefile, shapenum); +//} + Techno_Draw_Object(shapefile, shapenum, x, y, window); + + /* + ** If there is a rotating radar dish, draw it now. + */ + if (Class->IsRadarEquipped) { + shapenum = 32 + (Frame % 32); + Techno_Draw_Object(shapefile, shapenum, x, y-5, window); + } + + /* + ** If there is a turret, then it must be rendered as well. This may include + ** firing animation if required. + */ + if (!Class->IsChunkyShape && Class->IsTurretEquipped) { + int x1 = x; + int y1 = y; + + /* + ** Determine which turret shape to use. This depends on if there + ** is any firing animation in progress. + */ + shapenum = TechnoClass::BodyShape[tfacing]+32; + + /* + ** The shape to use for the rocket launcher is dependant on the + ** ammo remaining. + */ + if (*this == UNIT_MSAM) { + if (Ammo == 0) shapenum += 64; + if (Ammo == 1) shapenum += 32; + } + + /* + ** A recoiling turret moves "backward" one pixel. + */ + if (IsInRecoilState) { + Recoil_Adjust(SecondaryFacing.Current(), x1, y1); + } + + /* + ** The Mobile SAM and the Missile Launchers need their turrets moved based + ** on the facing of the vehicle. + */ + if (*this == UNIT_MSAM || *this == UNIT_MLRS) { + Turret_Adjust(PrimaryFacing, x1, y1); + } + if (*this == UNIT_JEEP || *this == UNIT_BUGGY) { + y1 -= 4; + } + + /* + ** Actually perform the draw. Overlay an optional shimmer effect as necessary. + */ + Techno_Draw_Object(shapefile, shapenum, x1, y1, window); + } + + /* + ** If this unit has "piggy back" unit(s), then render it at the same time. + */ + if (*this == UNIT_HOVER && Is_Something_Attached()) { + TechnoClass * u = (TechnoClass *)Attached_Object(); + + int counter = 0; + for (;;) { + int x1,y1; + + if (Map.Coord_To_Pixel(Coord_Add(Coord_Add(Coord, 0xFF80FF80L), StoppingCoordAbs[counter++]), x1, y1)) { + u->Draw_It(x1, y1, WINDOW_TACTICAL); + } + if (!u->Next) break; + u = (TechnoClass *)u->Next; + } + } + } + + /* + ** If this unit is carrying the flag, then draw that on top of everything else. + */ + if (Flagged != HOUSE_NONE) { + CC_Draw_Shape(MixFileClass::Retrieve("FLAGFLY.SHP"), Frame % 14, x, y, window, SHAPE_CENTER|SHAPE_FADING|SHAPE_GHOST, HouseClass::As_Pointer(Flagged)->Remap_Table(false, false), Map.UnitShadow); + } + + TarComClass::Draw_It(x, y, window); +} + + +/*********************************************************************************************** + * UnitClass::Goto_Tiberium -- Search for and head toward nearest available Tiberium patch. * + * * + * This routine is used to move a harvester to a place where it can load up with * + * Tiberium. It will return true only if it can start harvesting. Otherwise, it sets * + * the navigation computer toward the nearest Tiberium and lets the unit head there * + * automatically. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is it located directly over a Tiberium patch? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Tiberium_Check(CELL ¢er, int x, int y) +{ + Validate(); + /* + ** If the specified offset from the origin will cause it + ** to spill past the map edge, then abort this cell check. + */ + if (Cell_X(center)+x < Map.MapCellX) return(false); + if (Cell_X(center)+x >= Map.MapCellX+Map.MapCellWidth) return(false); + if (Cell_Y(center)+y < Map.MapCellY) return(false); + if (Cell_Y(center)+y >= Map.MapCellY+Map.MapCellHeight) return(false); + + center = XY_Cell(Cell_X(center)+x, Cell_Y(center)+y); + + if ((GameToPlay != GAME_NORMAL || (!IsOwnedByPlayer || Map[center].IsVisible))) { + if (!Map[center].Cell_Techno() && Map[center].Land_Type() == LAND_TIBERIUM) { + return(true); + } + } + return(false); +} + + +bool UnitClass::Goto_Tiberium(void) +{ + Validate(); + if (!Target_Legal(NavCom)) { + CELL center = Coord_Cell(Center_Coord()); + if (Map[center].Land_Type() == LAND_TIBERIUM) { + return(true); + } else { + + /* + ** Perform a ring search outward from the center. + */ + for (int radius = 1; radius < 32; radius++) { + for (int x = -radius; x <= radius; x++) { + CELL cell = center; + if (Tiberium_Check(cell, x, -radius)) { + Assign_Destination(::As_Target(cell)); + return(false); + } + + cell = center; + if (Tiberium_Check(cell, x, +radius)) { + Assign_Destination(::As_Target(cell)); + return(false); + } + + cell = center; + if (Tiberium_Check(cell, -radius, x)) { + Assign_Destination(::As_Target(cell)); + return(false); + } + + + cell = center; + if (Tiberium_Check(cell, +radius, x)) { + Assign_Destination(::As_Target(cell)); + return(false); + } + } + } + } + } + + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Harvesting -- Harvests tiberium at the current location. * + * * + * This routine is used to by the harvester to harvest Tiberium at the current location. * + * When harvesting is complete, this routine will return true. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is harvesting complete? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Harvesting(void) +{ + Validate(); + CELL cell = Coord_Cell(Coord); + CellClass * ptr = &Map[cell]; + + /* + ** Keep waiting if still heading toward a spot to harvest. + */ + if (Target_Legal(NavCom)) return(true); + + if (Tiberium_Load() < 0x0100 && ptr->Land_Type() == LAND_TIBERIUM) { + + /* + ** Lift some Tiberium from the ground. Try to lift a complete + ** "level" of Tiberium. A level happens to be 6 steps. If there + ** is a partial level, then lift that instead. Never lift more + ** than the harvester can carry. + */ + int reducer = (ptr->OverlayData % 6) + 1; + reducer = ptr->Reduce_Tiberium(MIN(reducer, UnitTypeClass::STEP_COUNT-Tiberium)); + Tiberium += reducer; + Set_Stage(0); + Set_Rate(2); + + } else { + + /* + ** If the harvester is stopped on a non Tiberium field and the harvester + ** isn't loaded with Tiberium, then no further action can be performed + ** by this logic routine. Bail with a failure and thus cause a branch to + ** a better suited logic processor. + */ + Set_Stage(0); + Set_Rate(0); + return(false); + } + return(true); +} + + +/*********************************************************************************************** + * UnitClass::Mission_Unload -- Handles unloading cargo. * + * * + * This is the AI control sequence for when a transport desires to unload its cargo and * + * then exit the map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Mission_Unload(void) +{ + Validate(); + enum { + INITIAL_CHECK, + MANEUVERING, + OPENING_DOOR, + UNLOADING, + CLOSING_DOOR + }; + DirType dir; + CELL cell; + + switch (Class->Type) { + case UNIT_APC: + switch (Status) { + case INITIAL_CHECK: + dir = Desired_Load_Dir(NULL, cell); + if (How_Many() && cell != 0) { + Do_Turn(dir); + Status = MANEUVERING; + return(1); + } else { + Assign_Mission(MISSION_GUARD); + } + break; + + case MANEUVERING: + if (!IsRotating) { + APC_Open_Door(); + if (Is_Door_Opening()) { + Status = OPENING_DOOR; + return(1); + } + } + break; + + case OPENING_DOOR: + if (Is_Door_Open()) { + Status = UNLOADING; + return(1); + } else { + if (!Is_Door_Opening()) { + Status = INITIAL_CHECK; + } + } + break; + + case UNLOADING: + if (How_Many()) { + FootClass * passenger = Detach_Object(); + + if (passenger) { + DirType toface = DIR_S + PrimaryFacing; + bool placed = false; + + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + DirType newface = toface + face; + CELL newcell = Adjacent_Cell(Coord_Cell(Coord), newface); + + if (passenger->Can_Enter_Cell(newcell) == MOVE_OK) { + ScenarioInit++; + passenger->Unlimbo(Coord_Move(Coord, newface, 0x0080), newface); + ScenarioInit--; + passenger->Assign_Mission(MISSION_MOVE); + passenger->Assign_Destination(::As_Target(newcell)); + placed = true; + break; + } + } + + /* + ** If the attached unit could NOT be deployed, then re-attach + ** it and then bail out of this deploy process. + */ + if (!placed) { + Attach(passenger); + Status = CLOSING_DOOR; + } + } + } else { + Status = CLOSING_DOOR; + } + break; + + /* + ** Close APC door in preparation for normal operation. + */ + case CLOSING_DOOR: + if (Is_Door_Open()) { + APC_Close_Door(); + } + if (Is_Door_Closed()) { + Assign_Mission(MISSION_GUARD); + } + break; + } + break; + + case UNIT_MCV: + switch (Status) { + case 0: + Path[0] = FACING_NONE; + Status = 1; + break; + + case 1: + if (!IsDriving) { + Try_To_Deploy(); + if (IsDeploying) { + Status = 2; + } else { + Assign_Mission(MISSION_GUARD); + } + } + break; + + case 2: + break; + } + return(1); + + case UNIT_HOVER: + switch (Status) { + case 0: + if (Unload_Hovercraft_Process()) { + Status = 1; + } + break; + + /* + ** Hovercraft always leave the map when they finish + ** unloading. + */ + case 1: + if (Team) { + Team->Remove(this); + } + Exit_Map(); + break; + } + break; + } + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * UnitClass::Mission_Harvest -- Handles the harvesting process used by harvesters. * + * * + * This is the AI process used by harvesters when they are doing their harvesting action. * + * This entails searching for nearby Tiberium, heading there, harvesting, and then * + * returning to a refinery for unloading. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 06/21/1995 JLB : Force guard mode if no Tiberium found. * + *=============================================================================================*/ +int UnitClass::Mission_Harvest(void) +{ + Validate(); + enum { + LOOKING, + HARVESTING, + FINDHOME, + HEADINGHOME, + GOINGTOIDLE, + }; + + /* + ** A non-harvesting type unit will just sit still if it is given the harvest mission. This + ** allows combat units to act "brain dead". + */ + if (!Class->IsToHarvest) return(TICKS_PER_SECOND*30); + + switch (Status) { + + /* + ** Go and find a Tiberium field to harvest. + */ + case LOOKING: + IsHarvesting = false; + if (Goto_Tiberium()) { + IsHarvesting = true; + Set_Rate(2); + Set_Stage(0); + Status = HARVESTING; + return(1); + } else { + + /* + ** If the harvester isn't on Tiberium and it is not heading toward Tiberium, then + ** force it to go into guard mode. This will prevent the harvester from repeatedly + ** searching for Tiberium. + */ + if (!Target_Legal(NavCom)) { + Status = GOINGTOIDLE; + return(TICKS_PER_SECOND*15); + } + } + break; + + /* + ** Harvest at current location until full or Tiberium exhausted. + */ + case HARVESTING: + if (!Harvesting()) { + IsHarvesting = false; + if (Tiberium_Load() == 0x0100) { + Status = FINDHOME; + ArchiveTarget = ::As_Target(Coord_Cell(Coord)); + } else { + if (!Goto_Tiberium() && !Target_Legal(NavCom)) { + ArchiveTarget = TARGET_NONE; + Status = FINDHOME; + } else { + Status = HARVESTING; + IsHarvesting = true; + } + } + return(1); + } + break; + + /* + ** Find and head to refinery. + */ + case FINDHOME: + if (!Target_Legal(NavCom)) { + + /* + ** Find nearby refinery and head to it? + */ + BuildingClass * nearest = Find_Docking_Bay(STRUCT_REFINERY, false); + + /* + ** Since the refinery said it was ok to load, establish radio + ** contact with the refinery and then await docking orders. + */ + if (nearest && Transmit_Message(RADIO_HELLO, nearest) == RADIO_ROGER) { + Status = HEADINGHOME; + } else { + ScenarioInit++; + nearest = Find_Docking_Bay(STRUCT_REFINERY, false); + ScenarioInit--; + if (nearest) { + Assign_Destination(::As_Target(nearest->Nearby_Location(this))); + } + } + } + break; + + /* + ** In communication with refinery so that it will successfully dock and + ** unload. If, for some reason, radio contact was lost, then hunt for + ** another refinery to unload at. + */ + case HEADINGHOME: + Assign_Mission(MISSION_ENTER); + return(1); + + case GOINGTOIDLE: + Assign_Mission(MISSION_GUARD); + break; + + } + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * UnitClass::Mission_Hunt -- This is the AI process for aggressive enemy units. * + * * + * Computer controlled units must be intelligent enough to find enemies as well as to * + * attack them. This AI process will handle both the simple attack process as well as the * + * scanning for enemy units to attack. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Mission_Hunt(void) +{ + Validate(); + if (*this == UNIT_MCV) { + switch (Status) { + + /* + ** This stage handles locating a convenient spot, rotating to face the correct + ** direction and then commencing the deployment operation. + */ + case 0: + if (Goto_Clear_Spot()) { + if (Try_To_Deploy()) { + Status = 1; + } + } + break; + + /* + ** This stage watchdogs the deployment operation and if for some reason, the deployment + ** is aborted (the IsDeploying flag becomes false), then it reverts back to hunting for + ** a convenient spot to deploy. + */ + case 1: + if (!IsDeploying) { + Status = 0; + } + break; + } + } else { + + if (*this == UNIT_GUNBOAT) { + if (!Target_Legal(NavCom)) { + if (PrimaryFacing == DIR_W) { + Assign_Destination( ::As_Target(XY_Cell(Map.MapCellX-1, Cell_Y(Coord_Cell(Coord)))) ); + } else { + Assign_Destination( ::As_Target(XY_Cell(Map.MapCellX+Map.MapCellWidth, Cell_Y(Coord_Cell(Coord)))) ); + } + Set_Speed(255); + } + if (!Speed) { + Set_Speed(255); + } + if (!Target_Legal(TarCom) || !In_Range(TarCom, 0)) { + Target_Something_Nearby(THREAT_AREA); + } + } else { + return(TarComClass::Mission_Hunt()); + } + } + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * UnitClass::Look -- Perform map revelation from a unit's position. * + * * + * Reveal the map around the specified unit with the sighting range * + * associated with the specified unit. * + * * + * INPUT: incremental -- If looking is to process only the cells in the * + * outer ring of the unit's search radius, then * + * set this parameter to true. This method is * + * quite a bit faster than processing all cells, * + * but must be used with caution. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/08/1992 JLB : Created. * + * 03/08/1994 JLB : Added incremental option. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void UnitClass::Look(bool incremental) +{ + Validate(); + if (!IsInLimbo && IsOwnedByPlayer) { + int sight = Class->SightRange; + + if (sight) { + Map.Sight_From(Coord_Cell(Coord), sight, (*this == UNIT_GUNBOAT) ? false : incremental); + } + } +} + + +/*********************************************************************************************** + * UnitClass::Overlap_List -- Determines overlap list for units. * + * * + * The unit overlap list is used to keep track of which cells are to * + * be marked for redraw. This is critical in order to keep the units * + * displayed correctly. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the overlap list pointer for the unit at its * + * present position. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/26/1994 JLB : Created. * + * 06/19/1994 JLB : Uses Coord_Spillable_List function. * + *=============================================================================================*/ +short const * UnitClass::Overlap_List(void) const +{ + Validate(); + static short const _gunboat[] = {-3, -2, 2, 3, REFRESH_EOL}; + int size; + + /* + ** The gunboat is a special case. + */ + if (*this == UNIT_GUNBOAT) { + return(&_gunboat[0]); + } + + size = ICON_PIXEL_W; + if (IsSelected || IsFiring) { + size += 24; + } + if (IsSelected || Class->IsGigundo || IsAnimAttached) { + size = ICON_PIXEL_W*2; + } + return(Coord_Spillage_List(Coord, size)+1); +} + + +#ifdef NEVER +/********************************************************************************************* * + * UnitClass::Blocking_Object -- Determines how a object blocks a unit * + * * + * This routine is used by the Can_Enter_Cell logic when an object is in the desired cell * + * and it needs to know if that causes blockage. If blocked, this routine will return why. * + * * + * INPUT: TechnoClass * pointer to object that is blocking unit * + * * + * CELL the cell the unit is being blocked in * + * * + * OUTPUT: MoveBitType the way that the object is blocking the unit * + * * + * HISTORY: * + * 06/08/1995 PWG : Created. * + *=============================================================================================*/ +MoveBitType UnitClass::Blocking_Object(TechnoClass const *techno, CELL cell) const +{ + Validate(); + /* + ** There are some extra checks we need to make if the techno is a unit + */ + bool unit = (techno->What_Am_I() == RTTI_INFANTRY || techno->What_Am_I() == RTTI_UNIT); + CellClass const * cellptr = &Map[cell]; + + if (House->Is_Ally(techno)) { + + if (techno == Contact_With_Whom() && IsTethered) { + return(MOVE_BIT_OK); + } + + if (unit) { + /* + ** If the unit in question has a destination than we should + ** be prepared to wait for the unit to get out of our way. + */ + if (((FootClass *)techno)->NavCom != TARGET_NONE) { + int face = Dir_Facing(PrimaryFacing); + int techface = Dir_Facing(((FootClass const *)techno)->PrimaryFacing) ^4; + if (face != techface && Distance((AbstractClass const *)techno) > 0x1FF) { + return(MOVE_BIT_MOVING_BLOCK); + } else { +// Mono_Printf("Move No!\r"); + return(MOVE_BIT_NO); + } + } + + return(MOVE_BIT_TEMP); + } + } else { + + /* + ** If its an enemy unit, things are dealt with a little differently + */ + if (unit) { + +#ifdef NEVER + /* + ** If this is an enemy unit and we are not doing a find path then + ** we need to tell the unit to uncloak just in case it is a + ** stealth tank. + */ + if (!IsFindPath) { + techno->Do_Uncloak(); + } +#endif + + /* + ** Can we just run it over? + */ + if (techno->Class_Of().IsCrushable && (cellptr->Flag.Composite & 0xE0) == 0 && Class->IsCrusher) { + + /* + ** Now lets run it over. + */ + return(MOVE_BIT_OK); + } + + /* + ** If the object is cloaked, then consider it passable for findpath purposes, + ** but not so for all other cases. + */ + if (techno->Cloak == CLOAKED) { + if (House == techno->House) return(MOVE_BIT_NO); + if (IsFindPath) return(MOVE_BIT_OK); + return(MOVE_BIT_CLOAK); + } + + /* + ** If our vehicle is weapon equipped, then report that the cell occupier + ** needs only to be destroyed in order to make the cell passable. + */ + if (Class->Primary != WEAPON_NONE) { + return(MOVE_BIT_DESTROYABLE); + } + } + } + return(MOVE_BIT_NO); +} +#endif + + +/*********************************************************************************************** + * UnitClass::Can_Enter_Cell -- Determines cell entry legality. * + * * + * Use this routine to determine if the unit can enter the cell * + * specified and given the direction of entry specified. Typically, * + * this is used when determining unit travel path. * + * * + * INPUT: cell -- The cell to examine. * + * * + * facing -- The facing that the unit would enter the specified * + * cell. If this value is -1, then don't consider * + * facing when performing the check. * + * * + * OUTPUT: Returns the reason why it couldn't enter the cell or MOVE_OK if movement is * + * allowed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/07/1992 JLB : Created. * + * 04/16/1994 JLB : Converted to member function. * + * 07/04/1995 JLB : Allowed to drive on building trying to enter it. * + *=============================================================================================*/ +MoveType UnitClass::Can_Enter_Cell(CELL cell, FacingType ) const +{ + Validate(); + CellClass const * cellptr = &Map[cell]; + + if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO); + + /* + ** The gunboat can always move. This prevents it from trying to move around possible hover + ** craft blockage. + */ + if (*this == UNIT_GUNBOAT) return(MOVE_OK); + + /* + ** Moving off the edge of the map is not allowed unless + ** this is a loaner vehicle. + */ + if (IsLocked && !IsALoaner && !ScenarioInit && !Map.In_Radar(cell)) { + return(MOVE_NO); + } + + MoveType retval = MOVE_OK; + + /* + ** Certain vehicles can drive over walls. Check for this case and + ** and return the appropriate flag. Other units treat walls as impassable. + */ + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + + if (optr->IsCrate && !House->IsHuman) { + return(MOVE_NO); + } + + if (optr->IsWall) { + if (Class->Primary != WEAPON_NONE) { + WarheadTypeClass const * whead = &Warheads[BulletTypeClass::As_Reference(Weapons[Class->Primary].Fires).Warhead]; + + if (whead->IsWallDestroyer || (whead->IsWoodDestroyer && optr->IsWooden)) { + if (!House->IsHuman && !House->Is_Ally(cellptr->Owner)) { + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; + } else { + return(MOVE_NO); + } + } else { + return(MOVE_NO); + } + } else { + return(MOVE_NO); + } + } + } + + /* + ** If the cell is out and out impassable because of underlying terrain, then + ** return this immutable fact. + */ +#ifdef ADVANCED + if (retval != MOVE_DESTROYABLE && !Ground[cellptr->Land_Type()].Cost[Class->Speed]) { +#else + if (!Ground[cellptr->Land_Type()].Cost[Class->Speed]) { +#endif + return(MOVE_NO); + } + + /* + ** Loop through all of the objects in the square setting a bit + ** for how they affect movement. + */ + bool crushable = false; + ObjectClass *obj = cellptr->Cell_Occupier(); + while (obj) { + + if (obj != this) { + + /* + ** Always allow entry if trying to move on a cell with + ** authorization from the occupier. + */ + if (obj == Contact_With_Whom() && (IsTethered || (obj->What_Am_I() == RTTI_BUILDING && *((BuildingClass *)obj) == STRUCT_REPAIR))) { + return(MOVE_OK); + } + + bool is_moving = (obj->What_Am_I() == RTTI_INFANTRY || obj->What_Am_I() == RTTI_UNIT) && Target_Legal(((FootClass *)obj)->NavCom); + + if (House->Is_Ally(obj)) { + if (is_moving) { + int face = Dir_Facing(PrimaryFacing); + int techface = Dir_Facing(((FootClass const *)obj)->PrimaryFacing) ^4; + if (face == techface && Distance((AbstractClass const *)obj) <= 0x1FF) { + return(MOVE_NO); + } + if (retval < MOVE_MOVING_BLOCK) retval = MOVE_MOVING_BLOCK; + } else { + if (obj->What_Am_I() == RTTI_BUILDING) return(MOVE_NO); + if (retval < MOVE_TEMP) retval = MOVE_TEMP; + } + } else { + + /* + ** Cloaked enemy objects are not considered if this is a Find_Path() + ** call. + */ + if (!obj->Is_Techno() || ((TechnoClass *)obj)->Cloak != CLOAKED) { + + /* + ** If this unit can crush infantry, and there is an enemy infantry in the + ** cell, don't consider the cell impassible. This is true even if the unit + ** doesn't contain a legitimate weapon. + */ + if (!Class->IsCrusher || !obj->Class_Of().IsCrushable) { + + /* + ** Any non-allied blockage is considered impassible if the unit + ** is not equipped with a weapon. + */ + if (Class->Primary == WEAPON_NONE) return(MOVE_NO); + + /* + ** Some kinds of terrain are considered destroyable if the unit is equipped + ** with the weapon that can destroy it. Otherwise, the terrain is considered + ** impassable. + */ + switch (obj->What_Am_I()) { + case RTTI_TERRAIN: + if (((TerrainClass *)obj)->Class->IsFlammable && + BulletTypeClass::As_Reference(Weapons[Class->Primary].Fires).Warhead == WARHEAD_FIRE) { + + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; + } else { + return(MOVE_NO); + } + break; + + default: + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; + break; + } + } else { + crushable = true; + } + } else { + if (retval < MOVE_CLOAK) retval = MOVE_CLOAK; + } + } + } + + /* + ** Move to next object in chain. + */ + obj = obj->Next; + } + + /* + ** If some allied object has reserved the cell, then consider the cell + ** as blocked by a moving object. + */ + if (retval == MOVE_OK && !crushable && cellptr->Flag.Composite) { + + /* + ** If reserved by a vehicle, then consider this blocked terrain. + */ + if (cellptr->Flag.Occupy.Vehicle) { + retval = MOVE_MOVING_BLOCK; + } else { + if (cellptr->InfType != HOUSE_NONE && House->Is_Ally(cellptr->InfType)) { + retval = MOVE_MOVING_BLOCK; + } else { + + /* + ** Enemy infantry have reserved the cell. If this unit can crush + ** infantry, consider the cell passable. If not, then consider the + ** cell destroyable if it has a weapon. If neither case applies, then + ** this vehicle should avoid the cell altogether. + */ + if (!Class->IsCrusher) { + if (Class->Primary != WEAPON_NONE) { + retval = MOVE_DESTROYABLE; + } else { + return(MOVE_NO); + } + } + } + } + } + + /* + ** If its ok to move into the cell because we can crush whats in the cell, then + ** make sure no one else is already moving into the cell to crush something. + */ + if (retval == MOVE_OK && crushable && cellptr->Flag.Occupy.Vehicle) { + +#ifdef ADVANCED + /* + ** However, if the cell is occupied by a crushable vehicle, then we can + ** never be sure if some other friendly vehicle is also trying to crush + ** the cell at the same time. In the case of a crushable vehicle in the + ** cell, then allow entry. + */ + if (!cellptr->Cell_Unit() || !cellptr->Cell_Unit()->Class->IsCrushable) { + return(MOVE_MOVING_BLOCK); + } +#else + return(MOVE_MOVING_BLOCK); +#endif + } + + /* + ** Return with the most severe reason why this cell would be impassable. + */ + return(retval); +} + + +/*********************************************************************************************** + * UnitClass::Init -- Clears all units for scenario preparation. * + * * + * This routine will zero out the unit list and unit objects. This routine is typically * + * used in preparation for a new scenario load. All units are guaranteed to be eliminated * + * by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/15/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Init(void) +{ + UnitClass * ptr; + + Units.Free_All(); + + ptr = new UnitClass(); + VTable = ((void **)(((char *)ptr) + sizeof(AbstractClass) - 4))[0]; + delete ptr; +} + + +/*********************************************************************************************** + * UnitClass::Target_Coord -- The coordinate to use when targeting this unit. * + * * + * Sometimes the coordinate to use when targeting an object is not the same as its center * + * coordinate. This is especially true for boats since leading their movement is needed * + * in order have any chance of hitting. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to fire upon when attacking the unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +COORDINATE UnitClass::Target_Coord(void) const +{ + Validate(); +// if (*this == UNIT_GUNBOAT) { +// return(Coord_Move(Coord, PrimaryFacing.Current(), 0x0080)); +// } + return(TarComClass::Center_Coord()); +} + + +/*********************************************************************************************** + * UnitClass::Scatter -- Causes the unit to travel to a nearby safe cell. * + * * + * This routine is called when the unit discovers that it should get out of the "hot seat" * + * and move to an adjacent cell. Since the safety of the adjacent cell is not determined * + * before the move begins, it will appear that the unit is just scattering (which it * + * should). * + * * + * INPUT: threat -- The coordinate of the source of the threat. The unit will try to move * + * roughly away from the threat. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/25/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Scatter(COORDINATE threat, bool forced) +{ + Validate(); + if (*this != UNIT_GUNBOAT && *this != UNIT_HOVER) { + if ((!Target_Legal(TarCom) && !Target_Legal(NavCom)) || forced || Random_Pick(1, 4) == 1) { + FacingType toface; + FacingType newface; + CELL newcell; + + if (threat) { + toface = Dir_Facing(Direction8(threat, Coord)); + toface = toface + (Random_Pick(0, 2)-1); + } else { + toface = Dir_Facing(PrimaryFacing.Current()); + } + + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + newface = toface + face; + newcell = Adjacent_Cell(Coord_Cell(Coord), newface); + + if (Map.In_Radar(newcell) && Can_Enter_Cell(newcell) == MOVE_OK) { + Assign_Destination(::As_Target(newcell)); + } + } + } + } +} + + +/*********************************************************************************************** + * UnitClass::Stop_Driver -- Handles removing occupation bits when driving stops. * + * * + * This routine will remove the "reservation" flag (if present) when the vehicle is * + * required to stop movement. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the vehicle stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Stop_Driver(void) +{ + Validate(); + + /* + ** We only need to do something if the vehicle is actually going + ** somewhere. + */ + if (Head_To_Coord()) { + + /* + ** Safe off whether the vehicle is down or not so we know whether + ** we have to put it back down. + */ + int temp = IsDown; + + /* + ** If the vehicle is down, pick it up so it doesnt interfere with + ** our flags. + */ + if (temp) { + Mark(MARK_UP); + } + + /* + ** Call the drive class function which will let us release the + ** reserved track. + */ + Mark_Track(Head_To_Coord(), MARK_UP); + + /* + ** If it was down it should be down when we are done. + */ + if (temp) { + Mark(MARK_DOWN); + } + } + return(TarComClass::Stop_Driver()); +} + + +/*********************************************************************************************** + * UnitClass::Start_Driver -- Starts driving and reserves destination cell. * + * * + * This routine will start the vehicle moving by marking the destination cell as * + * reserved. Cells must be reserved in this fashion or else they might become occupied as * + * the vehicle is proceeding toward it. * + * * + * INPUT: headto -- The location where the vehicle will be heading. * + * * + * OUTPUT: bool; Was the vehicle started to move? Failure could be the result of the cell * + * being occupied. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Start_Driver(COORDINATE & headto) +{ + Validate(); + if (TarComClass::Start_Driver(headto)) { + Mark_Track(headto, MARK_DOWN); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Limbo -- Prepares vehicle and then limbos it. * + * * + * This routine removes the occupation bits for the vehicle and also handles cleaning up * + * any vehicle reservation bits. After this, it then proceeds with limboing the unit. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the vehicle limboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Limbo(void) +{ + Validate(); + if (!IsInLimbo) { + Stop_Driver(); + } + return(TarComClass::Limbo()); +} + + +/*********************************************************************************************** + * UnitClass::Response_Select -- Voice feedback when selecting the unit. * + * * + * This is the voice to play when the unit is selected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/30/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Response_Select(void) +{ + Validate(); + static VocType _response[] = { + VOC_VEHIC, + VOC_UNIT, + VOC_YESSIR, + VOC_YESSIR, + VOC_YESSIR, + VOC_AWAIT + }; + VocType response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + + if (*this == UNIT_TRIC || *this == UNIT_TREX || *this == UNIT_RAPT || *this == UNIT_STEG) { + response = VOC_DINOYES; + } + + if (AllowVoice) { + Sound_Effect(response, 0, -(Units.ID(this)+1)); + } +} + + +/*********************************************************************************************** + * UnitClass::Response_Move -- Voice feedback when ordering the unit to move. * + * * + * This plays the audio feedback when ordering this unit to move to a new destination. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/30/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Response_Move(void) +{ + Validate(); + static VocType _response[] = { + VOC_MOVEOUT, + VOC_MOVEOUT, + VOC_MOVEOUT, + VOC_ACKNOWL, + VOC_AFFIRM, + VOC_AFFIRM + }; + VocType response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + + if (*this == UNIT_TRIC || *this == UNIT_TREX || *this == UNIT_RAPT || *this == UNIT_STEG) { + response = VOC_DINOMOUT; + } + + if (AllowVoice) { + Sound_Effect(response, 0, -(Units.ID(this)+1)); + } +} + + +/*********************************************************************************************** + * UnitClass::Response_Attack -- Voice feedback when ordering the unit to attack a target. * + * * + * This plays the audio feedback when ordering this unit to attack. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/30/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Response_Attack(void) +{ + Validate(); + static VocType _response[] = { + VOC_AFFIRM, + VOC_ACKNOWL, + VOC_YESSIR, + VOC_YESSIR, + VOC_YESSIR + }; + VocType response = _response[Sim_Random_Pick(0, (int)(sizeof(_response) / sizeof(_response[0]))-1)]; + + if (*this == UNIT_TRIC || *this == UNIT_TREX || *this == UNIT_RAPT || *this == UNIT_STEG) { + response = VOC_DINOMOUT; + } + + if (AllowVoice) { + Sound_Effect(response, 0, -(Units.ID(this)+1)); + } +} + + +/*********************************************************************************************** + * UnitClass::What_Action -- Determines what action would occur if clicked on object. * + * * + * Use this function to determine what action would likely occur if the specified object * + * were clicked on while this unit was selected as current. This function controls, not * + * only the action to perform, but indirectly controls the cursor shape to use as well. * + * * + * INPUT: object -- The object that to check for against "this" object. * + * * + * OUTPUT: Returns with the default action to perform. If no clear action can be determined, * + * then ACTION_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/11/1995 JLB : Created. * + *=============================================================================================*/ +ActionType UnitClass::What_Action(ObjectClass * object) const +{ + Validate(); + ActionType action = TarComClass::What_Action(object); + + /* + ** If the unit doesn't have a weapon, but can crush the object, then consider + ** the object as a movable location. + */ + if (action == ACTION_ATTACK && !Can_Player_Fire()) { + if (Class->IsCrusher && object->Class_Of().IsCrushable) { + action = ACTION_MOVE; + } else { + action = ACTION_SELECT; + } + } + + /* + ** Don't allow special deploy action unless there is something to deploy. + */ + if (action == ACTION_SELF) { + if (*this != UNIT_MCV) { + if (!Class->IsTransporter || !How_Many()) { + action = ACTION_NONE; + } + } else { + ((ObjectClass &)(*this)).Mark(MARK_UP); + if (!BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(Coord_Cell(Adjacent_Cell(Center_Coord(), FACING_NW)))) { + action = ACTION_NONE; + } + ((ObjectClass &)(*this)).Mark(MARK_DOWN); + } + } + + /* + ** Special return to friendly refinery action. + */ + if (IsOwnedByPlayer && object->Is_Techno() && ((TechnoClass const *)object)->House->Is_Ally(this)) { + if (object->What_Am_I() == RTTI_BUILDING && ((UnitClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)object) == RADIO_ROGER) { + action = ACTION_ENTER; + } + } + + /* + ** Special return to friendly repair factory action. + */ + if (IsOwnedByPlayer && action == ACTION_SELECT && object->What_Am_I() == RTTI_BUILDING) { + BuildingClass * building = (BuildingClass *)object; + if (building->Class->Type == STRUCT_REPAIR && !building->In_Radio_Contact() && !building->Is_Something_Attached()) { + action = ACTION_MOVE; + } + } + + return(action); +} + + +ActionType UnitClass::What_Action(CELL cell) const +{ + Validate(); + ActionType action = TarComClass::What_Action(cell); + if (action == ACTION_MOVE && Map[cell].Land_Type() == LAND_TIBERIUM && Class->IsToHarvest) { + return(ACTION_HARVEST); + } + return(action); +} + + +/*********************************************************************************************** + * UnitClass::Can_Player_Move -- Determines if the player is legally allowed to move it. * + * * + * Use this routine to see if the player can move this object. If the player can move the * + * object, even only in theory, then this function returns true. In all other cases, such * + * as for enemy units, gunboats, or hovercraft, it returns false. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can the player give this object a movement order? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Can_Player_Move(void) const +{ + Validate(); + return(TarComClass::Can_Player_Move() && *this != UNIT_GUNBOAT && *this != UNIT_HOVER); +} + + +/*********************************************************************************************** + * UnitClass::Read_INI -- Reads units from scenario INI file. * + * * + * This routine is used to read all the starting units from the * + * scenario control INI file. The units are created and placed on the * + * map by this routine. * + * * + * INI entry format: * + * Housename, Typename, Strength, Coord, Facingnum, Missionname, Triggername * + * * + * INPUT: buffer -- Pointer to the loaded scenario INI file. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Read_INI(char *buffer) +{ + UnitClass *unit; // Working unit pointer. + char *tbuffer; // Accumulation buffer of unit IDs. + HousesType inhouse; // Unit house. + UnitType classid; // Unit class. + int len; // Length of data in buffer. + char buf[128]; + + len = strlen(buffer) + 2; + tbuffer = buffer + len; + + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-len, buffer); + while (*tbuffer != '\0') { + + WWGetPrivateProfileString(INI_Name(), tbuffer, NULL, buf, sizeof(buf)-1, buffer); + inhouse = HouseTypeClass::From_Name(strtok(buf, ",")); + if (inhouse != HOUSE_NONE) { + classid = UnitTypeClass::From_Name(strtok(NULL, ",")); + + if (classid != UNIT_NONE) { + + unit = new UnitClass(classid, inhouse); + if (unit) { + + /* + ** Read the raw data. + */ + int strength = atoi(strtok(NULL, ",\r\n")); + COORDINATE coord = Cell_Coord((CELL)atoi(strtok(NULL, ",\r\n"))); + DirType dir = (DirType)atoi(strtok(NULL, ",\r\n")); + MissionType mission = MissionClass::Mission_From_Name(strtok(NULL, ",\n\r")); + unit->Trigger = TriggerClass::As_Pointer(strtok(NULL,",\r\n")); + if (unit->Trigger) { + unit->Trigger->AttachCount++; + } + + if (unit->Unlimbo(coord, dir)) { + unit->Strength = Fixed_To_Cardinal(unit->Class->MaxStrength, strength); + if (GameToPlay == GAME_NORMAL || unit->House->IsHuman) { + unit->Assign_Mission(mission); + unit->Commence(); + } else { + unit->Enter_Idle_Mode(); + } + + /* + ** The gunboat is a special case: It must "drive" off the edge of the map. + ** Just pick the map edge that it is facing and set that as the destination + ** of the drive. + */ + if (*unit == UNIT_GUNBOAT) { + unit->PrimaryFacing.Set_Desired(DIR_W); + unit->PrimaryFacing.Set_Current(DIR_W); + unit->Assign_Mission(MISSION_HUNT); + unit->Commence(); + unit->Assign_Destination( ::As_Target(XY_Cell(Map.MapCellX-1, Cell_Y(Coord_Cell(unit->Coord))))); + } + + } else { + + /* + ** If the unit could not be unlimboed, then this is a catastrophic error + ** condition. Delete the unit. + */ + delete unit; + } + } + } + } + tbuffer += strlen(tbuffer)+1; + } +} + + +/*********************************************************************************************** + * UnitClass::Write_INI -- Writes all the units out to an INI file. * + * * + * This routine writes all of the units in the game out to an INI file. This is used * + * in the scenario editor when the game needs to be saved. * + * * + * INI entry format: * + * Housename, Typename, Strength, Coord, Facingnum, Missionname, Triggername * + * * + * INPUT: buffer -- A pointer to the loaded INI file staging area. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Write_INI(char *buffer) +{ + int index; + char uname[10]; + char buf[128]; + char *tbuffer; // Accumulation buffer of unit IDs. + + /* + ** First, clear out all existing unit data from the ini file. + */ + tbuffer = buffer + strlen(buffer) + 2; + WWGetPrivateProfileString(INI_Name(), NULL, NULL, tbuffer, ShapeBufferSize-strlen(buffer), buffer); + while (*tbuffer != '\0') { + WWWritePrivateProfileString(INI_Name(), tbuffer, NULL, buffer); + tbuffer += strlen(tbuffer)+1; + } + + /* + ** Write the unit data out. + */ + for (index = 0; index < Units.Count(); index++) { + UnitClass * unit; + + unit = Units.Ptr(index); + if (!unit->IsInLimbo && unit->IsActive) { + + sprintf(uname, "%03d", index); + sprintf(buf, "%s,%s,%d,%u,%d,%s,%s", + unit->House->Class->IniName, + unit->Class->IniName, + unit->Health_Ratio(), + Coord_Cell(unit->Coord), + unit->PrimaryFacing.Current(), + MissionClass::Mission_Name(unit->Mission), + unit->Trigger ? unit->Trigger->Get_Name() : "None" + ); + WWWritePrivateProfileString(INI_Name(), uname, buf, buffer); + } + } +} + + +/*********************************************************************************************** + * UnitClass::Exit_Repair -- Drive the unit off the repair facility. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/03/1995 BWG : Created. * + *=============================================================================================*/ +#define XYCELL(x,y) (y*MAP_CELL_W+x) +void UnitClass::Exit_Repair(void) +{ + Validate(); + int i; + CELL cell; + bool found = false; + static short const ExitRepair[] = { + XYCELL(0,-2), + XYCELL(1,-1), + XYCELL(2, 0), + XYCELL(1, 1), + XYCELL(0, 2), + XYCELL(-1,1), + XYCELL(-2,0), + XYCELL(-1,-1) + }; + + cell = Coord_Cell(Coord) + ExitRepair[Dir_Facing(PrimaryFacing.Current())]; + if (Can_Enter_Cell(cell) == MOVE_OK) found = true; + + if (!found) for (i=0; i<8; i++) { + cell = Coord_Cell(Coord) + ExitRepair[i]; + if (Can_Enter_Cell(cell) == MOVE_OK) { + found = true; + break; + } + } + if (found) { + DirType dir = Direction(cell); + + Assign_Mission(MISSION_MOVE); + Assign_Destination(::As_Target(cell)); + } +} + + +/*********************************************************************************************** + * UnitClass::Mission_Guard -- Special guard mission override processor. * + * * + * This routine will intercept the guard mission and if it is for a hovercraft, assign * + * it the unload mission instead. This prevents the hovercraft from being stuck in the * + * water if something unexpected causes it to drop into guard mode. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the time delay before this command is executed again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 05/08/1995 JLB : Fixes gunboat problems. * + *=============================================================================================*/ +int UnitClass::Mission_Guard(void) +{ + Validate(); + if (*this == UNIT_HOVER) { + if (Is_Something_Attached()) { + Assign_Mission(MISSION_UNLOAD); + Find_LZ(); + } else { + Exit_Map(); + } + return(TICKS_PER_SECOND); + } + + if (*this == UNIT_GUNBOAT) { + Assign_Mission(MISSION_HUNT); + return(TICKS_PER_SECOND); + } + + if (*this == UNIT_HARVESTER && !House->IsHuman) { + Assign_Mission(MISSION_HARVEST); + return(TICKS_PER_SECOND); + } + return(TarComClass::Mission_Guard()); +} + + +/*********************************************************************************************** + * UnitClass::Mission_Move -- Handles special move mission overrides. * + * * + * This routine intercepts the normal move mission and if a gunboat is being processed, * + * changes its mission to hunt. This is an attempt to keep the gunboat on the hunt mission * + * regardless of what the player did. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of ticks before this routine should be called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/09/1995 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Mission_Move(void) +{ + Validate(); + IsHarvesting = false; + + /* + ** Always make sure that that transport door is closed if the vehcile is moving. + */ + if (!Is_Door_Closed()) { + APC_Close_Door(); + } + + /* + ** Gunboats must always have the hunt mission. + */ + if (*this == UNIT_GUNBOAT) { + Assign_Mission(MISSION_HUNT); + return(TICKS_PER_SECOND); + } + return(TarComClass::Mission_Move()); +} + + +/*********************************************************************************************** + * UnitClass::Desired_Load_Dir -- Determines the best cell and facing for loading. * + * * + * This routine examines the unit and adjacent cells in order to find the best facing * + * for the transport and best staging cell for the potential passengers. This location is * + * modified by adjacent cell passability and direction of the potential passenger. * + * * + * INPUT: passenger -- Pointer to the potential passenger. * + * * + * moveto -- Reference to the cell number that specifies where the potential * + * passenger should move to first. * + * * + * OUTPUT: Returns with the direction the transport should face before opening the transport * + * door. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +DirType UnitClass::Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const +{ + Validate(); + /* + ** Determine the ideal facing that provides the least resistance. This would be the direction + ** of the potential passenger or the current transport facing if it is going to unload. + */ + DirType faceto; + if (passenger) { + faceto = Direction(passenger); + } else { + faceto = PrimaryFacing.Current() + DIR_S; + } + + /* + ** Sweep through the adjacent cells in order to find the best candidate. + */ + FacingType bestdir; + int bestval = -1; + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + int value = 0; + CELL cellnum = Adjacent_Cell(Coord_Cell(Coord), face); + + /* + ** Base the initial value of the potential cell according to whether the passenger is + ** allowed to enter the cell. If it can't, then give such a negative value to the + ** cell so that it is prevented from ever choosing that cell for load/unload. + */ + if (passenger) { + value = (passenger->Can_Enter_Cell(cellnum) == MOVE_OK || Coord_Cell(passenger->Coord) == cellnum) ? 128 : -128; + } else { + CellClass * cell = &Map[cellnum]; + if (Ground[cell->Land_Type()].Cost[SPEED_FOOT] == 0 || cell->Flag.Occupy.Building || cell->Flag.Occupy.Vehicle || cell->Flag.Occupy.Monolith || (cell->Flag.Composite & 0x01F) == 0x01F) { + value = -128; + } else { + if (cell->Cell_Techno() && !House->Is_Ally(cell->Cell_Techno())) { + value = -128; + } else { + value = 128; + } + } + } + + /* + ** Give more weight to the cells that require the least rotation of the transport or the + ** least roundabout movement for the potential passenger. + */ + value -= (int)ABS(Dir_Diff(Facing_Dir(face), faceto)); + if (face == FACING_S) { + value -= 100; + } + if (face == FACING_SW || face == FACING_SE) value += 64; + + /* + ** If the value for the potiential cell is greater than the last recorded potential + ** value, then record this cell as the best candidate. + */ + if (bestval == -1 || value > bestval) { + bestval = value; + bestdir = face; + } + } + + /* + ** If a suitable direction was found, then return with the direction value. + */ + moveto = 0; + if (bestval > 0) { + static DirType _desired_to_actual[FACING_COUNT] = {DIR_S, DIR_SW, DIR_NW, DIR_NW, DIR_NE, DIR_NE, DIR_NE, DIR_SE}; + + moveto = Adjacent_Cell(Coord_Cell(Coord), bestdir); + return(_desired_to_actual[bestdir]); + } + return(DIR_S); +} + + +/*********************************************************************************************** + * UnitClass::Mission_Attack -- Handles the mission attack logic. * + * * + * This routine intercepts the normal mission attack logic. If a gunboat is assigned the * + * attack mission then it must be converted back to a hunt mission. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the time before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Mission_Attack(void) +{ + Validate(); + if (*this == UNIT_GUNBOAT) { + Assign_Mission(MISSION_HUNT); + return(TICKS_PER_SECOND); + } + return(TarComClass::Mission_Attack()); +} + + +/*********************************************************************************************** + * UnitClass::Flag_Attach -- Attaches a house flag to this unit. * + * * + * This routine will attach a house flag to this unit. * + * * + * INPUT: house -- The house that is having its flag attached to it. * + * * + * OUTPUT: Was the house flag successfully attached to this unit? * + * * + * WARNINGS: A unit can only carry one flag at a time. This might be a reason for failure * + * of this routine. * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Flag_Attach(HousesType house) +{ + Validate(); + if (house != HOUSE_NONE && Flagged == HOUSE_NONE) { + Flagged = house; + Mark(MARK_CHANGE); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Flag_Remove -- Removes the house flag from this unit. * + * * + * This routine will remove the house flag that is attached to this unit. * + * * + * INPUT: none * + * * + * OUTPUT: Was the flag successfully removed? * + * * + * WARNINGS: This routine doesn't put the flag into a new location. That operation must * + * be performed or else the house flag will cease to exist. * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Flag_Remove(void) +{ + Validate(); + if (Flagged != HOUSE_NONE) { + Flagged = HOUSE_NONE; + Mark(MARK_CHANGE); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Stun -- Stuns the unit in preparation for unit removal. * + * * + * This routine intercepts the stun operation for the unit and if there is a house flag * + * attached, it will drop it to the ground. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Stun(void) +{ + Validate(); + if (Flagged != HOUSE_NONE) { + HouseClass::As_Pointer(Flagged)->Flag_Attach(Coord_Cell(Coord)); + } + TarComClass::Stun(); +} + + +/*********************************************************************************************** + * UnitClass::Pip_Count -- Fetchs the number of pips to display on unit. * + * * + * This routine is used to fetch the number of "fullness" pips to display on the unit. * + * This will either be the number of passengers or the percentage full (in 1/5ths) of * + * a harvester. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of pips to draw on this unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Pip_Count(void) const +{ + Validate(); + if (Class->IsTransporter) { + return(How_Many()); + } + if (Class->IsToHarvest) { + return(Fixed_To_Cardinal(UnitTypeClass::FULL_LOAD_CREDITS/100, Tiberium_Load())); + } + return(0); +} + + +/*********************************************************************************************** + * UnitClass::APC_Close_Door -- Closes an APC door. * + * * + * This routine will initiate closing of the APC door. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +void UnitClass::APC_Close_Door(void) +{ + Validate(); + Close_Door(10, 2); +} + + +/*********************************************************************************************** + * UnitClass::APC_Open_Door -- Opens an APC door. * + * * + * This routine will initiate opening of the APC door. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +void UnitClass::APC_Open_Door(void) +{ + Validate(); + if (!IsDriving && !IsRotating) { + if (PrimaryFacing == DIR_NW || PrimaryFacing == DIR_NE) { + Open_Door(10, 2); + } else { + Open_Door(1, 2); + } + } +} + + +/*********************************************************************************************** + * UnitClass::Remap_Table -- Fetches the remap table to use for this object. * + * * + * Use this routine to determine the rendering remap table to use for this object. The * + * remap table is normally the unit remap table, except for the MCV and the Harvestor. * + * These units use the building remap table since these units become part of the building * + * animation. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the remap table to use for this unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void const * UnitClass::Remap_Table(void) +{ + Validate(); + if (*this == UNIT_MCV || *this == UNIT_HARVESTER) { + return(House->Remap_Table(IsBlushing, false)); + } + return(TarComClass::Remap_Table()); +} + + +/*********************************************************************************************** + * UnitClass::Crew_Type -- Fetches the kind of crew that this object produces. * + * * + * When a unit is destroyed, a crew member might be generated. This routine will return * + * with the infantry type to produce for this unit. This routine will be called for every * + * survivor that is generated. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a suggested infantry type to generate as a survivor from this unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +InfantryType UnitClass::Crew_Type(void) const +{ + Validate(); + if (Class->Primary == WEAPON_NONE) { + if (Random_Pick(0, 1) == 0) { + return(INFANTRY_C1); + } else { + return(INFANTRY_C7); + } + } + return(TarComClass::Crew_Type()); +} + + +/*********************************************************************************************** + * UnitClass::What_Am_I -- Returns with the RTTI type this object is. * + * * + * This will return that this is a normal vehicle unit type. Each object class overrides * + * this function in order to provide run time type identification support. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the RTTI type that this object is (i.e., RTTI_UNIT). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +RTTIType UnitClass::What_Am_I(void) const +{ + Validate(); + return(RTTI_UNIT); +} diff --git a/UNIT.H b/UNIT.H new file mode 100644 index 0000000..91aae41 --- /dev/null +++ b/UNIT.H @@ -0,0 +1,200 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\unit.h_v 2.19 16 Oct 1995 16:45:56 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : UNIT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef UNIT_H +#define UNIT_H + +#include "tarcom.h" +#include "radio.h" +#include "cargo.h" +#include "mission.h" +#include "target.h" + + +/**************************************************************************** +** For each instance of a unit (vehicle) in the game, there is one of +** these structures. This structure holds information that is specific +** and dynamic for a particular unit. +*/ +class UnitClass : public TarComClass +{ + public: + + /* + ** This records the house flag that this object is currently carrying. + */ + HousesType Flagged; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + static void * operator new(size_t size); + static void operator delete(void *ptr); + UnitClass(void) {}; + UnitClass(UnitType classid, HousesType house); + operator UnitType(void) const {return Class->Type;}; + virtual ~UnitClass(void); + virtual RTTIType What_Am_I(void) const; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + static void Init(void); + + bool Goto_Clear_Spot(void); + bool Try_To_Deploy(void); + + bool Tiberium_Check(CELL ¢er, int x, int y); + bool Flag_Attach(HousesType house); + bool Flag_Remove(void); + void Find_LZ(void); + bool Unload_Hovercraft_Process(void); + bool Goto_Tiberium(void); + bool Harvesting(void); + void APC_Close_Door(void); + void APC_Open_Door(void); + + /* + ** Query functions. + */ + virtual bool Can_Player_Move(void) const; + virtual int Pip_Count(void) const; + virtual InfantryType Crew_Type(void) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Sort_Y(void) const; + + /* + ** Object entry and exit from the game system. + */ + virtual bool Unlimbo(COORDINATE , DirType facing=DIR_N); + virtual bool Limbo(void); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void const * Remap_Table(void); + virtual void Look(bool incremental=false); + virtual short const * Overlap_List(void) const; + virtual void Draw_It(int x, int y, WindowNumberType window); + + /* + ** User I/O. + */ + virtual ActionType What_Action(CELL cell) const; + virtual ActionType What_Action(ObjectClass * object) const; + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual void Active_Click_With(ActionType action, CELL cell); + virtual void Response_Select(void); + virtual void Response_Move(void); + virtual void Response_Attack(void); + + /* + ** Combat related. + */ + virtual COORDINATE Target_Coord(void) const; + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0); + virtual TARGET As_Target(void) const; + virtual void Stun(void); + + /* + ** Driver control support functions. These are used to control cell + ** occupation flags and driver instructions. + */ + virtual bool Stop_Driver(void); + virtual bool Start_Driver(COORDINATE & coord); + + /* + ** AI. + */ + virtual DirType Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const; + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual void AI(void); + virtual int Mission_Attack(void); + virtual int Mission_Unload(void); + virtual int Mission_Guard(void); + virtual int Mission_Harvest(void); + virtual int Mission_Hunt(void); + virtual int UnitClass::Mission_Move(void); + virtual FireErrorType Can_Fire(TARGET, int which) const; + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** Movement and animation. + */ + virtual void Enter_Idle_Mode(bool initial=false); + virtual MoveType Can_Enter_Cell(CELL cell, FacingType facing=FACING_NONE) const; + virtual void Per_Cell_Process(bool center); + virtual void Scatter(COORDINATE threat, bool forced=false); + void Exit_Repair(void); +// MoveType Blocking_Object(TechnoClass const *techno, CELL cell) const; + + /* + ** File I/O. + */ + static void Read_INI(char *buffer); + static void Write_INI(char *buffer); + static char *INI_Name(void) {return "UNITS";}; + bool Load(FileClass & file); + bool Save(FileClass & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Dee-buggin' support. + */ + int Validate(void) const; + + private: + + /* + ** This contains the value of the Virtual Function Table Pointer + */ + static void * VTable; +}; + +#endif diff --git a/UTRACKER.CPP b/UTRACKER.CPP new file mode 100644 index 0000000..f253c99 --- /dev/null +++ b/UTRACKER.CPP @@ -0,0 +1,226 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : UTRACKER.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : June 3rd, 1996 * + * * + * Last Update : June 7th, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * The UnitTracker class exists to track the various statistics * + * required for internet games. * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + * * + * Functions: * + * * + * * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + + +#include "function.h" + + +/*********************************************************************************************** + * UTC::UnitTrackerClass -- Class constructor * + * * + * * + * * + * INPUT: Number of unit types to reserve space for * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:10AM ST : Created * + *=============================================================================================*/ +UnitTrackerClass::UnitTrackerClass (int unit_count) +{ + UnitTotals = new long [unit_count]; // Allocate memory for the unit totals + UnitCount = unit_count; // Keep a record of how many unit entries there are + InNetworkFormat = 0; // The unit entries are in host format + Clear_Unit_Total(); // Clear each entry +} + + +/*********************************************************************************************** + * UTC::~UnitTrackerClass -- Class destructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:10AM ST : Created * + *=============================================================================================*/ +UnitTrackerClass::~UnitTrackerClass (void) +{ + delete UnitTotals; +} + + + +/*********************************************************************************************** + * UTC::Increment_Unit_Total -- Increment the total for the specefied unit * + * * + * * + * * + * INPUT: Unit number * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:12AM ST : Created * + *=============================================================================================*/ +void UnitTrackerClass::Increment_Unit_Total(int unit_type) +{ + UnitTotals[unit_type]++; +} + + +/*********************************************************************************************** + * UTC::Decrement_Unit_Total -- Decrement the total for the specefied unit * + * * + * * + * * + * INPUT: Unit number * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:13AM ST : Created * + *=============================================================================================*/ +void UnitTrackerClass::Decrement_Unit_Total(int unit_type) +{ + UnitTotals[unit_type]--; +} + + +/*********************************************************************************************** + * UTC::Get_All_Totals -- Returns a pointer to the start of the unit totals list * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Ptr to unit totals list * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:13AM ST : Created * + *=============================================================================================*/ +long *UnitTrackerClass::Get_All_Totals (void) +{ + return (UnitTotals); +} + + +/*********************************************************************************************** + * UTC::Clear_Unit_Total -- Clear out all the unit totals * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:14AM ST : Created * + *=============================================================================================*/ +void UnitTrackerClass::Clear_Unit_Total (void) +{ + memset (UnitTotals, 0, UnitCount * sizeof(long) ); +} + + + +/*********************************************************************************************** + * UTC::To_Network_Format -- Changes all unit totals to network format for the internet * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:15AM ST : Created * + *=============================================================================================*/ +void UnitTrackerClass::To_Network_Format (void) +{ + if (!InNetworkFormat){ + for (int i=0 ; i. +*/ + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : UTRACKER.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : June 3rd, 1996 * + * * + * Last Update : June 7th, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * The UnitTracker class exists to track the various statistics * + * required for internet games. * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +/* +** UnitTracker Class +*/ + +class UnitTrackerClass { + + public: + + UnitTrackerClass(int unit_count); + ~UnitTrackerClass(void); + + void Increment_Unit_Total (int unit_type); + void Decrement_Unit_Total (int unit_type); + void Clear_Unit_Total(void); + + int Get_Unit_Total (int unit_type); + long *Get_All_Totals (void); + int Get_Unit_Count (void){return (UnitCount);}; + + void To_Network_Format(void); + void To_PC_Format(void); + + private: + + long *UnitTotals; + int UnitCount; + int InNetworkFormat; + +}; + + + + + + + + + + + + + + + + diff --git a/VECTOR.CPP b/VECTOR.CPP new file mode 100644 index 0000000..ffdf110 --- /dev/null +++ b/VECTOR.CPP @@ -0,0 +1,898 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\vector.cpv 2.17 16 Oct 1995 16:49:26 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : VECTOR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/19/95 * + * * + * Last Update : July 18, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BooleanVectorClass::BooleanVectorClass -- Copy constructor fo boolean array. * + * BooleanVectorClass::BooleanVectorClass -- Explicit data buffer constructor. * + * BooleanVectorClass::Clear -- Resets boolean vector to empty state. * + * BooleanVectorClass::Fixup -- Updates the boolean vector to a known state. * + * BooleanVectorClass::Reset -- Clear all boolean values in array. * + * BooleanVectorClass::Resize -- Resizes a boolean vector object. * + * BooleanVectorClass::Set -- Forces all boolean elements to true. * + * BooleanVectorClass::operator = -- Assignment operator. * + * BooleanVectorClass::operator == -- Comparison operator for boolean vector. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "vector.h" +#include +#include + +/* +** The following template function can be located here ONLY if all the instatiations are +** declared in a header file this module includes. By placing the template functions here, +** it speeds up compiler operation and reduces object module size. +*/ + +/*********************************************************************************************** + * VectorClass::VectorClass -- Constructor for vector class. * + * * + * This constructor for the vector class is passed the initial size of the vector and an * + * optional pointer to a preallocated block of memory that the vector will be placed in. * + * If this optional pointer is NULL (or not provided), then the vector is allocated out * + * of free store (with the "new" operator). * + * * + * INPUT: size -- The number of elements to initialize this vector to. * + * * + * array -- Optional pointer to a previously allocated memory block to hold the * + * vector. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass::VectorClass(unsigned size, T const * array) +{ + Vector = 0; + VectorMax = size; + IsAllocated = false; + + /* + ** Allocate the vector. The default constructor will be called for every + ** object in this vector. + */ + if (size) { + if (array) { + Vector = new((void*)array) T[size]; + } else { + Vector = new T[size]; + IsAllocated = true; + } + } +} + + +/*********************************************************************************************** + * VectorClass::~VectorClass -- Default destructor for vector class. * + * * + * This is the default destructor for the vector class. It will deallocate any memory * + * that it may have allocated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass::~VectorClass(void) +{ + VectorClass::Clear(); +} + + +/*********************************************************************************************** + * VectorClass::VectorClass -- Copy constructor for vector object. * + * * + * This is the copy constructor for the vector class. It will duplicate the provided * + * vector into the new vector being created. * + * * + * INPUT: vector -- Reference to the vector to use as a copy. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass::VectorClass(VectorClass const & vector) +{ + VectorMax = 0; + IsAllocated = false; + Vector = 0; + *this = vector; +} + + +/*********************************************************************************************** + * VectorClass::operator = -- The assignment operator. * + * * + * This the the assignment operator for vector objects. It will alter the existing lvalue * + * vector to duplicate the rvalue one. * + * * + * INPUT: vector -- The rvalue vector to copy into the lvalue one. * + * * + * OUTPUT: Returns with reference to the newly copied vector. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass & VectorClass::operator =(VectorClass const & vector) +{ + Clear(); + VectorMax = vector.Length(); + if (VectorMax) { + Vector = new T[VectorMax]; + if (Vector) { + IsAllocated = true; + for (int index = 0; index < VectorMax; index++) { + Vector[index] = vector[index]; + } + } + } else { + Vector = 0; + IsAllocated = false; + } + return(*this); +} + + +/*********************************************************************************************** + * VectorClass::operator == -- Equality operator for vector objects. * + * * + * This operator compares two vectors for equality. It does this by performing an object * + * by object comparison between the two vectors. * + * * + * INPUT: vector -- The right vector expression. * + * * + * OUTPUT: bool; Are the two vectors essentially equal? (do they contain comparable elements * + * in the same order?) * + * * + * WARNINGS: The equality operator must exist for the objects that this vector contains. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int VectorClass::operator == (VectorClass const & vector) const +{ + if (VectorMax == vector.Length()) { + for (int index = 0; index < VectorMax; index++) { + if (Vector[index] != vector[index]) { + return(false); + } + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * VectorClass::ID -- Pointer based conversion to index number. * + * * + * Use this routine to convert a pointer to an element in the vector back into the index * + * number of that object. This routine ONLY works with actual pointers to object within * + * the vector. For "equivalent" object index number (such as with similar integral values) * + * then use the "by value" index number ID function. * + * * + * INPUT: pointer -- Pointer to an actual object in the vector. * + * * + * OUTPUT: Returns with the index number for the object pointed to by the parameter. * + * * + * WARNINGS: This routine is only valid for actual pointers to object that exist within * + * the vector. All other object pointers will yield undefined results. * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +template +inline int VectorClass::ID(T const * ptr) +{ + return(((unsigned long)ptr - (unsigned long)&(*this)[0]) / sizeof(T)); +} + + +/*********************************************************************************************** + * VectorClass::ID -- Finds object ID based on value. * + * * + * Use this routine to find the index value of an object with equivalent value in the * + * vector. Typical use of this would be for integral types. * + * * + * INPUT: object -- Reference to the object that is to be looked up in the vector. * + * * + * OUTPUT: Returns with the index number of the object that is equivalent to the one * + * specified. If no matching value could be found then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +template +int VectorClass::ID(T const & object) +{ + for (int index = 0; index < VectorMax; index++) { + if ((*this)[index] == object) { + return(index); + } + } + return(-1); +} + + +/*********************************************************************************************** + * VectorClass::Clear -- Frees and clears the vector. * + * * + * Use this routine to reset the vector to an empty (non-allocated) state. A vector will * + * free all allocated memory when this routine is called. In order for the vector to be * + * useful after this point, the Resize function must be called to give it element space. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +void VectorClass::Clear(void) +{ + if (Vector && IsAllocated) { + delete[] Vector; + Vector = 0; + } + IsAllocated = false; + VectorMax = 0; +} + + +/*********************************************************************************************** + * VectorClass::Resize -- Changes the size of the vector. * + * * + * This routine is used to change the size (usually to increase) the size of a vector. This * + * is the only way to increase the vector's working room (number of elements). * + * * + * INPUT: newsize -- The desired size of the vector. * + * * + * array -- Optional pointer to a previously allocated memory block that the * + * array will be located in. If this parameter is not supplied, then * + * the array will be allocated from free store. * + * * + * OUTPUT: bool; Was the array resized successfully? * + * * + * WARNINGS: Failure to succeed could be the result of running out of memory. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int VectorClass::Resize(unsigned newsize, T const * array) +{ + if (newsize) { + + /* + ** Allocate a new vector of the size specified. The default constructor + ** will be called for every object in this vector. + */ + T * newptr; + if (!array) { + newptr = new T[newsize]; + } else { + newptr = new((void*)array) T[newsize]; + } + if (!newptr) { + return(false); + } + + /* + ** If there is an old vector, then it must be copied (as much as is feasable) + ** to the new vector. + */ + if (Vector) { + + /* + ** Copy as much of the old vector into the new vector as possible. This + ** presumes that there is a functional assignment operator for each + ** of the objects in the vector. + */ + int copycount = (newsize < VectorMax) ? newsize : VectorMax; + for (int index = 0; index < copycount; index++) { + newptr[index] = Vector[index]; + } + + /* + ** Delete the old vector. This might cause the destructors to be called + ** for all of the old elements. This makes the implementation of suitable + ** assignment operator very important. The default assigment operator will + ** only work for the simplist of objects. + */ + if (IsAllocated) { + delete[] Vector; + Vector = 0; + } + } + + /* + ** Assign the new vector data to this class. + */ + Vector = newptr; + VectorMax = newsize; + IsAllocated = (Vector && !array); + + } else { + + /* + ** Resizing to zero is the same as clearing the vector. + */ + Clear(); + } + return(true); +} + + +/*********************************************************************************************** + * DynamicVectorClass::DynamicVectorClass -- Constructor for dynamic vector. * + * * + * This is the normal constructor for the dynamic vector class. It is similar to the normal * + * vector class constructor. The vector is initialized to contain the number of elements * + * specified in the "size" parameter. The memory is allocated from free store unless the * + * optional array parameter is provided. In this case it will place the vector at the * + * memory location specified. * + * * + * INPUT: size -- The maximum number of objects allowed in this vector. * + * * + * array -- Optional pointer to the memory area to place the vector at. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +DynamicVectorClass::DynamicVectorClass(unsigned size, T const * array) + : VectorClass(size, array) +{ + GrowthStep = 10; + ActiveCount = 0; +} + + +/*********************************************************************************************** + * DynamicVectorClass::Resize -- Changes the size of a dynamic vector. * + * * + * Use this routine to change the size of the vector. The size changed is the maximum * + * number of allocated objects within this vector. If a memory buffer is provided, then * + * the vector will be located there. Otherwise, the memory will be allocated out of free * + * store. * + * * + * INPUT: newsize -- The desired maximum size of this vector. * + * * + * array -- Optional pointer to a previosly allocated memory array. * + * * + * OUTPUT: bool; Was vector successfully resized according to specifications? * + * * + * WARNINGS: Failure to resize the vector could be the result of lack of free store. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Resize(unsigned newsize, T const * array) +{ + if (VectorClass::Resize(newsize, array)) { + if (Length() < ActiveCount) ActiveCount = Length(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * DynamicVectorClass::ID -- Find matching value in the dynamic vector. * + * * + * Use this routine to find a matching object (by value) in the vector. Unlike the base * + * class ID function of similar name, this one restricts the scan to the current number * + * of valid objects. * + * * + * INPUT: object -- A reference to the object that a match is to be found in the * + * vector. * + * * + * OUTPUT: Returns with the index number of the object that is equivalent to the one * + * specified. If no equivalent object could be found then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::ID(T const & object) +{ + for (int index = 0; index < Count(); index++) { + if ((*this)[index] == object) return(index); + } + return(-1); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Add -- Add an element to the vector. * + * * + * Use this routine to add an element to the vector. The vector will automatically be * + * resized to accomodate the new element IF the vector was allocated previosly and the * + * growth rate is not zero. * + * * + * INPUT: object -- Reference to the object that will be added to the vector. * + * * + * OUTPUT: bool; Was the object added successfully? If so, the object is added to the end * + * of the vector. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Add(T const & object) +{ + if (ActiveCount >= Length()) { + if ((IsAllocated || !VectorMax) && GrowthStep > 0) { + if (!Resize(Length() + GrowthStep)) { + + /* + ** Failure to increase the size of the vector is an error condition. + ** Return with the error flag. + */ + return(false); + } + } else { + + /* + ** Increasing the size of this vector is not allowed! Bail this + ** routine with the error code. + */ + return(false); + } + } + + /* + ** There is room for the new object now. Add it to the end of the object vector. + */ + (*this)[ActiveCount++] = object; + return(true); +} + + +template +int DynamicVectorClass::Add_Head(T const & object) +{ + if (ActiveCount >= Length()) { + if ((IsAllocated || !VectorMax) && GrowthStep > 0) { + if (!Resize(Length() + GrowthStep)) { + + /* + ** Failure to increase the size of the vector is an error condition. + ** Return with the error flag. + */ + return(false); + } + } else { + + /* + ** Increasing the size of this vector is not allowed! Bail this + ** routine with the error code. + */ + return(false); + } + } + + /* + ** There is room for the new object now. Add it to the end of the object vector. + */ + if (ActiveCount) { + memmove(&(*this)[1], &(*this)[0], ActiveCount * sizeof(T)); + } + (*this)[0] = object; + ActiveCount++; +// (*this)[ActiveCount++] = object; + return(true); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Delete -- Remove the specified object from the vector. * + * * + * This routine will delete the object referenced from the vector. All objects in the * + * vector that follow the one deleted will be moved "down" to fill the hole. * + * * + * INPUT: object -- Reference to the object in this vector that is to be deleted. * + * * + * OUTPUT: bool; Was the object deleted successfully? This should always be true. * + * * + * WARNINGS: Do no pass a reference to an object that is NOT part of this vector. The * + * results of this are undefined and probably catastrophic. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Delete(T const & object) +{ + int index = ID(object); + if (index != -1){ + return(Delete(index)); + }else{ + return (false); + } +} + + +/*********************************************************************************************** + * DynamicVectorClass::Delete -- Deletes the specified index from the vector. * + * * + * Use this routine to delete the object at the specified index from the objects in the * + * vector. This routine will move all the remaining objects "down" in order to fill the * + * hole. * + * * + * INPUT: index -- The index number of the object in the vector that is to be deleted. * + * * + * OUTPUT: bool; Was the object index deleted successfully? Failure might mean that the index * + * specified was out of bounds. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Delete(int index) +{ + if ((unsigned)index < ActiveCount) { + ActiveCount--; + + /* + ** If there are any objects past the index that was deleted, copy those + ** objects down in order to fill the hole. A simple memory copy is + ** not sufficient since the vector could contain class objects that + ** need to use the assignment operator for movement. + */ + for (int i = index; i < ActiveCount; i++) { + (*this)[i] = (*this)[i+1]; + } + return(true); + } + return(false); +} + +//---------------------------------------------------------------------------------------------- + +/*********************************************************************************************** + * BooleanVectorClass::BooleanVectorClass -- Explicit data buffer constructor. * + * * + * This is the constructor for a boolean array. This constructor takes the memory pointer * + * provided as assigns that as the array data pointer. * + * * + * INPUT: size -- The size of the array (in bits). * + * * + * array -- Pointer to the memory that the array is to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: You must make sure that the memory specified is large enough to contain the * + * bits specified. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +BooleanVectorClass::BooleanVectorClass(unsigned size, unsigned char * array) +{ + BitArray.Resize(((size + (8-1)) / 8), array); + LastIndex = -1; + BitCount = size; +} + + +/*********************************************************************************************** + * BooleanVectorClass::BooleanVectorClass -- Copy constructor fo boolean array. * + * * + * This is the copy constructor for a boolean array. It is used to make a duplicate of the * + * boolean array. * + * * + * INPUT: vector -- Reference to the vector to be duplicated. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +BooleanVectorClass::BooleanVectorClass(BooleanVectorClass const & vector) +{ + LastIndex = -1; + *this = vector; +} + + +/*********************************************************************************************** + * BooleanVectorClass::operator = -- Assignment operator. * + * * + * This routine will make a copy of the specifed boolean vector array. The vector is * + * copied into an already constructed existing vector. The values from the existing vector * + * are destroyed by this copy. * + * * + * INPUT: vector -- Reference to the vector to make a copy of. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +BooleanVectorClass & BooleanVectorClass::operator =(BooleanVectorClass const & vector) +{ + Fixup(); + Copy = vector.Copy; + LastIndex = vector.LastIndex; + BitArray = vector.BitArray; + BitCount = vector.BitCount; + return(*this); +} + + +/*********************************************************************************************** + * BooleanVectorClass::operator == -- Comparison operator for boolean vector. * + * * + * This is the comparison operator for a boolean vector class. Boolean vectors are equal * + * if the bit count and bit values are identical. * + * * + * INPUT: vector -- Reference to the vector to compare to. * + * * + * OUTPUT: Are the boolean vectors identical? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +int BooleanVectorClass::operator == (const BooleanVectorClass & vector) +{ + Fixup(LastIndex); + return(BitCount == vector.BitCount && BitArray == vector.BitArray); +} + + +/*********************************************************************************************** + * BooleanVectorClass::Resize -- Resizes a boolean vector object. * + * * + * This routine will resize the boolean vector object. An index value used with a boolean * + * vector must be less than the value specified in as the new size. * + * * + * INPUT: size -- The new maximum size of this boolean vector. * + * * + * OUTPUT: Was the boolean vector sized successfully? * + * * + * WARNINGS: The boolean array might be reallocated or even deleted by this routine. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +int BooleanVectorClass::Resize(unsigned size) +{ + Fixup(); + + if (size) { + + /* + ** Record the previous bit count of the boolean vector. This is used + ** to determine if the array has grown in size and thus clearing is + ** necessary. + */ + int oldsize = BitCount; + + /* + ** Actually resize the bit array. Since this is a bit packed array, + ** there are 8 elements per byte (rounded up). + */ + int success = BitArray.Resize(((size + (8-1)) / 8)); + + /* + ** Since there is no default constructor for bit packed integers, a manual + ** clearing of the bits is required. + */ + BitCount = size; + if (success && oldsize < size) { + for (int index = oldsize; index < size; index++) { + (*this)[index] = 0; + } + } + + return(success); + } + + /* + ** Resizing to zero is the same as clearing and deallocating the array. + ** This is always successful. + */ + Clear(); + return(true); +} + + +/*********************************************************************************************** + * BooleanVectorClass::Clear -- Resets boolean vector to empty state. * + * * + * This routine will clear out the boolean array. This will free any allocated memory and * + * result in the boolean vector being unusable until the Resize function is subsiquently * + * called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The boolean vector cannot be used until it is resized to a non null condition. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +void BooleanVectorClass::Clear(void) +{ + Fixup(); + BitCount = 0; + BitArray.Clear(); +} + + +/*********************************************************************************************** + * BooleanVectorClass::Reset -- Clear all boolean values in array. * + * * + * This is the preferred (and quick) method to clear the boolean array to a false condition.* + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +void BooleanVectorClass::Reset(void) +{ + LastIndex = -1; + if (BitArray.Length()) { + memset(&BitArray[0], '\0', BitArray.Length()); + } +} + + +/*********************************************************************************************** + * BooleanVectorClass::Set -- Forces all boolean elements to true. * + * * + * This is the preferred (and fast) way to set all boolean elements to true. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +void BooleanVectorClass::Set(void) +{ + LastIndex = -1; + if (BitArray.Length()) { + memset(&BitArray[0], '\xFF', BitArray.Length()); + } +} + + +/*********************************************************************************************** + * BooleanVectorClass::Fixup -- Updates the boolean vector to a known state. * + * * + * Use this routine to set the boolean value copy to match the appropriate bit in the * + * boolean array. The boolean array will be updated with any changes from the last time * + * a value was fetched from the boolean vector. By using this update method, the boolean * + * array can be treated as a normal array even though the elements are composed of * + * otherwise inaccessable bits. * + * * + * INPUT: index -- The index to set the new copy value to. If the index is -1, then the * + * previous value will be updated into the vector array, but no new value * + * will be fetched from it. * + * * + * OUTPUT: none * + * * + * WARNINGS: Always call this routine with "-1" if any direct manipulation of the bit * + * array is to occur. This ensures that the bit array is accurate. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +void BooleanVectorClass::Fixup(int index) const +{ + /* + ** If the requested index value is illegal, then force the index + ** to be -1. This is the default non-index value. + */ + if ((unsigned)index >= BitCount) { + index = -1; + } + + /* + ** If the new index is different than the previous index, there might + ** be some fixing up required. + */ + if (index != LastIndex) { + + /* + ** If the previously fetched boolean value was changed, then update + ** the boolean array accordingly. + */ + if (LastIndex != -1) { + Set_Bit((void*)&BitArray[0], LastIndex, Copy); + } + + /* + ** If this new current index is valid, then fill in the reference boolean + ** value with the approriate data from the bit array. + */ + if (index != -1) { + ((unsigned char&)Copy) = Get_Bit(&BitArray[0], index); + } + + ((int &)LastIndex) = index; + } +} + + diff --git a/VECTOR.H b/VECTOR.H new file mode 100644 index 0000000..541abe8 --- /dev/null +++ b/VECTOR.H @@ -0,0 +1,296 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\vector.h_v 2.15 16 Oct 1995 16:47:38 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : VECTOR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/19/95 * + * * + * Last Update : March 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * VectorClass::VectorClass -- Constructor for vector class. * + * VectorClass::~VectorClass -- Default destructor for vector class. * + * VectorClass::VectorClass -- Copy constructor for vector object. * + * VectorClass::operator = -- The assignment operator. * + * VectorClass::operator == -- Equality operator for vector objects. * + * VectorClass::Clear -- Frees and clears the vector. * + * VectorClass::Resize -- Changes the size of the vector. * + * DynamicVectorClass::DynamicVectorClass -- Constructor for dynamic vector. * + * DynamicVectorClass::Resize -- Changes the size of a dynamic vector. * + * DynamicVectorClass::Add -- Add an element to the vector. * + * DynamicVectorClass::Delete -- Remove the specified object from the vector. * + * DynamicVectorClass::Delete -- Deletes the specified index from the vector. * + * VectorClass::ID -- Pointer based conversion to index number. * + * VectorClass::ID -- Finds object ID based on value. * + * DynamicVectorClass::ID -- Find matching value in the dynamic vector. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef VECTOR_H +#define VECTOR_H + +#ifndef false +#define false 0 +#endif +#ifndef true +#define true 1 +#endif + +#include +#include + +inline void * operator new(size_t , void * pointer) {return(pointer);} +inline void * operator new[](size_t , void * pointer) {return(pointer);} + + +/************************************************************************** +** This is a general purpose vector class. A vector is defined by this +** class, as an array of arbitrary objects where the array can be dynamically +** sized. Because is deals with arbitrary object types, it can handle everything. +** As a result of this, it is not terribly efficient for integral objects (such +** as char or int). It will function correctly, but the copy constructor and +** equality operator could be highly optimized if the integral type were known. +** This efficiency can be implemented by deriving an integral vector template +** from this one in order to supply more efficient routines. +*/ +template +class VectorClass +{ + public: + VectorClass(unsigned size=0, T const * array=0); + VectorClass(VectorClass const &); // Copy constructor. + virtual ~VectorClass(void); + + T & operator[](unsigned index) {return(Vector[index]);}; + T const & operator[](unsigned index) const {return(Vector[index]);}; + virtual VectorClass & operator =(VectorClass const &); // Assignment operator. + virtual int operator == (VectorClass const &) const; // Equality operator. + virtual int Resize(unsigned newsize, T const * array=0); + virtual void Clear(void); + unsigned Length(void) const {return VectorMax;}; + virtual int ID(T const * ptr); // Pointer based identification. + virtual int ID(T const & ptr); // Value based identification. + + protected: + + /* + ** This is a pointer to the allocated vector array of elements. + */ + T * Vector; + + /* + ** This is the maximum number of elements allowed in this vector. + */ + unsigned VectorMax; + + /* + ** Does the vector data pointer refer to memory that this class has manually + ** allocated? If so, then this class is responsible for deleting it. + */ + unsigned IsAllocated:1; +}; + + +/************************************************************************** +** This derivative vector class adds the concept of adding and deleting +** objects. The objects are packed to the beginning of the vector array. +** If this is instantiated for a class object, then the assignment operator +** and the equality operator must be supported. If the vector allocates its +** own memory, then the vector can grow if it runs out of room adding items. +** The growth rate is controlled by setting the growth step rate. A growth +** step rate of zero disallows growing. +*/ +template +class DynamicVectorClass : public VectorClass +{ + public: + DynamicVectorClass(unsigned size=0, T const * array=0); + + // Change maximum size of vector. + virtual int Resize(unsigned newsize, T const * array=0); + + // Resets and frees the vector array. + virtual void Clear(void) {ActiveCount = 0;VectorClass::Clear();}; + + // Fetch number of "allocated" vector objects. + int Count(void) const {return(ActiveCount);}; + + // Add object to vector (growing as necessary). + int Add(T const & object); + int Add_Head(T const & object); + + // Delete object just like this from vector. + int Delete(T const & object); + + // Delete object at this vector index. + int Delete(int index); + + // Deletes all objects in the vector. + void Delete_All(void) {ActiveCount = 0;}; + + // Set amount that vector grows by. + int Set_Growth_Step(int step) {return(GrowthStep = step);}; + + // Fetch current growth step rate. + int Growth_Step(void) {return GrowthStep;}; + + virtual int ID(T const * ptr) {return(VectorClass::ID(ptr));}; + virtual int ID(T const & ptr); + + protected: + + /* + ** This is a count of the number of active objects in this + ** vector. The memory array often times is bigger than this + ** value. + */ + int ActiveCount; + + /* + ** If there is insufficient room in the vector array for a new + ** object to be added, then the vector will grow by the number + ** of objects specified by this value. This is controlled by + ** the Set_Growth_Step() function. + */ + int GrowthStep; +}; + + +/************************************************************************** +** This is a derivative of a vector class that supports boolean flags. Since +** a boolean flag can be represented by a single bit, this class packs the +** array of boolean flags into an array of bytes containing 8 boolean values +** each. For large boolean arrays, this results in an 87.5% savings. Although +** the indexing "[]" operator is supported, DO NOT pass pointers to sub elements +** of this bit vector class. A pointer derived from the indexing operator is +** only valid until the next call. Because of this, only simple +** direct use of the "[]" operator is allowed. +*/ +class BooleanVectorClass +{ + public: + BooleanVectorClass(unsigned size=0, unsigned char * array=0); + BooleanVectorClass(BooleanVectorClass const & vector); + + // Assignment operator. + BooleanVectorClass & operator =(BooleanVectorClass const & vector); + + // Equivalency operator. + int operator == (BooleanVectorClass const & vector); + + // Fetch number of boolean objects in vector. + int Length(void) {return BitCount;}; + + // Set all boolean values to false; + void Reset(void); + + // Set all boolean values to true. + void Set(void); + + // Resets vector to zero length (frees memory). + void Clear(void); + + // Change size of this boolean vector. + int Resize(unsigned size); + + // Fetch reference to specified index. + bool const & operator[](int index) const { + if (LastIndex != index) Fixup(index); + return(Copy); + }; + bool & operator[](int index) { + if (LastIndex != index) Fixup(index); + return(Copy); + }; + + // Quick check on boolean state. + bool Is_True(int index) const { + if (index == LastIndex) return(Copy); + return(Get_Bit(&BitArray[0], index)); + }; + + // Find first index that is false. + int First_False(void) const { + if (LastIndex != -1) Fixup(-1); + + int retval = First_False_Bit(&BitArray[0]); + if (retval < BitCount) return(retval); + + /* + ** Failure to find a false boolean value in the vector. Return this + ** fact in the form of an invalid index number. + */ + return(-1); + } + + // Find first index that is true. + int First_True(void) const { + if (LastIndex != -1) Fixup(-1); + + int retval = First_True_Bit(&BitArray[0]); + if (retval < BitCount) return(retval); + + /* + ** Failure to find a true boolean value in the vector. Return this + ** fact in the form of an invalid index number. + */ + return(-1); + } + + private: + void Fixup(int index=-1) const; + + /* + ** This is the number of boolean values in the vector. This value is + ** not necessarily a multiple of 8, even though the underlying character + ** vector contains a multiple of 8 bits. + */ + int BitCount; + + /* + ** This is a referential copy of an element in the bit vector. The + ** purpose of this copy is to allow normal reference access to this + ** object (for speed reasons). This hides the bit packing scheme from + ** the user of this class. + */ + bool Copy; + + /* + ** This records the index of the value last fetched into the reference + ** boolean variable. This index is used to properly restore the value + ** when the reference copy needs updating. + */ + int LastIndex; + + /* + ** This points to the allocated bitfield array. + */ + VectorClass BitArray; +}; + + +#endif diff --git a/VISUDLG.CPP b/VISUDLG.CPP new file mode 100644 index 0000000..f9ee4fc --- /dev/null +++ b/VISUDLG.CPP @@ -0,0 +1,424 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\visudlg.cpv 2.17 16 Oct 1995 16:51:40 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : VISUDLG.CPP * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : June 18, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * VisualControlsClass::Process -- Process the visual control dialog box. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "visudlg.h" +int VisualControlsClass::Init(void) +{ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + Option_Width = 216 * factor; + Option_Height = 122 * factor; + Option_X = (((SeenBuff.Get_Width() - Option_Width) / 2)); + Option_Y = ((SeenBuff.Get_Height() - Option_Height) / 2); + Text_X = Option_X + (28 * factor); + Text_Y = Option_Y + (30 * factor); + Slider_X = Option_X + (105 * factor); + Slider_Y = Option_Y + (30 * factor); + Slider_Width = 70 * factor; + Slider_Height = 5 * factor; + Slider_Y_Spacing = 11 * factor; + Button_X = Option_X + (63 * factor); + Button_Y = Option_Y + (102 * factor); + return(factor); +} +/*********************************************************************************************** + * VisualControlsClass::Process -- Process the visual control dialog box. * + * * + * This routine displays and processes the visual controls dialog box. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1995 JLB : Created. * + *=============================================================================================*/ +void VisualControlsClass::Process(void) +{ + static int _titles[4] = { + TXT_BRIGHTNESS, + TXT_COLOR, + TXT_CONTRAST, + TXT_TINT + }; + + enum { + NUM_OF_BUTTONS = 6, + }; + + /* + ** Variables. + */ + int selection; + int factor; + bool pressed; + int curbutton; + TextButtonClass *buttons[NUM_OF_BUTTONS]; + SliderClass *buttonsliders[NUM_OF_BUTTONS]; + + factor = Init(); + Set_Logic_Page(SeenBuff); + + /* + ** Create Buttons. Button coords are in pixels, but are window-relative. + */ + TextButtonClass optionsbtn( + BUTTON_OPTIONS, + TXT_GAME_CONTROLS, + TPF_6PT_GRAD | TPF_NOSHADOW, + 0, + Button_Y ); + + TextButtonClass resetbtn( + BUTTON_RESET, + TXT_RESET_MENU, + TPF_6PT_GRAD | TPF_NOSHADOW, + 0, + Button_Y); + + /* + ** Centers options button. + */ + optionsbtn.X = Option_X + (Option_Width - optionsbtn.Width - (15 * factor)); + resetbtn.X = Option_X + (15 *factor); + + resetbtn.Add_Tail(optionsbtn); + + /* + ** Brightness (value) control. + */ + SliderClass brightness(BUTTON_BRIGHTNESS, Slider_X, Slider_Y + (Slider_Y_Spacing*0), Slider_Width, Slider_Height); + brightness.Set_Thumb_Size(20); + brightness.Set_Value(Options.Get_Brightness()); + brightness.Add_Tail(optionsbtn); + + /* + ** Color (saturation) control. + */ + SliderClass color(BUTTON_COLOR, Slider_X, Slider_Y + (Slider_Y_Spacing*1), Slider_Width, Slider_Height); + color.Set_Thumb_Size(20); + color.Set_Value(Options.Get_Color()); + color.Add_Tail(optionsbtn); + + /* + ** Contrast control. + */ + SliderClass contrast(BUTTON_CONTRAST, Slider_X, Slider_Y + (Slider_Y_Spacing*2), Slider_Width, Slider_Height); + contrast.Set_Thumb_Size(20); + contrast.Set_Value(Options.Get_Contrast()); + contrast.Add_Tail(optionsbtn); + + /* + ** Tint (hue) control. + */ + SliderClass tint(BUTTON_TINT, Slider_X, Slider_Y + (Slider_Y_Spacing*3), Slider_Width, Slider_Height); + tint.Set_Thumb_Size(20); + tint.Set_Value(Options.Get_Tint()); + tint.Add_Tail(optionsbtn); + + /* + ** This causes left mouse button clicking within the confines of the dialog to + ** be ignored if it wasn't recognized by any other button or slider. + */ + GadgetClass dialog(Option_X, Option_Y, Option_Width, Option_Height, GadgetClass::LEFTPRESS); + dialog.Add_Tail(optionsbtn); + + /* + ** This causes a right click anywhere or a left click outside the dialog region + ** to be equivalent to clicking on the return to options dialog. + */ + ControlClass background(BUTTON_OPTIONS, 0, 0, SeenBuff.Get_Width(), SeenBuff.Get_Height(), GadgetClass::LEFTPRESS|GadgetClass::RIGHTPRESS); + background.Add_Tail(optionsbtn); + + curbutton = 0; + buttons[0] = NULL; + buttons[1] = NULL; + buttons[2] = NULL; + buttons[3] = NULL; + buttons[4] = &resetbtn; + buttons[5] = &optionsbtn; + + buttonsliders[0] = &brightness; + buttonsliders[1] = &color; + buttonsliders[2] = &contrast; + buttonsliders[3] = ∭ + buttonsliders[4] = NULL; + buttonsliders[5] = NULL; + + /* + ** Main Processing Loop. + */ + bool display = true; + bool process = true; + bool partial = true; + pressed = false; + while (process) { + + /* + ** Invoke game callback. + */ + if (GameToPlay == GAME_NORMAL) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + } + } + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=TRUE; + } + + /* + ** Refresh display if needed. + */ + if (display) { + Hide_Mouse(); + Dialog_Box(Option_X, Option_Y, Option_Width, Option_Height); + Draw_Caption(TXT_VISUAL_CONTROLS, Option_X, Option_Y, Option_Width); + Show_Mouse(); + display = false; + partial = true; + } + + /* + ** If just the buttons and captions need to be redrawn, then do so now. + */ + if (partial) { + Hide_Mouse(); + + /* + ** Draw the titles. + */ + for (int i = 0; i < (sizeof(_titles)/sizeof(_titles[0])); i++) { + Fancy_Text_Print(_titles[i], Slider_X-8, Text_Y + (i*Slider_Y_Spacing), + CC_GREEN, TBLACK, TPF_6PT_GRAD|TPF_RIGHT|TPF_NOSHADOW| ((curbutton == i) ? TPF_BRIGHT_COLOR : TPF_USE_GRAD_PAL)); + } + optionsbtn.Draw_All(); + Show_Mouse(); + partial = false; + } + + /* + ** Get and process player input. + */ + KeyNumType input = optionsbtn.Input(); + switch (input) { + case (BUTTON_BRIGHTNESS | KN_BUTTON): + Options.Set_Brightness(brightness.Get_Value()); + break; + + case (BUTTON_COLOR | KN_BUTTON): + Options.Set_Color(color.Get_Value()); + break; + + case (BUTTON_CONTRAST | KN_BUTTON): + Options.Set_Contrast(contrast.Get_Value()); + break; + + case (BUTTON_TINT | KN_BUTTON): + Options.Set_Tint(tint.Get_Value()); + break; + + case (BUTTON_RESET | KN_BUTTON): + selection = BUTTON_RESET; + pressed = true; + break; + + case KN_ESC: + case BUTTON_OPTIONS|KN_BUTTON: + selection = BUTTON_OPTIONS; + pressed = true; + break; + + case (KN_LEFT): + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS)) { + buttonsliders[curbutton]->Bump(1); + switch (curbutton) { + case (BUTTON_BRIGHTNESS - BUTTON_BRIGHTNESS): + Options.Set_Brightness(brightness.Get_Value()); + break; + + case (BUTTON_COLOR - BUTTON_BRIGHTNESS): + Options.Set_Color(color.Get_Value()); + break; + + case (BUTTON_CONTRAST - BUTTON_BRIGHTNESS): + Options.Set_Contrast(contrast.Get_Value()); + break; + + case (BUTTON_TINT - BUTTON_BRIGHTNESS): + Options.Set_Tint(tint.Get_Value()); + break; + } + } else { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton--; + if (curbutton < (BUTTON_RESET - BUTTON_BRIGHTNESS) ) { + curbutton = (BUTTON_OPTIONS - BUTTON_BRIGHTNESS); + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RIGHT): + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS)) { + buttonsliders[curbutton]->Bump(0); + switch (curbutton) { + case (BUTTON_BRIGHTNESS - BUTTON_BRIGHTNESS): + Options.Set_Brightness(brightness.Get_Value()); + break; + + case (BUTTON_COLOR - BUTTON_BRIGHTNESS): + Options.Set_Color(color.Get_Value()); + break; + + case (BUTTON_CONTRAST - BUTTON_BRIGHTNESS): + Options.Set_Contrast(contrast.Get_Value()); + break; + + case (BUTTON_TINT - BUTTON_BRIGHTNESS): + Options.Set_Tint(tint.Get_Value()); + break; + } + } else { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton++; + if (curbutton > (BUTTON_OPTIONS - BUTTON_BRIGHTNESS) ) { + curbutton = (BUTTON_RESET - BUTTON_BRIGHTNESS); + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_UP): + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS) ) { + partial = true; + } else { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + } + + curbutton--; + if (curbutton == (BUTTON_RESET - BUTTON_BRIGHTNESS) ) { + curbutton--; + } + + if (curbutton < 0) { + curbutton = (BUTTON_RESET - BUTTON_BRIGHTNESS); + } + + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS) ) { + partial = true; + } else { + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_DOWN): + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS) ) { + partial = true; + } else { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + } + + curbutton++; + if (curbutton > (BUTTON_RESET - BUTTON_BRIGHTNESS) ) { + curbutton = 0; + } + + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS) ) { + partial = true; + } else { + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_BRIGHTNESS; + pressed = true; + break; + + default: + break; + } + + + if (pressed) { + switch (selection) { + case (BUTTON_RESET): + brightness.Set_Value(128); + contrast.Set_Value(128); + color.Set_Value(128); + tint.Set_Value(128); + + Options.Set_Brightness(128); + Options.Set_Contrast(128); + Options.Set_Color(128); + Options.Set_Tint(128); + break; + + case (BUTTON_OPTIONS): + process = false; + break; + } + + pressed = false; + } + } +} + diff --git a/VISUDLG.H b/VISUDLG.H new file mode 100644 index 0000000..ef125da --- /dev/null +++ b/VISUDLG.H @@ -0,0 +1,92 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\visudlg.h_v 2.15 16 Oct 1995 16:47:46 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : VISUDLG.H * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : Jan 18, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + *---------------------------------------------------------------------------------------------*/ + +#ifndef VISUDLG_H +#define VISUDLG_H + +#include "gadget.h" + +class VisualControlsClass +{ + private: + + enum VisualControlEnums { + BUTTON_BRIGHTNESS=1, + BUTTON_COLOR, + BUTTON_CONTRAST, + BUTTON_TINT, + BUTTON_RESET, + BUTTON_OPTIONS, // Button number for "Options menu" + OPTION_WIDTH=216, // Width of dialog box. + OPTION_HEIGHT=122, // Height of dialog box. + OPTION_X=(((320 - OPTION_WIDTH) / 2)), + OPTION_Y=((200 - OPTION_HEIGHT) / 2), + TEXT_X=OPTION_X+28, // Title's x pos + TEXT_Y=OPTION_Y+30, // Add 11 for each following line + SLIDER_X=OPTION_X+105, // Slider's x pos + SLIDER_Y=OPTION_Y+30, // Add 11 for each following line + SLIDER_WIDTH=70, // Width of each control slider. + SLIDER_HEIGHT=5, // Height of each control slider. + SLIDER_Y_SPACING=11, // Vertical spacing between sliders. + BUTTON_X=OPTION_X+63, // Options button x pos + BUTTON_Y=OPTION_Y+102, // Options button y pos + }; + + public: + + VisualControlsClass(void) {}; + void Process(void); + int Init(void); + + int Option_Width; // Width of dialog box. + int Option_Height; // Height of dialog box. + int Option_X; + int Option_Y; + int Text_X; // Title's x pos + int Text_Y; // Add 11 for each following line + int Slider_X; // Slider's x pos + int Slider_Y; // Add 11 for each following line + int Slider_Width; // Width of each control slider. + int Slider_Height; // Height of each control slider. + int Slider_Y_Spacing; // Vertical spacing between sliders. + int Button_X; // Options button x pos + int Button_Y; // Options button y pos + +}; + +#endif diff --git a/WATCOM.H b/WATCOM.H new file mode 100644 index 0000000..678e03b --- /dev/null +++ b/WATCOM.H @@ -0,0 +1,77 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\watcom.h_v 2.13 16 Oct 1995 16:45:58 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : WATCOM.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/12/95 * + * * + * Last Update : March 12, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WATCOM_H +#define WATCOM_H + +// Turn all warnings into errors. +#pragma warning * 0 + +// Disables warning when "sizeof" is used on an object with virtual functions. +#pragma warning 549 9 + +// Disable the "Integral value may be truncated during assignment or initialization". +#pragma warning 389 9 + +// Allow constructing a temporary to be used as a parameter. +#pragma warning 604 9 + +// Disable the construct resolved as an expression warning. +#pragma warning 595 9 + +// Disable the strange "construct resolved as a declaration/type" warning. +#pragma warning 594 9 + +// Disable the "pre-compiled header file cannot be used" warning. +#pragma warning 698 9 + +// Disable the "temporary object used to initialize a non-constant reference" warning. +#pragma warning 665 9 + +// Disable the "pointer or reference truncated by cast. Cast is supposed to REMOVE warnings, not create them. +#pragma warning 579 9 + +// Disable the warning about moving empty constructors/destructors to the class declaration. +#pragma warning 657 9 + +// Turns off unreferenced function parameter warning. +//#pragma off(unreferenced) + +// Turns off "expression with side effect in sizeof()". This is needed if memchecker is used. +#pragma warning 472 9 + +#endif diff --git a/WIDEFUNC.MAC b/WIDEFUNC.MAC new file mode 100644 index 0000000..2f32a9e --- /dev/null +++ b/WIDEFUNC.MAC @@ -0,0 +1 @@ +y#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#41381#4138#41431#4138#4143#4157#4157#4157#4157#4157#4157#4157#4157#4157#4157#4157#4157#4157#4157#4157#4157#4157#4157#4157#41571#4143#4190#4143#4138#4190#4188#4143#4188#4138 \ No newline at end of file diff --git a/WIDEHDR.MAC b/WIDEHDR.MAC new file mode 100644 index 0000000..09b003a --- /dev/null +++ b/WIDEHDR.MAC @@ -0,0 +1 @@ +#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138#4138y#4138#41431#4138#4143#4141#4141#4141#4141#4141#4141#4141#4141#4141#41411#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#4141#41411 \ No newline at end of file diff --git a/WINASM.ASM b/WINASM.ASM new file mode 100644 index 0000000..7381e46 --- /dev/null +++ b/WINASM.ASM @@ -0,0 +1,889 @@ +; +; Command & Conquer(tm) +; Copyright 2025 Electronic Arts Inc. +; +; 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 3 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 General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . +; + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S I N C ** +;*************************************************************************** +;* * +;* Project Name : Command & Conquer * +;* * +;* File Name : WINSAM.ASM * +;* * +;* Programmer : Steve Tall * +;* * +;* Start Date : October 26th, 1995 * +;* * +;* Last Update : October 26th, 1995 [ST] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +global C _AbortModemFunctionPtr:dword +global C Memory_Error_Exit :dword +global C MouseQX :dword +global C MouseQY :dword + +global FastGetPortHardware_ :near +global FastSetPortHardware_ :near +global PortOpenGreenleafFast_ :near +global HMWaitForOK_ :near +global HMSetDialingMethod_ :near +global HMDial_ :near +global HMInputLine_ :near +global HMAnswer_ :near +global PortKillTime_ :near +global HMSendStringNoWait_ :near +global HMSetUpEchoRoutine_ :near +global HMSetUpAbortKey_ :near +global SetAbortModemFunctionPtr_:near +global Change8259Priority_ :near +global HMSendString_ :near +global C Stop_Execution :near + + +global _IPX_Initialise:near +global _ASM_IPX_Initialise:near + + + codeseg + +proc _ASM_IPX_Initialise near + + int 3 + jmp _IPX_Initialise + +endp + + + + +global _Int3:near +proc _Int3 near + ;int 3 + ret +endp + + +proc Stop_Execution C near + + nop + ret + +endp + + + +; +; Stuff needed from the shape library +; +; +; + + +INCLUDE "shape.inc" + + + + +;*************************************************************************** +;* ModeX_Blit -- Copy a 320x200 graphic view port to a modex screen * +;* * +;* * +;* INPUT: eax - graphic view port * +;* * +;* OUTPUT: none * +;* * +;* PROTO: extern "C" void ModeX_Blit (GraphicBufferClass *source); * +;* * +;* HISTORY: * +;* 10/26/1994 PWG : Created. * +;*=========================================================================* + +SEQUENCER =3c4h ; sequencer port +MAP_MASK =2 ; map mask register + + INCLUDE "gbuffer.inc" + global ModeX_Blit_:near + + +proc ModeX_Blit_ NEAR + + pushad + mov ebx,eax + + mov esi,[(GraphicViewPort ebx).GVPOffset] + mov eax,[(GraphicViewPort ebx).GVPXAdd] + add eax,[(GraphicViewPort ebx).GVPPitch] + mov edi,0a0000h + mov ebx,eax + + mov al,MAP_MASK + + mov ebp,200 + +??each_line_lp: mov ah,1 ;1st plane + push ebx + push esi + +??each_plane_lp:mov edx,SEQUENCER + out dx,ax + + push esi + push edi + push eax + + rept 10 + mov al,[esi] + mov bl,[esi+8] + mov cl,[esi+16] + mov dl,[esi+24] + mov ah,[esi+4] + mov bh,[esi+12] + mov ch,[esi+20] + mov dh,[esi+28] + shl ebx,16 + shl edx,16 + or ebx,eax + or edx,ecx + mov [edi],ebx + mov [edi+4],edx + add esi,32 + add edi,8 + endm + + pop eax + pop edi + pop esi + inc esi + shl ah,1 + cmp ah,16 + jl ??each_plane_lp + + + pop esi + pop ebx + lea esi,[esi+ebx+320] + add edi,80 + dec ebp + jnz ??each_line_lp + + popad + + ret + +endp ModeX_Blit_ + + + + + + +ifdef cuts + pushad + mov ebx,eax + + mov esi,[(GraphicViewPort ebx).GVPOffset] + mov eax,[(GraphicViewPort ebx).GVPXAdd] + add eax,[(GraphicViewPort ebx).GVPPitch] + mov edi,0a0000h + mov ebx,eax + + mov al,MAP_MASK + mov ah,1 ;1st plane + +??each_plane_lp:mov edx,SEQUENCER + out dx,ax + mov ebp,200 ;do 200 lines + push esi + push edi + +??each_line_lp: mov ecx,320/4 + +??each_pixel_lp:mov dl,[esi] + mov [edi],dl + add esi,4 + inc edi + dec ecx + jnz ??each_pixel_lp + + add esi,ebx + dec ebp + jnz ??each_line_lp + + pop edi + pop esi + inc esi + shl ah,1 + + cmp ah,16 + jl ??each_plane_lp +endif + + + + + + + + + + +proc FastGetPortHardware_ NEAR +endp + +proc FastSetPortHardware_ NEAR +endp + +proc PortOpenGreenleafFast_ NEAR +endp + +proc HMWaitForOK_ NEAR +endp + +proc HMSetDialingMethod_ NEAR +endp + +proc HMDial_ NEAR +endp + +proc HMInputLine_ NEAR +endp + +proc HMAnswer_ NEAR +endp + +proc PortKillTime_ NEAR +endp + +proc HMSendStringNoWait_ NEAR +endp + +proc HMSetUpEchoRoutine_ NEAR +endp + +proc HMSetUpAbortKey_ NEAR +endp + +proc SetAbortModemFunctionPtr_ NEAR +endp + +proc Change8259Priority_ NEAR +endp + +proc HMSendString_ NEAR +endp + + ret + + + + + + + + masm +; +; Change a DAC colour register directly +; +; register number in al +; +; bh=red bl=green cl=blue +; + +set_dac_col proc near + pushad + cli + push eax + mov dx,03dah + in al,dx + jmp @@1 +@@1: mov dx,03c8h + pop eax + out dx,al + jmp @@2 +@@2: inc dl + mov al,bh + out dx,al + jmp @@3 +@@3: mov al,bl + out dx,al + jmp @@4 +@@4: mov al,cl + out dx,al + jmp @@5 +@@5: sti + popad + ret +set_dac_col endp + + ideal + + +global Set_Palette_Register_:near + + +proc Set_Palette_Register_ near + + pushad + and cl,63 + mov bh,dl + and bh,63 + and bl,63 + call set_dac_col + popad + ret + +endp Set_Palette_Register_ + + + + + locals ?? + + + + ends + + dataseg + +LineBuffer dd 640 dup (?) + ends + + + + segment mycode page public use32 'code' ; Need stricter segment alignment + +global C Asm_Interpolate:near +global C Asm_Interpolate_Line_Double:near +global C Asm_Interpolate_Line_Interpolate:near +global C PaletteInterpolationTable:byte + + +;********************************************************************************************* +;* Asm_Interpolate -- interpolate a 320x200 buffer to a 640x400 screen * +;* * +;* INPUT: ptr to source buffer (320x200 image) * +;* ptr to dest buffer (640x400) * +;* height of source buffer * +;* width of source buffer * +;* width of dest buffer * +;* * +;* * +;* OUTPUT: none * +;* * +;* Warnings: * +;* * +;* HISTORY: * +;* 12/15/95 ST : Created. * +;*===========================================================================================* + +PROC Asm_Interpolate C near + + ARG src_ptr:dword + ARG dest_ptr:dword + ARG source_height:dword + ARG source_width:dword + ARG dest_width:dword + + LOCAL old_dest:dword + + pushad + + mov eax,[dest_ptr] + mov [old_dest],eax + + mov esi,[src_ptr] + +??each_line_loop: + mov ecx,[source_width] + sub ecx,2 + shr ecx,1 + mov edi,[old_dest] + jmp ??interpolate_loop + + align 32 +; +; convert 2 pixels of source into 4 pixels of destination +; so we can write to video memory with dwords +; +??interpolate_loop: + mov eax,[esi] + lea esi,[esi+2] + mov edx,eax + mov ebx,eax + and edx,65535 + ror ebx,8 + mov bl,[edx+PaletteInterpolationTable] + mov bh,ah + and eax,000ffff00h + ror ebx,8 + +;1st 3 pixels now in ebx + + shr eax,8 + mov bh,[eax+PaletteInterpolationTable] + ror ebx,16 + mov [edi],ebx + add edi,4 + + dec ecx + jnz ??interpolate_loop + +; do the last three pixels and a blank on the end of a row + xor eax,eax + mov ax,[esi] + mov [edi],al + inc edi + lea esi,[esi+2] + mov al,[eax+PaletteInterpolationTable] + mov [edi],al + inc edi + mov [edi],ah + inc edi + mov [byte edi],0 + + mov edi,[dest_width] + add [old_dest],edi + + dec [source_height] + jnz ??each_line_loop + + popad + ret + +endp Asm_Interpolate + + + + + + + + + + + + + + +PROC Asm_Interpolate_Line_Double C near + ARG src_ptr:dword + ARG dest_ptr:dword + ARG source_height:dword + ARG source_width:dword + ARG dest_width:dword + + LOCAL old_dest:dword + LOCAL width_counter:dword + LOCAL pixel_count:dword + + pushad + + mov eax,[dest_ptr] + mov [old_dest],eax + + mov esi,[src_ptr] + mov edi,[dest_ptr] + +??each_line_loop: + mov [width_counter],0 + mov ecx,[source_width] + sub ecx,2 + shr ecx,1 + mov [pixel_count],ecx + mov ecx,offset LineBuffer + mov edi,[old_dest] + jmp ??interpolate_loop + align 16 + +; convert 2 pixels of source into 4 pixels of destination +??interpolate_loop: + mov eax,[esi] + lea esi,[esi+2] + mov edx,eax + mov ebx,eax + and edx,65535 + ror ebx,8 + mov bl,[edx+PaletteInterpolationTable] + mov bh,ah + and eax,000ffff00h + ror ebx,8 + + ;1st 3 pixels now in ebx + shr eax,8 + mov bh,[eax+PaletteInterpolationTable] + ror ebx,16 + mov [edi],ebx + mov [ecx],ebx + add edi,4 + add ecx,4 + + dec [pixel_count] + jnz ??interpolate_loop + +; do the last three pixels and a blank + + xor eax,eax + mov ax,[esi] + mov [edi],al + mov [ecx],al + inc edi + inc ecx + lea esi,[esi+2] + mov al,[eax+PaletteInterpolationTable] + mov [edi],al + mov [ecx],al + inc edi + inc ecx + mov [edi],ah + mov [ecx],ah + inc edi + inc ecx + mov [byte edi],0 + mov [byte ecx],0 + + mov edi,[dest_width] + shr edi,1 + add [old_dest],edi + push esi + push edi + mov esi,offset LineBuffer + mov edi,[old_dest] + mov ecx,[source_width] + shr ecx,1 + rep movsd + pop edi + pop esi + add [old_dest],edi + mov edi,[old_dest] + + dec [source_height] + jnz ??each_line_loop + + popad + ret + +endp Asm_Interpolate_Line_Double + + + + + + + + + + ends + + dataseg + +TopLine dd 640 dup (?) +BottomLine dd 640 dup (?) + + + segment mycode page public use32 'code' ; Need stricter segment alignment + + +proc Interpolate_Single_Line C near + + ARG source_ptr:dword + ARG dest_ptr:dword + ARG source_width:dword + + pushad + + mov ecx,[source_width] + sub ecx,2 + shr ecx,1 + + mov esi,[source_ptr] + mov edi,[dest_ptr] + +??interpolate_loop: + mov eax,[esi] + lea esi,[esi+2] + mov edx,eax + mov ebx,eax + and edx,65535 + ror ebx,8 + mov bl,[edx+PaletteInterpolationTable] + mov bh,ah + and eax,000ffff00h + ror ebx,8 + + ;1st 3 pixels now in ebx + shr eax,8 + mov bh,[eax+PaletteInterpolationTable] + ror ebx,16 + mov [edi],ebx + add edi,4 + + dec ecx + jnz ??interpolate_loop + +; do the last three pixels and a blank + + xor eax,eax + mov ax,[esi] + mov [edi],al + inc edi + mov al,[eax+PaletteInterpolationTable] + mov [edi],al + inc edi + mov [edi],ah + inc edi + mov [byte edi],0 + + popad + ret + + +endp Interpolate_Single_Line + + +proc Interpolate_Between_Lines C near + + ARG source1:dword + ARG source2:dword + ARG destination:dword + ARG source_width:dword + + pushad + mov esi,[source1] + mov edi,[destination] + mov ebx,[source2] + xor eax,eax + mov ecx,[source_width] + add ecx,ecx + +??interpolate_each_pixel_loop: + mov al,[esi] + mov ah,[ebx] + inc esi + inc ebx + mov dl,[eax+PaletteInterpolationTable] + mov [edi],dl + inc edi + dec ecx + jnz ??interpolate_each_pixel_loop + + popad + ret + +endp Interpolate_Between_Lines + + + + +macro Lineswp + push [next_line] + push [last_line] + pop [next_line] + pop [last_line] +endm + + +PROC Asm_Interpolate_Line_Interpolate C near + + + ARG src_ptr:dword + ARG dest_ptr:dword + ARG source_lines:dword + ARG source_width:dword + ARG dest_width:dword + + LOCAL old_dest:dword + LOCAL pixel_count:dword + LOCAL next_line:dword + LOCAL last_line:dword + + pushad + + mov eax,[dest_ptr] + mov [old_dest],eax + + mov [next_line],offset TopLine + mov [last_line],offset BottomLine + mov ecx,[source_width] + shr ecx,1 + mov [pixel_count],ecx + shr [dest_width],1 + + call Interpolate_Single_Line C,[src_ptr],[next_line],[source_width] + mov esi,[source_width] + Lineswp + add [src_ptr],esi + dec [source_lines] + + +??each_line_pair_loop: + call Interpolate_Single_Line C,[src_ptr],[next_line],[source_width] + call Interpolate_Between_Lines C,[last_line],[next_line],offset LineBuffer,[source_width] + + mov esi,[last_line] + mov edi,[old_dest] + mov ecx,[pixel_count] + rep movsd + + mov edi,[old_dest] + mov esi,offset LineBuffer + add edi,[dest_width] + mov ecx,[pixel_count] + mov [old_dest],edi + rep movsd + + mov edi,[old_dest] + mov esi,[source_width] + add edi,[dest_width] + add [src_ptr],esi + mov [old_dest],edi + + Lineswp + dec [source_lines] + jnz ??each_line_pair_loop + + call Interpolate_Single_Line C,[src_ptr],[next_line],[source_width] + mov esi,[next_line] + mov edi,[old_dest] + mov ecx,[pixel_count] + rep movsd + + popad + ret + + +endp Asm_Interpolate_Line_Interpolate + + ends mycode + + + + +global C Asm_Create_Palette_Interpolation_Table:near +global C InterpolationPalette:dword + + codeseg + + +proc Asm_Create_Palette_Interpolation_Table C near + + LOCAL palette_counter:dword + LOCAL first_palette:dword + LOCAL second_palette:dword + LOCAL dest_ptr:dword + LOCAL count:dword + LOCAL closest_colour:dword + LOCAL distance_of_closest:dword + + pushad + + mov [dest_ptr],0 + mov [palette_counter],256 + mov esi,[InterpolationPalette] + +??palette_outer_loop: + mov edi,[InterpolationPalette] + mov ecx,256 + +??palette_inner_loop: + mov bl,[esi] + add bl,[edi] + shr bl,1 + + mov bh,[esi+1] + add bh,[edi+1] + shr bh,1 + + mov dl,[esi+2] + add dl,[edi+2] + shr dl,1 + + mov [closest_colour],0 + mov [distance_of_closest],-1 + + push edi + push ecx + mov edi,[InterpolationPalette] + mov [count],0 + +??cmp_pal_lp: xor eax,eax + xor ecx,ecx + mov al,[edi] + sub al,bl + imul al + mov ecx,eax + mov al,[edi+1] + sub al,bh + imul al + add ecx,eax + mov al,[edi+2] + sub al,dl + imul al + add ecx,eax + + cmp ecx,[distance_of_closest] + ja ??end_cmp_lp + mov [distance_of_closest],ecx + mov eax,[count] + mov [closest_colour],eax + test ecx,ecx + jz ??got_perfect + +??end_cmp_lp: lea edi,[edi+3] + inc [count] + cmp [count],256 + jb ??cmp_pal_lp + + +??got_perfect: mov edi,[dest_ptr] + mov eax,[closest_colour] + mov [edi+PaletteInterpolationTable],al + inc [dest_ptr] + + pop ecx + pop edi + lea edi,[edi+3] + dec ecx + jnz ??palette_inner_loop + + lea esi,[esi+3] + dec [palette_counter] + jnz ??palette_outer_loop + + popad + ret + +endp Asm_Create_Palette_Interpolation_Table + + + + + DATASEG + +_AbortModemFunctionPtr dd 0 +Memory_Error_Exit dd 0 +MouseQX dd 0 +MouseQY dd 0 + +end diff --git a/WINSTUB.CPP b/WINSTUB.CPP new file mode 100644 index 0000000..6e42667 --- /dev/null +++ b/WINSTUB.CPP @@ -0,0 +1,921 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : WINSTUB.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 10/04/95 * + * * + * Last Update : October 4th 1995 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * This file contains stubs for undefined externals when linked under Watcom for Win 95 * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "tcpip.h" + +void output(short,short) +{} + +bool InDebugger = false; +bool ReadyToQuit = false; + +#if (0) +/*************************************************************************** + * Extract_Shape_Count -- returns # of shapes in the given shape block * + * * + * The # of shapes in a shape block is the first WORD in the block, so * + * this is the value returned. * + * * + * INPUT: * + * buffer pointer to shape block, created with MAKESHPS.EXE * + * * + * OUTPUT: * + * # shapes in the block * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int __cdecl Extract_Shape_Count(VOID const *buffer) +{ + ShapeBlock_Type *block = (ShapeBlock_Type *)buffer; + + return (block->NumShapes); + +} /* end of Extract_Shape_Count */ + + +/*************************************************************************** + * Extract_Shape -- Gets pointer to shape in given shape block * + * * + * INPUT: * + * buffer pointer to shape block, created with MAKESHPS.EXE * + * shape index of shape to get * + * * + * OUTPUT: * + * pointer to shape in the shape block * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +VOID * __cdecl Extract_Shape(VOID const *buffer, int shape) +{ + ShapeBlock_Type *block = (ShapeBlock_Type*) buffer; + long offset; // Offset of shape data, from start of block + char *bytebuf = (char*) buffer; + + /* + ----------------------- Return if invalid argument ----------------------- + */ + if (!buffer || shape < 0 || shape >= block->NumShapes) + return(NULL); + + offset = block->Offsets[shape]; + + return(bytebuf + 2 + offset); + +} /* end of Extract_Shape */ +#endif //(0) + + + +unsigned long CCFocusMessage = WM_USER+50; //Private message for receiving application focus +extern void VQA_PauseAudio(void); +extern void VQA_ResumeAudio(void); + + +ThemeType OldTheme = THEME_NONE; + + +/*********************************************************************************************** + * Focus_Loss -- this function is called when a library function detects focus loss * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 2/1/96 2:10PM ST : Created * + *=============================================================================================*/ + +void Focus_Loss(void) +{ + if (SoundOn){ + if (OldTheme == THEME_NONE){ + OldTheme = Theme.What_Is_Playing(); + } + } + Theme.Stop(); + Stop_Primary_Sound_Buffer(); + if (WWMouse) WWMouse->Clear_Cursor_Clip(); +} + + +void Focus_Restore(void) +{ + Restore_Cached_Icons(); + Map.Flag_To_Redraw(true); + Start_Primary_Sound_Buffer(TRUE); + if (WWMouse) WWMouse->Set_Cursor_Clip(); + VisiblePage.Clear(); + HiddenPage.Clear(); +} + + + +/*********************************************************************************************** + * Check_For_Focus_Loss -- check for the end of the focus loss * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 2/2/96 10:49AM ST : Created * + *=============================================================================================*/ + +void Check_For_Focus_Loss(void) +{ + static BOOL focus_last_time = 1; + MSG msg; + + if ( !GameInFocus ){ + Focus_Loss(); + while( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE | PM_NOYIELD ) ){ + if( !GetMessage( &msg, NULL, 0, 0 ) ){ + return; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + if (!focus_last_time && GameInFocus){ + + VQA_PauseAudio(); + CountDownTimerClass cd; + cd.Set(60*1); + + do { + while (PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE )) { + if( !GetMessage( &msg, NULL, 0, 0 ) ){ + return; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + } while(cd.Time()); + VQA_ResumeAudio(); + //AllSurfaces.Restore_Surfaces(); + //VisiblePage.Clear(); + //HiddenPage.Clear(); + //Map.Flag_To_Redraw(true); + PostMessage (MainWindow, CCFocusMessage,0,0); + } + + focus_last_time = GameInFocus; + +} + + + +extern BOOL InMovie; + +long FAR PASCAL _export Windows_Procedure (HWND hwnd, UINT message, UINT wParam, LONG lParam) +{ + + int low_param = LOWORD(wParam); + + if (message == CCFocusMessage){ + Start_Primary_Sound_Buffer(TRUE); + if (!InMovie){ + Theme.Queue_Song(OldTheme); + OldTheme = THEME_NONE; + } + return(0); + } + + + switch ( message ){ + + case WM_MOUSEMOVE: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_KEYDOWN: + case WM_KEYUP: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_LBUTTONDBLCLK: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_MBUTTONDBLCLK: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_RBUTTONDBLCLK: + Kbd.Message_Handler(hwnd, message, wParam, lParam); + return(0); + + case WM_DESTROY: + CCDebugString ("C&C95 - WM_DESTROY message received.\n"); + CCDebugString ("C&C95 - About to call Prog_End.\n"); + Prog_End(); + CCDebugString ("C&C95 - About to Invalidate_Cached_Icons.\n"); + Invalidate_Cached_Icons(); + CCDebugString ("C&C95 - About to release the video surfaces.\n"); + VisiblePage.Un_Init(); + HiddenPage.Un_Init(); + AllSurfaces.Release(); + if (!InDebugger){ + CCDebugString ("C&C95 - About to reset the video mode.\n"); + Reset_Video_Mode(); + } + CCDebugString ("C&C95 - About to stop the profiler.\n"); + Stop_Profiler(); + CCDebugString ("C&C95 - Posting the quit message.\n"); + PostQuitMessage( 0 ); + /* + ** If we are shutting down gracefully than flag that the message loop has finished. + ** If this is a forced shutdown (ReadyToQuit == 0) then try and close down everything + ** before we exit. + */ + if (ReadyToQuit){ + CCDebugString ("C&C95 - We are now ready to quit.\n"); + ReadyToQuit = 2; + }else{ + CCDebugString ("C&C95 - Emergency shutdown.\n"); + CCDebugString ("C&C95 - Shut down the network stuff.\n"); +#ifndef DEMO + Shutdown_Network(); +#endif + CCDebugString ("C&C95 - Kill the Winsock stuff.\n"); + if (Winsock.Get_Connected()) Winsock.Close(); + CCDebugString ("C&C95 - Call ExitProcess.\n"); + ExitProcess(0); + } + CCDebugString ("C&C95 - Clean & ready to quit.\n"); + return(0); + + case WM_ACTIVATEAPP: + GameInFocus=(BOOL)wParam; + if (!GameInFocus) { + Focus_Loss(); + } + AllSurfaces.Set_Surface_Focus (GameInFocus); + AllSurfaces.Restore_Surfaces(); +// if (GameInFocus){ +// Restore_Cached_Icons(); +// Map.Flag_To_Redraw(true); +// Start_Primary_Sound_Buffer(TRUE); +// if (WWMouse) WWMouse->Set_Cursor_Clip(); +// } + return(0); +#if (0) + case WM_ACTIVATE: + if (low_param == WA_INACTIVE){ + GameInFocus = FALSE; + Focus_Loss(); + } + return(0); +#endif //(0) + + case WM_SYSCOMMAND: + switch ( wParam ) { + + case SC_CLOSE: + /* + ** Windows sent us a close message. Probably in response to Alt-F4. Ignore it by + ** pretending to handle the message and returning 0; + */ + return (0); + + case SC_SCREENSAVE: + /* + ** Windoze is about to start the screen saver. If we just return without passing + ** this message to DefWindowProc then the screen saver will not be allowed to start. + */ + return (0); + } + break; + +#ifdef FORCE_WINSOCK + case WM_ACCEPT: + case WM_HOSTBYADDRESS: + case WM_HOSTBYNAME: + case WM_ASYNCEVENT: + case WM_UDPASYNCEVENT: + Winsock.Message_Handler(hwnd, message, wParam, lParam); + return (0); +#endif //FORCE_WINSOCK + } + + return (DefWindowProc (hwnd, message, wParam, lParam)); +} + + + + + + + + + + +/*********************************************************************************************** + * Create_Main_Window -- opens the MainWindow for C&C * + * * + * * + * * + * INPUT: instance -- handle to program instance * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/10/95 4:08PM ST : Created * + *=============================================================================================*/ + +#define CC_ICON 1 +int ShowCommand; + +void Create_Main_Window ( HANDLE instance ,int command_show , int width , int height ) + +{ + HWND hwnd ; + WNDCLASS wndclass ; + // + // Register the window class + // + + wndclass.style = CS_HREDRAW | CS_VREDRAW ; + wndclass.lpfnWndProc = Windows_Procedure ; + wndclass.cbClsExtra = 0 ; + wndclass.cbWndExtra = 0 ; + wndclass.hInstance = instance ; + wndclass.hIcon = LoadIcon (instance, MAKEINTRESOURCE(CC_ICON)) ; + wndclass.hCursor = NULL; + wndclass.hbrBackground = NULL; + wndclass.lpszMenuName = "Command & Conquer"; //NULL + wndclass.lpszClassName = "Command & Conquer"; + + RegisterClass (&wndclass) ; + + + // + // Create our main window + // + hwnd = CreateWindowEx ( + WS_EX_TOPMOST, + "Command & Conquer", + "Command & Conquer", + WS_POPUP | WS_MAXIMIZE, + 0, + 0, + width, + height, + NULL, + NULL, + instance, + NULL ); + + ShowWindow (hwnd, command_show ); + ShowCommand = command_show; + UpdateWindow (hwnd); + SetFocus (hwnd); + MainWindow=hwnd; //Save the handle to our main window + hInstance = instance; + + CCFocusMessage = RegisterWindowMessage ("CC_GOT_FOCUS"); + + Audio_Focus_Loss_Function = &Focus_Loss; + Misc_Focus_Loss_Function = &Focus_Loss; + Misc_Focus_Restore_Function = &Focus_Restore; + Gbuffer_Focus_Loss_Function = &Focus_Loss; +} + +void Window_Dialog_Box(HANDLE hinst, LPCTSTR lpszTemplate, HWND hwndOwner, DLGPROC dlgprc) +{ + MSG msg; + /* + ** Get rid of the Westwood mouse cursor because we are showing a + ** dialog box and we want to have the right windows cursor showing + ** for it. + */ + Hide_Mouse(); + ShowCursor(TRUE); + + /* + ** Pop up the dialog box and then run a standard message handler + ** until the dialog box is closed. + */ + + DialogBox(hinst, lpszTemplate, hwndOwner, dlgprc); + while (GetMessage(&msg, NULL, 0, 0) && !AllDone) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + /* + ** Restore the westwood mouse cursor and get rid of the windows one + ** because it is now time to restore back to the westwood way of + ** doing things. + */ + ShowCursor(FALSE); + Show_Mouse(); +} + + + +typedef struct tColourList { + + char Red; + char Green; + char Blue; +} ColourList; + +ColourList ColourLookup[9]={ + 0,0,0, + 63,0,0, + 0,63,0, + 0,0,63, + 63,0,63, + 63,63,0, + 0,63,63, + 32,32,32, + 63,63,63 +}; + + + + +int DebugColour=1; + + +extern "C" void Set_Palette_Register(int number,int red ,int green ,int blue); +#pragma off (unreferenced) +void Colour_Debug (int call_number) +{ + //#if 0 + //if (DebugColour==call_number || !call_number){ + + //if (call_number){ + // Wait_Vert_Blank(); + //} + + Set_Palette_Register (0,ColourLookup[call_number].Red , + ColourLookup[call_number].Green, + ColourLookup[call_number].Blue); + //} + //#endif +} + +#pragma on (unreferenced) + + + + + +BOOL Any_Locked (void) +{ + if (SeenBuff.Get_LockCount() || + HidPage.Get_LockCount()){ + return (TRUE); + }else{ + return(FALSE); + } +} + + + + + + + +HANDLE DebugFile = INVALID_HANDLE_VALUE; + +/*********************************************************************************************** + * CCDebugString -- sends a string to the debugger and echos it to disk * + * * + * * + * * + * INPUT: string * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/28/96 12:48PM ST : Created * + *=============================================================================================*/ +void CCDebugString (char *string) +{ +#if (0) + + char outstr[256]; + + sprintf (outstr, "%s", string); + + DWORD actual; + if (DebugFile == INVALID_HANDLE_VALUE){ + DebugFile = CreateFile("debug.txt", GENERIC_WRITE, 0, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + }else{ + DebugFile = CreateFile("debug.txt", GENERIC_WRITE, 0, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + } + + if (DebugFile != INVALID_HANDLE_VALUE){ + SetFilePointer (DebugFile, 0, NULL, FILE_END); + WriteFile(DebugFile, outstr, strlen(outstr)+1, &actual, NULL); + CloseHandle (DebugFile); + } + + OutputDebugString (string); + +#else + + string = string; + +#endif + +} + + + + + + + + + + + + + + +// +// Miscellaneous stubs. Mainly for multi player stuff +// +// +// + +//IPXAddressClass::IPXAddressClass(void) { +// int i; +// i++; +//} +//int IPXManagerClass::Num_Connections(void){ return (0); } +//int IPXManagerClass::Connection_ID( int ) { return (0); } +//IPXAddressClass * IPXManagerClass::Connection_Address( int ) { return ((IPXAddressClass*)0); } +//char * IPXManagerClass::Connection_Name( int ) { return ((char*)0); } +//int IPXAddressClass::Is_Broadcast() { return (0); } +//int IPXManagerClass::Send_Global_Message( void *, int, int, IPXAddressClass * ) { return (0); } +//int IPXManagerClass::Service() { return (0); } +//int IPXManagerClass::Get_Global_Message( void *, int *, IPXAddressClass *, short unsigned * ) { return (0); } +//int IPXAddressClass::operator ==( IPXAddressClass & ) { return (0); } +//IPXManagerClass::IPXManagerClass( int, int, int, int, short unsigned, short unsigned ) {} +//IPXManagerClass::~IPXManagerClass() { +// int i; +// i++; +// } +//int IPXManagerClass::Delete_Connection( int ) { return (0); } +//IPXAddressClass::IPXAddressClass( char unsigned *, char unsigned * ){} +//void IPXManagerClass::Set_Socket( short unsigned ){} +//int IPXManagerClass::Is_IPX() { return (0); } +//int IPXManagerClass::Init() { return (0); } +//void IPXAddressClass::Get_Address( char unsigned *, char unsigned * ){} +//void IPXManagerClass::Set_Bridge( char unsigned * ){} +//int IPXManagerClass::Global_Num_Send() { return (0); } +//void IPXManagerClass::Set_Timing( long unsigned, long unsigned, long unsigned ){} +//unsigned long IPXManagerClass::Global_Response_Time() { return (0); } +//int IPXManagerClass::Create_Connection( int, char *, IPXAddressClass * ) { return (0); } +//int IPXAddressClass::operator !=( IPXAddressClass & ) { return (0); } +//int IPXManagerClass::Send_Private_Message( void *, int, int, int ) { return (0); } +//int IPXManagerClass::Get_Private_Message( void *, int *, int * ) { return (0); } +//int IPXManagerClass::Connection_Index( int ) { return (0); } +//void IPXManagerClass::Reset_Response_Time(){} +//long unsigned IPXManagerClass::Response_Time() { return (0); } +//int IPXManagerClass::Private_Num_Send( int ) { return (0); } + +//_VQAHandle * VQA_Alloc(void){ return ((_VQAHandle *)0); } +//void VQA_Init( _VQAHandle *, long ( *)()) {} +//long VQA_Open( _VQAHandle *, char const *, _VQAConfig * ) { return (0); } +//void VQA_Free( _VQAHandle * ) {} +//void VQA_Close( _VQAHandle * ) {} +//long VQA_Play( _VQAHandle *, long ) { return (0); } + +//void VQA_Init(VQAHandle *, long(*)(VQAHandle *vqa, long action, void *buffer, long nbytes)){} + +//long VQA_Open(VQAHandle *, char const *, VQAConfig *) +//{ +// return (0); +//} + +//void VQA_Close(VQAHandle *){} + +//long VQA_Play(VQAHandle *, long) +//{ +// return (0); +//} + + +unsigned char *VQPalette; +long VQNumBytes; +unsigned long VQSlowpal; +bool VQPaletteChange = false; + + +extern "C"{ + void __cdecl SetPalette(unsigned char *palette,long numbytes,unsigned long slowpal); +} + + + +void Flag_To_Set_Palette(unsigned char *palette,long numbytes,unsigned long slowpal) +{ + VQPalette = palette; + VQNumBytes = numbytes; + VQSlowpal = slowpal; + VQPaletteChange = true; +} + + + +void Check_VQ_Palette_Set(void) +{ + if (VQPaletteChange){ + SetPalette(VQPalette, VQNumBytes, VQSlowpal); + VQPaletteChange = false; + } +} + + + + + +void __cdecl SetPalette(unsigned char *palette,long,unsigned long) +{ + for (int i=0 ; i<256*3 ; i++){ + *(palette+i)&=63; + } + Increase_Palette_Luminance(palette , 15 , 15 , 15 ,63); + + if (PalettesRead){ + memcpy (&PaletteInterpolationTable[0][0] , InterpolatedPalettes[PaletteCounter++] , 65536); + } + Set_Palette(palette); +} + + + +/*********************************************************************************************** + * Memory_Error_Handler -- Handle a possibly fatal failure to allocate memory * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/22/96 3:57PM ST : Created * + *=============================================================================================*/ +void Memory_Error_Handler(void) +{ + VisiblePage.Clear(); + Set_Palette(GamePalette); + while (Get_Mouse_State()){Show_Mouse();}; + CCMessageBox().Process("Error - out of memory.", "Abort", false); + Prog_End(); + Invalidate_Cached_Icons(); + PostQuitMessage( 0 ); + ExitProcess(0); +} + + + + + + + + + + + +GraphicBufferClass* Read_PCX_File(char* name, char* Palette, void *Buff, long Size); + + + +/*********************************************************************************************** + * Load_Title_Screen -- loads the title screen into the given video buffer * + * * + * * + * * + * INPUT: screen name * + * video buffer * + * ptr to buffer for palette * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 7/5/96 11:30AM ST : Created * + *=============================================================================================*/ + +void Load_Title_Screen(char *name, GraphicViewPortClass *video_page, unsigned char *palette) +{ + + GraphicBufferClass *load_buffer; + + + load_buffer = Read_PCX_File (name, (char*)palette, NULL, 0); + + if (load_buffer){ + load_buffer->Blit(*video_page); + delete load_buffer; + } +} + + + +#include "filepcx.h" + +/*************************************************************************** + * READ_PCX_FILE -- read a pcx file into a Graphic Buffer * + * * + * GraphicBufferClass* Read_PCX_File (char* name, char* palette ,void *Buff, long size ); * + * * + * * + * INPUT: name is a NULL terminated string of the fromat [xxxx.pcx] * + * palette is optional, if palette != NULL the the color palette of * + * the pcx file will be place in the memory block pointed * + * by palette. * + * Buff is optinal, if Buff == NULL a new memory Buffer * + * will be allocated, otherwise the file will be placed * + * at location pointd by Buffer; * + * Size is the size in bytes of the memory block pointed by Buff * + * is also optional; + * * + * OUTPUT: on succes a pointer to a GraphicBufferClass cointaining the * + * pcx file, NULL othewise. * + * * + * WARNINGS: * + * Appears to be a comment-free zone * + * * + * HISTORY: * + * 05/03/1995 JRJ : Created. * + * 04/30/1996 ST : Tidied up and modified to use CCFileClass * + *=========================================================================*/ + +#define POOL_SIZE 2048 +#define READ_CHAR() *file_ptr++ ; \ + if ( file_ptr >= & pool [ POOL_SIZE ] ) { \ + file_handle.Read (pool , POOL_SIZE ); \ + file_ptr = pool ; \ + } + + +GraphicBufferClass* Read_PCX_File(char* name, char* palette, void *Buff, long Size) +{ + unsigned i, j; + unsigned rle; + unsigned color; + unsigned scan_pos; + char *file_ptr; + int width; + int height; + char *buffer; + PCX_HEADER header; + RGB *pal; + char pool [POOL_SIZE]; + GraphicBufferClass *pic; + + CCFileClass file_handle(name); + + if (!file_handle.Is_Available()) return (NULL); + + file_handle.Open(READ); + + file_handle.Read (&header, sizeof (PCX_HEADER)); + + if (header.id != 10 && header.version != 5 && header.pixelsize != 8 ) return NULL ; + + width = header.width - header.x + 1; + height = header.height - header.y + 1; + + if (Buff) { + buffer = (char *)Buff; + i = Size / width; + height = MIN (i - 1, height); + pic = new GraphicBufferClass(width, height, buffer, Size); + if ( !(pic && pic->Get_Buffer()) ) return NULL ; + } else { + pic = new GraphicBufferClass(width, height, NULL, width*(height+4)); + if ( !(pic && pic->Get_Buffer()) ) return NULL ; + } + + buffer = (char *) pic->Get_Buffer(); + file_ptr = pool ; + file_handle.Read (pool , POOL_SIZE); + + if ( header.byte_per_line != width ){ + + for ( scan_pos = j = 0 ; j < height ; j ++, scan_pos += width ) { + for ( i = 0 ; i < width ; ) { + rle = READ_CHAR (); + if ( rle > 192 ) { + rle -= 192 ; + color = READ_CHAR (); ; + memset ( buffer + scan_pos + i , color , rle ); + i += rle; + } else { + *(buffer+scan_pos + i++ ) = (char)rle; + } + } + } + + if ( i == width ) rle = READ_CHAR (); + if ( rle > 192 ) rle = READ_CHAR (); + + } else { + + for ( i = 0 ; i < width * height ; ) { + rle = READ_CHAR (); + rle &= 0xff; + if ( rle > 192 ) { + rle -= 192 ; + color = READ_CHAR (); + memset ( buffer + i , color , rle ); + i += rle ; + }else{ + *(buffer + i++) = (char)rle; + } + } + } + + if ( palette ) { + file_handle.Seek (- (256 * sizeof ( RGB )) , SEEK_END ); + file_handle.Read (palette , 256L * sizeof ( RGB )); + + pal = ( RGB * ) palette; + for (i = 0 ; i < 256 ; i ++) { + pal ->red >>= 2; + pal ->green >>= 2; + pal ->blue >>= 2; + pal ++ ; + } + } + + file_handle.Close(); + return pic; +} + diff --git a/WWALLOC.H b/WWALLOC.H new file mode 100644 index 0000000..441fc2b --- /dev/null +++ b/WWALLOC.H @@ -0,0 +1,68 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\wwalloc.h_v 2.18 16 Oct 1995 16:47:52 JOE_BOSTIC $ */ + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* +** This should be located in the wwlib32.h file, but is located here for +** test purposes. +*/ +#ifdef __FLAT__ +#define PRIVATE static +#endif + +typedef enum MemoryFlagType { + MEM_NORMAL = 0x0000, // Default memory (normal). + MEM_PUBLIC = 0x0000, // Default memory (normal). + MEM_CHIP = 0x0000, // Graphic & sound buffer memory (Amiga). + MEM_UNUSED = 0x0001, // + MEM_SYSTEM = 0x0002, // Allocate out of system heap (XMS or EMS only). + MEM_RELAXED= 0x0004, // Don't worry about page conservation in EMS. + MEM_TEMP = 0x0008, // Temporary allocation (used by library only). + MEM_CLEAR = 0x0010, // Fill memory with '\0' characters. + MEM_PARA = 0x0020, // Paragraph aligned (IBM only). + MEM_XMS = 0x0040, // XMS memory. + MEM_EMS = 0x0080, // EMS memory (not implemented). + MEM_X = 0x8000 // Here to force this enum to be unsigned sized. +} MemoryFlagType; +MemoryFlagType operator |(MemoryFlagType, MemoryFlagType); +MemoryFlagType operator &(MemoryFlagType, MemoryFlagType); +MemoryFlagType operator ~(MemoryFlagType); + + +/* Prototypes for functions defined in this file */ +void * __cdecl Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags); +void __cdecl Free(void const *pointer); +void * __cdecl Resize_Alloc(void const *original_ptr, unsigned long new_size_in_bytes); +long __cdecl Ram_Free(MemoryFlagType flag); +long __cdecl Total_Ram_Free(MemoryFlagType flag); +long __cdecl Heap_Size(MemoryFlagType flag); + +extern unsigned long __cdecl MinRam; // Record of least memory at worst case. +extern unsigned long __cdecl MaxRam; // Record of total allocated at worst case. + +#ifdef __cplusplus +} +#endif + diff --git a/WWFILE.H b/WWFILE.H new file mode 100644 index 0000000..cafef97 --- /dev/null +++ b/WWFILE.H @@ -0,0 +1,71 @@ +/* +** Command & Conquer(tm) +** Copyright 2025 Electronic Arts Inc. +** +** 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 3 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 General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program. If not, see . +*/ + +/* $Header: F:\projects\c&c\vcs\code\wwfile.h_v 2.15 16 Oct 1995 16:46:06 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : WWFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : August 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWFILE_H +#define WWFILE_H + +//#include + +#ifndef READ +#define READ _READ +#endif +#ifndef WRITE +#define WRITE _WRITE +#endif + +class FileClass +{ + public: + virtual ~FileClass(void) {}; + virtual char const * File_Name(void) const = 0; + virtual char const * Set_Name(char const *filename) = 0; + virtual int Create(void) = 0; + virtual int Delete(void) = 0; + virtual int Is_Available(int forced=false) = 0; + virtual int Is_Open(void) const = 0; + virtual int Open(char const *filename, int rights=READ) = 0; + virtual int Open(int rights=READ) = 0; + virtual long Read(void *buffer, long size) = 0; + virtual long Seek(long pos, int dir=SEEK_CUR) = 0; + virtual long Size(void) = 0; + virtual long Write(void const *buffer, long size) = 0; + virtual void Close(void) = 0; + + operator char const * () {return File_Name();}; +}; + +#endif